pax_global_header00006660000000000000000000000064117167403210014514gustar00rootroot0000000000000052 comment=f477fdcc5931eef6020926e540d6b8630111d3d4 openbve-1.4.0.10/000077500000000000000000000000001171674032100133735ustar00rootroot00000000000000openbve-1.4.0.10/AtsPluginProxy/000077500000000000000000000000001171674032100163435ustar00rootroot00000000000000openbve-1.4.0.10/AtsPluginProxy/AtsPluginProxy.cpp000066400000000000000000000172411171674032100220240ustar00rootroot00000000000000#include "stdafx.h" // --- main --- BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } // --- structures --- struct ATS_VEHICLESPEC { int BrakeNotches; int PowerNotches; int AtsNotch; int B67Notch; int Cars; }; struct ATS_VEHICLESTATE { double Location; float Speed; int Time; float BcPressure; float MrPressure; float ErPressure; float BpPressure; float SapPressure; float Current; }; struct ATS_BEACONDATA { int Type; int Signal; float Distance; int Optional; }; struct ATS_HANDLES { int Brake; int Power; int Reverser; int ConstantSpeed; }; #define ATS_API __declspec(dllimport) // --- handles --- HMODULE dllhandle = NULL; typedef ATS_API void (__stdcall *LOAD) (); LOAD load = NULL; typedef ATS_API void (__stdcall *DISPOSE) (); DISPOSE dispose = NULL; typedef ATS_API int (__stdcall *GETPLUGINVERSION) (); GETPLUGINVERSION getpluginversion = NULL; typedef ATS_API void (__stdcall *SETVEHICLESPEC) (ATS_VEHICLESPEC vehicleSpec); SETVEHICLESPEC setvehiclespec = NULL; typedef ATS_API void (__stdcall *INITIALIZE) (int brake); INITIALIZE initialize = NULL; typedef ATS_API ATS_HANDLES (__stdcall *ELAPSE) (ATS_VEHICLESTATE vehicleState, int* panel, int* sound); ELAPSE elapse = NULL; typedef ATS_API void (__stdcall *SETPOWER) (int setpower); SETPOWER setpower = NULL; typedef ATS_API void (__stdcall *SETBRAKE) (int setbrake); SETBRAKE setbrake = NULL; typedef ATS_API void (__stdcall *SETREVERSER) (int setreverser); SETREVERSER setreverser = NULL; typedef ATS_API void (__stdcall *KEYDOWN) (int atsKeyCode); KEYDOWN keydown = NULL; typedef ATS_API void (__stdcall *KEYUP) (int atsKeyCode); KEYUP keyup = NULL; typedef ATS_API void (__stdcall *HORNBLOW) (int hornType); HORNBLOW hornblow = NULL; typedef ATS_API void (__stdcall *DOOROPEN) (); DOOROPEN dooropen = NULL; typedef ATS_API void (__stdcall *DOORCLOSE) (); DOORCLOSE doorclose = NULL; typedef ATS_API void (__stdcall *SETSIGNAL) (int signal); SETSIGNAL setsignal = NULL; typedef ATS_API void (__stdcall *SETBEACONDATA) (ATS_BEACONDATA beaconData); SETBEACONDATA setbeacondata = NULL; // --- load the plugin --- int _stdcall LoadDLL(LPCWSTR fileUnicode, LPCSTR fileAnsi) { dllhandle = LoadLibraryW(fileUnicode); if (dllhandle == NULL) { dllhandle = LoadLibraryA(fileAnsi); if (dllhandle == NULL) return 0; } { // --- Load --- FARPROC functionhandle = GetProcAddress(dllhandle, "Load"); if (functionhandle != NULL) { load = (LOAD)functionhandle; } } { // --- Dispose --- FARPROC functionhandle = GetProcAddress(dllhandle, "Dispose"); if (functionhandle != NULL) { dispose = (DISPOSE)functionhandle; } } { // --- GetPluginVersion --- FARPROC functionhandle = GetProcAddress(dllhandle, "GetPluginVersion"); if (functionhandle != NULL) { getpluginversion = (GETPLUGINVERSION)functionhandle; } } { // --- SetVehicleSpec --- FARPROC functionhandle = GetProcAddress(dllhandle, "SetVehicleSpec"); if (functionhandle != NULL) { setvehiclespec = (SETVEHICLESPEC)functionhandle; } } { // --- Initialize --- FARPROC functionhandle = GetProcAddress(dllhandle, "Initialize"); if (functionhandle != NULL) { initialize = (INITIALIZE)functionhandle; } } { // --- Elapse --- FARPROC functionhandle = GetProcAddress(dllhandle, "Elapse"); if (functionhandle != NULL) { elapse = (ELAPSE)functionhandle; } } { // --- SetPower --- FARPROC functionhandle = GetProcAddress(dllhandle, "SetPower"); if (functionhandle != NULL) { setpower = (SETPOWER)functionhandle; } } { // --- SetBrake --- FARPROC functionhandle = GetProcAddress(dllhandle, "SetBrake"); if (functionhandle != NULL) { setbrake = (SETBRAKE)functionhandle; } } { // --- SetReverser --- FARPROC functionhandle = GetProcAddress(dllhandle, "SetReverser"); if (functionhandle != NULL) { setreverser = (SETREVERSER)functionhandle; } } { // --- KeyDown --- FARPROC functionhandle = GetProcAddress(dllhandle, "KeyDown"); if (functionhandle != NULL) { keydown = (KEYDOWN)functionhandle; } } { // --- KeyUp --- FARPROC functionhandle = GetProcAddress(dllhandle, "KeyUp"); if (functionhandle != NULL) { keyup = (KEYUP)functionhandle; } } { // --- HornBlow --- FARPROC functionhandle = GetProcAddress(dllhandle, "HornBlow"); if (functionhandle != NULL) { hornblow = (HORNBLOW)functionhandle; } } { // --- DoorOpen --- FARPROC functionhandle = GetProcAddress(dllhandle, "DoorOpen"); if (functionhandle != NULL) { dooropen = (DOOROPEN)functionhandle; } } { // --- DoorClose --- FARPROC functionhandle = GetProcAddress(dllhandle, "DoorClose"); if (functionhandle != NULL) { doorclose = (DOORCLOSE)functionhandle; } } { // --- SetSignal --- FARPROC functionhandle = GetProcAddress(dllhandle, "SetSignal"); if (functionhandle != NULL) { setsignal = (SETSIGNAL)functionhandle; } } { // --- SetBeaconData --- FARPROC functionhandle = GetProcAddress(dllhandle, "SetBeaconData"); if (functionhandle != NULL) { setbeacondata = (SETBEACONDATA)functionhandle; } } return 1; } // --- unload the plugin --- int _stdcall UnloadDLL () { if (dllhandle != NULL) { load = NULL; dispose = NULL; getpluginversion = NULL; setvehiclespec = NULL; initialize = NULL; elapse = NULL; setpower = NULL; setbrake = NULL; setreverser = NULL; keydown = NULL; keyup = NULL; hornblow = NULL; dooropen = NULL; doorclose = NULL; setsignal = NULL; setbeacondata = NULL; return FreeLibrary(dllhandle); } else { return 1; } } // --- Load --- void _stdcall Load () { if (load != NULL) load(); } // --- Dispose --- void _stdcall Dispose () { if (dispose != NULL) dispose(); } // --- GetPluginVersion --- int _stdcall GetPluginVersion () { if (getpluginversion != NULL) { return getpluginversion(); } else { return 0; } } // --- SetVehicleSpec --- void _stdcall SetVehicleSpec (ATS_VEHICLESPEC* vehicleSpec) { if (setvehiclespec != NULL) setvehiclespec(*vehicleSpec); } // --- Initialize --- void _stdcall Initialize (int brake) { if (initialize != NULL) initialize(brake); } // --- Elapse --- void _stdcall Elapse (ATS_HANDLES* atsHandles, ATS_VEHICLESTATE* vehicleState, int* panel, int* sound) { if (elapse != NULL) { ATS_HANDLES handles = elapse(*vehicleState, panel, sound); atsHandles->Brake = handles.Brake; atsHandles->Power = handles.Power; atsHandles->Reverser = handles.Reverser; atsHandles->ConstantSpeed = handles.ConstantSpeed; } } // --- SetPower --- void _stdcall SetPower(int notch) { if (setpower != NULL) setpower(notch); } // --- SetBrake --- void _stdcall SetBrake(int notch) { if (setbrake != NULL) setbrake(notch); } // --- SetReverser --- void _stdcall SetReverser(int pos) { if (setreverser != NULL) setreverser(pos); } // --- KeyDown --- void _stdcall KeyDown(int atsKeyCode) { if (keydown != NULL) keydown(atsKeyCode); } // --- KeyUp --- void _stdcall KeyUp(int atsKeyCode) { if (keyup != NULL) keyup(atsKeyCode); } // --- HornBlow --- void _stdcall HornBlow(int hornType) { if (hornblow != NULL) hornblow(hornType); } // --- DoorOpen --- void _stdcall DoorOpen() { if (dooropen != NULL) dooropen(); } // --- DoorClose --- void _stdcall DoorClose() { if (doorclose != NULL) doorclose(); } // --- SetSignal --- void _stdcall SetSignal(int signal) { if (setsignal != NULL) setsignal(signal); } // --- SetBeaconData --- void _stdcall SetBeaconData(ATS_BEACONDATA* beaconData) { if (setbeacondata != NULL) setbeacondata(*beaconData); }openbve-1.4.0.10/AtsPluginProxy/AtsPluginProxy.def000066400000000000000000000003341171674032100217730ustar00rootroot00000000000000EXPORTS LoadDLL UnloadDLL Load Dispose GetPluginVersion SetVehicleSpec Initialize Elapse SetPower SetBrake SetReverser KeyDown KeyUp HornBlow DoorOpen DoorClose SetSignal SetBeaconDataopenbve-1.4.0.10/AtsPluginProxy/Readme.txt000066400000000000000000000005031171674032100202770ustar00rootroot00000000000000This library has been compiled using Microsoft C/C++ 6.0. Only the code and export definition files are included in this distribution of the source code. You will need to create a project and import these files yourself for compilation. The encoding used by the files is US-ASCII (alternatively UTF-8 without BOM).openbve-1.4.0.10/Readme.txt000066400000000000000000000006471171674032100153400ustar00rootroot00000000000000============================ openBVE Source Code - Readme ============================ ----- Links ----- Official homepage: http://trainsimframework.org/ Official discussion board: http://openbve.freeforums.org/ ---------- No license ---------- This program is placed in the public domain. This means that you can make any modifications to it you like and share your modifications with others.openbve-1.4.0.10/openBVE/000077500000000000000000000000001171674032100146715ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBve.sln000066400000000000000000000147521171674032100167560ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 # SharpDevelop 4.1.0.8000 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{16553295-E70F-4596-AA78-848EEA576C4A}" ProjectSection(SolutionItems) = postProject EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sound.Flac", "Sound.Flac\Sound.Flac.csproj", "{081F5739-33DA-421A-B177-7B548D96646F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sound.RiffWave", "Sound.RiffWave\Sound.RiffWave.csproj", "{67418D38-1E2E-4944-A1B0-09E00FC2D055}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Texture.BmpGifJpegPngTiff", "Texture.BmpGifJpegPngTiff\Texture.BmpGifJpegPngTiff.csproj", "{4B775819-3574-443E-95AD-B40BC6EA6469}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBveAts", "OpenBveAts\OpenBveAts.csproj", "{15142E7B-35F5-4E81-AA18-79C4D60B4C26}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Texture.Ace", "Texture.Ace\Texture.Ace.csproj", "{06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBve", "OpenBve\OpenBve.csproj", "{34743421-2EB8-4F68-9600-AEAE79AECFA5}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBveApi", "OpenBveApi\OpenBveApi.csproj", "{27134980-4415-4375-A564-40A9014DFA5F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Debug|Any CPU.Build.0 = Debug|Any CPU {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Release|Any CPU.ActiveCfg = Release|Any CPU {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Release|Any CPU.Build.0 = Release|Any CPU {27134980-4415-4375-A564-40A9014DFA5F}.Debug|Any CPU.Build.0 = Debug|x86 {27134980-4415-4375-A564-40A9014DFA5F}.Debug|Any CPU.ActiveCfg = Debug|x86 {27134980-4415-4375-A564-40A9014DFA5F}.Release|Any CPU.Build.0 = Release|x86 {27134980-4415-4375-A564-40A9014DFA5F}.Release|Any CPU.ActiveCfg = Release|x86 {4B775819-3574-443E-95AD-B40BC6EA6469}.Debug|Any CPU.Build.0 = Debug|x86 {4B775819-3574-443E-95AD-B40BC6EA6469}.Debug|Any CPU.ActiveCfg = Debug|x86 {4B775819-3574-443E-95AD-B40BC6EA6469}.Release|Any CPU.Build.0 = Release|x86 {4B775819-3574-443E-95AD-B40BC6EA6469}.Release|Any CPU.ActiveCfg = Release|x86 {67418D38-1E2E-4944-A1B0-09E00FC2D055}.Debug|Any CPU.Build.0 = Debug|x86 {67418D38-1E2E-4944-A1B0-09E00FC2D055}.Debug|Any CPU.ActiveCfg = Debug|x86 {67418D38-1E2E-4944-A1B0-09E00FC2D055}.Release|Any CPU.Build.0 = Release|x86 {67418D38-1E2E-4944-A1B0-09E00FC2D055}.Release|Any CPU.ActiveCfg = Release|x86 {27134980-4415-4375-A564-40A9014DFA5F}.Debug|x86.Build.0 = Debug|x86 {27134980-4415-4375-A564-40A9014DFA5F}.Debug|x86.ActiveCfg = Debug|x86 {27134980-4415-4375-A564-40A9014DFA5F}.Release|x86.Build.0 = Release|x86 {27134980-4415-4375-A564-40A9014DFA5F}.Release|x86.ActiveCfg = Release|x86 {4B775819-3574-443E-95AD-B40BC6EA6469}.Debug|x86.Build.0 = Debug|x86 {4B775819-3574-443E-95AD-B40BC6EA6469}.Debug|x86.ActiveCfg = Debug|x86 {4B775819-3574-443E-95AD-B40BC6EA6469}.Release|x86.Build.0 = Release|x86 {4B775819-3574-443E-95AD-B40BC6EA6469}.Release|x86.ActiveCfg = Release|x86 {67418D38-1E2E-4944-A1B0-09E00FC2D055}.Debug|x86.Build.0 = Debug|x86 {67418D38-1E2E-4944-A1B0-09E00FC2D055}.Debug|x86.ActiveCfg = Debug|x86 {67418D38-1E2E-4944-A1B0-09E00FC2D055}.Release|x86.Build.0 = Release|x86 {67418D38-1E2E-4944-A1B0-09E00FC2D055}.Release|x86.ActiveCfg = Release|x86 {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Debug|x86.Build.0 = Debug|x86 {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Debug|x86.ActiveCfg = Debug|x86 {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Release|x86.Build.0 = Release|x86 {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Release|x86.ActiveCfg = Release|x86 {15142E7B-35F5-4E81-AA18-79C4D60B4C26}.Debug|Any CPU.Build.0 = Debug|x86 {15142E7B-35F5-4E81-AA18-79C4D60B4C26}.Debug|Any CPU.ActiveCfg = Debug|x86 {15142E7B-35F5-4E81-AA18-79C4D60B4C26}.Debug|x86.Build.0 = Debug|x86 {15142E7B-35F5-4E81-AA18-79C4D60B4C26}.Debug|x86.ActiveCfg = Debug|x86 {15142E7B-35F5-4E81-AA18-79C4D60B4C26}.Release|Any CPU.Build.0 = Release|x86 {15142E7B-35F5-4E81-AA18-79C4D60B4C26}.Release|Any CPU.ActiveCfg = Release|x86 {15142E7B-35F5-4E81-AA18-79C4D60B4C26}.Release|x86.Build.0 = Release|x86 {15142E7B-35F5-4E81-AA18-79C4D60B4C26}.Release|x86.ActiveCfg = Release|x86 {06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E}.Debug|Any CPU.Build.0 = Debug|x86 {06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E}.Debug|Any CPU.ActiveCfg = Debug|x86 {06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E}.Debug|x86.Build.0 = Debug|x86 {06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E}.Debug|x86.ActiveCfg = Debug|x86 {06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E}.Release|Any CPU.Build.0 = Release|x86 {06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E}.Release|Any CPU.ActiveCfg = Release|x86 {06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E}.Release|x86.Build.0 = Release|x86 {06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E}.Release|x86.ActiveCfg = Release|x86 {081F5739-33DA-421A-B177-7B548D96646F}.Debug|Any CPU.Build.0 = Debug|x86 {081F5739-33DA-421A-B177-7B548D96646F}.Debug|Any CPU.ActiveCfg = Debug|x86 {081F5739-33DA-421A-B177-7B548D96646F}.Debug|x86.Build.0 = Debug|x86 {081F5739-33DA-421A-B177-7B548D96646F}.Debug|x86.ActiveCfg = Debug|x86 {081F5739-33DA-421A-B177-7B548D96646F}.Release|Any CPU.Build.0 = Release|x86 {081F5739-33DA-421A-B177-7B548D96646F}.Release|Any CPU.ActiveCfg = Release|x86 {081F5739-33DA-421A-B177-7B548D96646F}.Release|x86.Build.0 = Release|x86 {081F5739-33DA-421A-B177-7B548D96646F}.Release|x86.ActiveCfg = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {081F5739-33DA-421A-B177-7B548D96646F} = {16553295-E70F-4596-AA78-848EEA576C4A} {67418D38-1E2E-4944-A1B0-09E00FC2D055} = {16553295-E70F-4596-AA78-848EEA576C4A} {4B775819-3574-443E-95AD-B40BC6EA6469} = {16553295-E70F-4596-AA78-848EEA576C4A} {15142E7B-35F5-4E81-AA18-79C4D60B4C26} = {16553295-E70F-4596-AA78-848EEA576C4A} {06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E} = {16553295-E70F-4596-AA78-848EEA576C4A} EndGlobalSection EndGlobal openbve-1.4.0.10/openBVE/OpenBve/000077500000000000000000000000001171674032100162275ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBve/Audio/000077500000000000000000000000001171674032100172705ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBve/Audio/Sounds.Convert.cs000066400000000000000000000150521171674032100225140ustar00rootroot00000000000000using System; using System.Collections.Generic; using OpenBveApi.Sounds; namespace OpenBve { internal static partial class Sounds { /// Mixes all channels into a single channel. /// The sound. /// The mono mix in the same format as the original. /// Raised when the bits per sample are not supported. private static byte[] GetMonoMix(Sound sound) { /* * Convert integer samples to floating-point samples. */ float[][] samples; if (sound.Bytes.Length == 1 || sound.Bytes[0].Length == 0) { return sound.Bytes[0]; } else if (sound.BitsPerSample == 8) { samples = new float[sound.Bytes.Length][]; for (int i = 0; i < sound.Bytes.Length; i++) { samples[i] = new float[sound.Bytes[i].Length]; for (int j = 0; j < sound.Bytes[i].Length; j++) { byte value = sound.Bytes[i][j]; samples[i][j] = ((float)value - 128.0f) / (value < 128 ? 128.0f : 127.0f); } } } else if (sound.BitsPerSample == 16) { samples = new float[sound.Bytes.Length][]; for (int i = 0; i < sound.Bytes.Length; i++) { samples[i] = new float[sound.Bytes[i].Length >> 1]; for (int j = 0; j < sound.Bytes[i].Length; j += 2) { short value = (short)(ushort)((int)sound.Bytes[i][j] | ((int)sound.Bytes[i][j + 1] << 8)); samples[i][j >> 1] = (float)value / (value < 0 ? 32768.0f : 32767.0f); } } } else { throw new NotSupportedException(); } /* * Mix floating-point samples to mono. * */ float[] mix = GetNormalizedMonoMix(samples); /* * Convert floating-point samples to integer samples. */ byte[] result; if (sound.BitsPerSample == 8) { result = new byte[mix.Length]; for (int i = 0; i < mix.Length; i++) { result[i] = (byte)((mix[i] < 0.0f ? 128.0f : 127.0f) * mix[i] + 128.0f); } } else if (sound.BitsPerSample == 16) { result = new byte[2 * mix.Length]; for (int i = 0; i < mix.Length; i++) { int value = (int)(ushort)(short)((mix[i] < 0.0f ? 32768.0f : 32767.0f) * mix[i]); result[2 * i + 0] = (byte)value; result[2 * i + 1] = (byte)(value >> 8); } } else { throw new NotSupportedException(); } return result; } private static float[] GetNormalizedMonoMix(float[][] samples) { /* * This mixer tries to find silent channels and discards them. * It then performs a mix to mono for all remaining channels * and tries to detect destructive interference (in which case * the first non-silent channel is returned). In all other cases, * volume in the mono mix is normalized to the average volume * in all non-silent channels. If necessary, the volume is * further normalized to prevent overflow. This also prevents * constructive interference. * */ // --- determine the volume per channel and the total volume --- float[] channelVolume = new float[samples.Length]; float totalVolume = 0.0f; for (int i = 0; i < samples.Length; i++) { for (int j = 0; j < samples[i].Length; j++) { channelVolume[i] += Math.Abs(samples[i][j]); } channelVolume[i] /= samples[i].Length; totalVolume += channelVolume[i]; } totalVolume /= samples.Length; // --- discard all channels that are below // a certain threshold of the total volume --- const float silentThreshold = 0.05f; float[][] remainingSamples = new float[samples.Length][]; int remainingSamplesUsed = 0; for (int i = 0; i < samples.Length; i++) { if (channelVolume[i] > silentThreshold * totalVolume) { channelVolume[remainingSamplesUsed] = channelVolume[i]; remainingSamples[remainingSamplesUsed] = samples[i]; remainingSamplesUsed++; } } if (remainingSamplesUsed == 1) { return remainingSamples[0]; } else if (remainingSamplesUsed == 0) { remainingSamples = samples; remainingSamplesUsed = samples.Length; } else { totalVolume = 0.0f; for (int i = 0; i < samples.Length; i++) { totalVolume += channelVolume[i]; } totalVolume /= remainingSamplesUsed; } // --- produce a mono mix from all remaining channels --- float[] mix = new float[remainingSamples[0].Length]; float mixVolume = 0.0f; for (int j = 0; j < remainingSamples[0].Length; j++) { for (int i = 0; i < remainingSamplesUsed; i++) { mix[j] += remainingSamples[i][j]; } mix[j] /= remainingSamplesUsed; mixVolume += Math.Abs(mix[j]); } mixVolume /= remainingSamples[0].Length; // --- if the volume in the mono mix is below // a certain threshold of the total volume, // assume destructive interference and return // the first non-silent channel --- const float destructiveInterferenceThreshold = 0.05f; if (mixVolume < destructiveInterferenceThreshold * totalVolume) { return remainingSamples[0]; } // --- normalize the volume in the mono mix so that // it corresponds to the average total volume --- float maximum = 0.0f; for (int j = 0; j < mix.Length; j++) { mix[j] *= totalVolume / mixVolume; float value = Math.Abs(mix[j]); if (value > maximum) maximum = value; } // --- if the maximum value now created exceeds the // permissible range, normalize the mono mix further --- if (maximum > 1.0f) { for (int j = 0; j < mix.Length; j++) { mix[j] /= maximum; } } return mix; } // private static float[] GetCombiningMonoMix(float[][] samples) { // float[] mix = new float[samples[0].Length]; // for (int j = 0; j < mix.Length; j++) { // mix[j] = samples[0][j]; // for (int i = 1; i < samples.Length; i++) { // mix[j] = GetCombiningMonoMix(mix[j], samples[i][j]); // } // } // return mix; // } // private static float GetCombiningMonoMix(float a, float b) { // if (a < 0.0f & b < 0.0f) { // return a + b + a * b; // } else if (a > 0.0f & b > 0.0f) { // return a + b - a * b; // } else { // return a + b; // } // } // private static float[] GetShiftedMonoMix(float[][] samples, int shift) { // if (samples.Length != 2) { // throw new NotSupportedException(); // } // float[] mix = new float[samples[0].Length]; // for (int j = 0; j < shift; j++) { // mix[j] = samples[0][j]; // } // for (int j = 0; j < samples[0].Length - shift; j++) { // mix[j] = samples[0][j] + samples[1][j + shift]; // } // return mix; // } } }openbve-1.4.0.10/openBVE/OpenBve/Audio/Sounds.SoundBuffer.cs000066400000000000000000000027011171674032100233130ustar00rootroot00000000000000using System; using OpenBveApi.Sounds; namespace OpenBve { internal static partial class Sounds { /// Represents a sound buffer. internal class SoundBuffer : SoundHandle { // --- members --- /// The origin where the sound can be loaded from. internal SoundOrigin Origin; /// The default effective radius. internal double Radius; /// Whether the sound is loaded and the OpenAL sound name is valid. internal bool Loaded; /// The OpenAL sound name. Only valid if the sound is loaded. internal int OpenAlBufferName; /// The duration of the sound in seconds. Only valid if the sound is loaded. internal double Duration; /// Whether to ignore further attemps to load the sound after previous attempts have failed. internal bool Ignore; // --- constructors --- internal SoundBuffer(string path, double radius) { this.Origin = new PathOrigin(path); this.Radius = radius; this.Loaded = false; this.OpenAlBufferName = 0; this.Duration = 0.0; this.Ignore = false; } internal SoundBuffer(OpenBveApi.Sounds.Sound sound, double radius) { this.Origin = new RawOrigin(sound); this.Radius = radius; this.Loaded = false; this.OpenAlBufferName = 0; this.Duration = 0.0; this.Ignore = false; } } } }openbve-1.4.0.10/openBVE/OpenBve/Audio/Sounds.SoundOrigin.cs000066400000000000000000000123511171674032100233330ustar00rootroot00000000000000#pragma warning disable 0659, 0661 using System; namespace OpenBve { internal static partial class Sounds { /* * A sound origin defines where to load a sound from. * This can be a path (file or directory), or raw data. * */ // --- sound origin --- /// Represents the origin where the sound can be loaded from. internal abstract class SoundOrigin { // --- functions --- /// Gets the sound from this origin. /// Receives the sound. /// Whether the sound could be obtained successfully. internal abstract bool GetSound(out OpenBveApi.Sounds.Sound sound); // --- operators --- /// Checks whether two origins are equal. /// The first origin. /// The second origin. /// Whether the two origins are equal. public static bool operator ==(SoundOrigin a, SoundOrigin b) { if (a is PathOrigin & b is PathOrigin) { return (PathOrigin)a == (PathOrigin)b; } else { return object.ReferenceEquals(a, b); } } /// Checks whether two origins are unequal. /// The first origin. /// The second origin. /// Whether the two origins are unequal. public static bool operator !=(SoundOrigin a, SoundOrigin b) { if (a is PathOrigin & b is PathOrigin) { return (PathOrigin)a != (PathOrigin)b; } else { return !object.ReferenceEquals(a, b); } } /// Checks whether this instance is equal to the specified object. /// The object. /// Whether this instance is equal to the specified object. public override bool Equals(object obj) { if (this is PathOrigin & obj is PathOrigin) { return (PathOrigin)this == (PathOrigin)obj; } else { return object.ReferenceEquals(this, obj); } } } // --- path origin --- /// Represents a file or directory where the sound can be loaded from. internal class PathOrigin : SoundOrigin { // --- members --- internal string Path; // --- constructors --- /// Creates a new path origin. /// The path to the sound. internal PathOrigin(string path) { this.Path = path; } // --- functions --- /// Gets the sound from this origin. /// Receives the sound. /// Whether the sound could be obtained successfully. internal override bool GetSound(out OpenBveApi.Sounds.Sound sound) { if (!Program.CurrentHost.LoadSound(this.Path, out sound)) { sound = null; return false; } else { return true; } } // --- operators --- /// Checks whether two origins are equal. /// The first origin. /// The second origin. /// Whether the two origins are equal. public static bool operator ==(PathOrigin a, PathOrigin b) { if (object.ReferenceEquals(a, b)) return true; if (object.ReferenceEquals(a, null)) return false; if (object.ReferenceEquals(b, null)) return false; return a.Path == b.Path; } /// Checks whether two origins are unequal. /// The first origin. /// The second origin. /// Whether the two origins are unequal. public static bool operator !=(PathOrigin a, PathOrigin b) { if (object.ReferenceEquals(a, b)) return false; if (object.ReferenceEquals(a, null)) return true; if (object.ReferenceEquals(b, null)) return true; return a.Path != b.Path; } /// Checks whether this instance is equal to the specified object. /// The object. /// Whether this instance is equal to the specified object. public override bool Equals(object obj) { if (object.ReferenceEquals(this, obj)) return true; if (object.ReferenceEquals(this, null)) return false; if (object.ReferenceEquals(obj, null)) return false; if (!(obj is PathOrigin)) return false; return this.Path == ((PathOrigin)obj).Path; } } // --- raw origin --- /// Represents sound raw data. internal class RawOrigin : SoundOrigin { // --- members --- /// The sound raw data. internal OpenBveApi.Sounds.Sound Sound; // --- constructors --- /// Creates a new raw data origin. /// The sound raw data. internal RawOrigin(OpenBveApi.Sounds.Sound sound) { this.Sound = sound; } // --- functions --- /// Gets the sound from this origin. /// Receives the sound. /// Whether the sound could be obtained successfully. internal override bool GetSound(out OpenBveApi.Sounds.Sound sound) { sound = this.Sound; return true; } } } }openbve-1.4.0.10/openBVE/OpenBve/Audio/Sounds.SoundSource.cs000066400000000000000000000065621171674032100233530ustar00rootroot00000000000000using System; namespace OpenBve { internal static partial class Sounds { /// Represents the state of a sound source. internal enum SoundSourceState { /// The sound will start playing once in audible range. The OpenAL sound name is not yet valid. PlayPending, /// The sound is playing and the OpenAL source name is valid. Playing, /// The sound will stop playing. The OpenAL sound name is still valid. StopPending, /// The sound has stopped and will be removed from the list of sound sources. The OpenAL source name is not valid any longer. Stopped } /// Represents a sound source. internal class SoundSource { // --- members --- /// The sound buffer. internal SoundBuffer Buffer; /// The effective sound radius. internal double Radius; /// The pitch change factor. internal double Pitch; /// The volume change factor. internal double Volume; /// The position. If a train and car are specified, the position is relative to the car, otherwise absolute. internal OpenBveApi.Math.Vector3 Position; /// The train this sound is attached to, or a null reference. internal TrainManager.Train Train; /// The car this sound is attached to, or a null reference. internal int Car; /// Whether this sound plays in a loop. internal bool Looped; /// The current state of the sound. Determines if the OpenAL sound name is valid. internal SoundSourceState State; /// The OpenAL source name. Only valid if the sound is playing. internal int OpenAlSourceName; // --- constructors --- /// Creates a new sound source. /// The sound buffer. /// The effective sound radius. /// The pitch change factor. /// The volume change factor. /// The position. If a train and car are specified, the position is relative to the car, otherwise absolute. /// The train this sound source is attached to, or a null reference. /// The car this sound source is attached to, or a null reference. /// Whether this sound source plays in a loop. internal SoundSource(SoundBuffer buffer, double radius, double pitch, double volume, OpenBveApi.Math.Vector3 position, TrainManager.Train train, int car, bool looped) { this.Buffer = buffer; this.Radius = radius; this.Pitch = pitch; this.Volume = volume; this.Position = position; this.Train = train; this.Car = car; this.Looped = looped; this.State = SoundSourceState.PlayPending; this.OpenAlSourceName = 0; } // --- functions --- /// Stops this sound. internal void Stop() { if (this.State == SoundSourceState.PlayPending) { this.State = SoundSourceState.Stopped; } else if (this.State == SoundSourceState.Playing) { this.State = SoundSourceState.StopPending; } } } } }openbve-1.4.0.10/openBVE/OpenBve/Audio/Sounds.Update.cs000066400000000000000000000314031171674032100223140ustar00rootroot00000000000000using System; using Tao.OpenAl; namespace OpenBve { internal static partial class Sounds { /// Updates the sound component. Should be called every frame. /// The time in seconds that elapsed since the last call to this function. internal static void Update(double timeElapsed) { /* * Set up the listener * */ OpenBveApi.Math.Vector3 listenerPosition = World.AbsoluteCameraPosition; OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); OpenBveApi.Math.Vector3 listenerVelocity; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead | World.CameraMode == World.CameraViewMode.Exterior) { TrainManager.Car car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]; OpenBveApi.Math.Vector3 diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3.Normalize(diff) + World.CameraAlignmentSpeed.Position; } else { listenerVelocity = World.CameraAlignmentSpeed.Position; } Al.alListener3f(Al.AL_POSITION, 0.0f, 0.0f, 0.0f); Al.alListener3f(Al.AL_VELOCITY, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); Al.alListenerfv(Al.AL_ORIENTATION, new float[] { (float)listenerOrientation.Z.X, (float)listenerOrientation.Z.Y, (float)listenerOrientation.Z.Z, -(float)listenerOrientation.Y.X, -(float)listenerOrientation.Y.Y, -(float)listenerOrientation.Y.Z }); /* * Set up the atmospheric attributes * */ double elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double airTemperature = Game.GetAirTemperature(elevation); double airPressure = Game.GetAirPressure(elevation, airTemperature); double airDensity = Game.GetAirDensity(airPressure, airTemperature); double speedOfSound = Game.GetSpeedOfSound(airPressure, airTemperature); try { Al.alSpeedOfSound((float)speedOfSound); } catch { } /* * Update the sound sources * */ int actuallyPlaying = 0; for (int i = 0; i < SourceCount; i++) { if (Sources[i].State == SoundSourceState.StopPending) { /* * The sound is still playing but is to be stopped. * Stop the sound, then remove it from the list of * sound sources. * */ Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (Sources[i].State == SoundSourceState.Stopped) { /* * The sound was already stopped. Remove it from * the list of sound sources. * */ Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (GlobalMute) { /* * The sound is playing or about to be played, but * the global mute option is enabled. Stop the sound * sound if necessary, then remove it from the list * of sound sources if the sound is not looping. * */ if (Sources[i].State == SoundSourceState.Playing) { Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * The sound is to be played or is already playing. * Calculate the sound gain. * */ OpenBveApi.Math.Vector3 position; OpenBveApi.Math.Vector3 velocity; if (Sources[i].Train != null) { OpenBveApi.Math.Vector3 direction; TrainManager.CreateWorldCoordinates(Sources[i].Train, Sources[i].Car, Sources[i].Position.X, Sources[i].Position.Y, Sources[i].Position.Z, out position.X, out position.Y, out position.Z, out direction.X, out direction.Y, out direction.Z); velocity = Sources[i].Train.Cars[Sources[i].Car].Specs.CurrentSpeed * direction; } else { position = Sources[i].Position; velocity = OpenBveApi.Math.Vector3.Null; } OpenBveApi.Math.Vector3 positionDifference = position - listenerPosition; double gain; if (GlobalMute) { gain = 0.0; } else if (Interface.CurrentOptions.SoundModel == SoundModels.Experimental) { // --- inverse distance clamped model (experimental) --- double distance = positionDifference.Norm(); double radius = Sources[i].Radius; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) { if (Sources[i].Train != TrainManager.PlayerTrain || Sources[i].Car != TrainManager.PlayerTrain.DriverCar) { radius *= 0.5; } } if (distance < 2.0 * radius) { gain = 1.0 - distance * distance * (4.0 * radius - distance) / (16.0 * radius * radius * radius); } else { gain = radius / distance; } gain -= Math.Exp(ClampFactorLogarithm) * distance * distance; gain *= Sources[i].Volume; } else { // --- linear distance clamped model (default) --- double distance = positionDifference.Norm(); double innerRadius = Sources[i].Radius; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) { if (Sources[i].Train != TrainManager.PlayerTrain || Sources[i].Car != TrainManager.PlayerTrain.DriverCar) { innerRadius *= 0.5; } } double outerRadius = OuterRadiusFactor * innerRadius; if (distance < outerRadius) { if (distance <= innerRadius) { gain = Sources[i].Volume; } else { gain = (distance - outerRadius) / (innerRadius - outerRadius); gain *= Sources[i].Volume; } gain = 3.0 * gain * gain - 2.0 * gain * gain * gain; } else { gain = 0.0; } } if (gain <= GainThreshold) { /* * If the gain is too low to be audible, stop the sound. * If the sound is not looping, stop it if necessary, * then remove it from the list of sound sources. * */ if (Sources[i].State == SoundSourceState.Playing) { Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * Play the sound and update position, velocity, pitch and gain. * For non-looping sounds, check if the sound is still playing. * */ gain = (gain - GainThreshold) / (1.0 - GainThreshold); if (Sources[i].State != SoundSourceState.Playing) { LoadBuffer(Sources[i].Buffer); if (Sources[i].Buffer.Loaded) { Al.alGenSources(1, out Sources[i].OpenAlSourceName); Al.alSourcei(Sources[i].OpenAlSourceName, Al.AL_BUFFER, Sources[i].Buffer.OpenAlBufferName); } else { /* * We cannot play the sound because * the buffer could not be loaded. * */ Sources[i].State = SoundSourceState.Stopped; continue; } } Al.alSource3f(Sources[i].OpenAlSourceName, Al.AL_POSITION, (float)positionDifference.X, (float)positionDifference.Y, (float)positionDifference.Z); Al.alSource3f(Sources[i].OpenAlSourceName, Al.AL_VELOCITY, (float)velocity.X, (float)velocity.Y, (float)velocity.Z); Al.alSourcef(Sources[i].OpenAlSourceName, Al.AL_PITCH, (float)Sources[i].Pitch); Al.alSourcef(Sources[i].OpenAlSourceName, Al.AL_GAIN, (float)gain); if (Sources[i].State != SoundSourceState.Playing) { Al.alSourcei(Sources[i].OpenAlSourceName, Al.AL_LOOPING, Sources[i].Looped ? Al.AL_TRUE : Al.AL_FALSE); Al.alSourcePlay(Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Playing; } if (!Sources[i].Looped) { int state; Al.alGetSourcei(Sources[i].OpenAlSourceName, Al.AL_SOURCE_STATE, out state); if (state != Al.AL_INITIAL & state != Al.AL_PLAYING) { /* * The sound is not playing any longer. * Remove it from the list of sound sources. * */ Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else { actuallyPlaying++; } } else { actuallyPlaying++; } } } } /* * Adjust the outer radius factor / the clamp factor. * */ if (Interface.CurrentOptions.SoundModel == SoundModels.Experimental) { const double slowSpeed = 1.0; const double fastSpeed = 5.0; if (actuallyPlaying > Interface.CurrentOptions.SoundNumber) { ClampFactorLogarithmSpeed += fastSpeed * timeElapsed; if (ClampFactorLogarithmSpeed > fastSpeed) { ClampFactorLogarithmSpeed = fastSpeed; } } else if (actuallyPlaying > Interface.CurrentOptions.SoundNumber - 2) { ClampFactorLogarithmSpeed += timeElapsed; if (ClampFactorLogarithmSpeed > slowSpeed) { ClampFactorLogarithmSpeed = slowSpeed; } } else if (actuallyPlaying > Interface.CurrentOptions.SoundNumber - 8) { if (ClampFactorLogarithmSpeed < 0.0) { if (ClampFactorLogarithmSpeed < -fastSpeed) { ClampFactorLogarithmSpeed = -fastSpeed; } else { ClampFactorLogarithmSpeed += timeElapsed; if (ClampFactorLogarithmSpeed > 0.0) { ClampFactorLogarithmSpeed = 0.0; } } } else if (ClampFactorLogarithmSpeed > 0.0) { if (ClampFactorLogarithmSpeed > fastSpeed) { ClampFactorLogarithmSpeed = fastSpeed; } else { ClampFactorLogarithmSpeed -= timeElapsed; if (ClampFactorLogarithmSpeed < 0.0) { ClampFactorLogarithmSpeed = 0.0; } } } } else if (actuallyPlaying > Interface.CurrentOptions.SoundNumber - 10) { ClampFactorLogarithmSpeed -= timeElapsed; if (ClampFactorLogarithmSpeed < -slowSpeed) { ClampFactorLogarithmSpeed = -slowSpeed; } } else { ClampFactorLogarithmSpeed -= timeElapsed; if (ClampFactorLogarithmSpeed < -fastSpeed) { ClampFactorLogarithmSpeed = -fastSpeed; } } ClampFactorLogarithm += timeElapsed * ClampFactorLogarithmSpeed; if (ClampFactorLogarithm < ClampFactorLogarithmMinimum) { ClampFactorLogarithm = ClampFactorLogarithmMinimum; ClampFactorLogarithmSpeed = 0.0; } else if (ClampFactorLogarithm > ClampFactorLogarithmMaximum) { ClampFactorLogarithm = ClampFactorLogarithmMaximum; ClampFactorLogarithmSpeed = 0.0; } } else { if (actuallyPlaying >= Interface.CurrentOptions.SoundNumber - 2) { /* * Too many sounds are playing. * Reduce the outer radius factor. * */ OuterRadiusFactorSpeed -= timeElapsed; if (OuterRadiusFactorSpeed < -OuterRadiusFactorMaximumSpeed) { OuterRadiusFactorSpeed = -OuterRadiusFactorMaximumSpeed; } } else if (actuallyPlaying <= Interface.CurrentOptions.SoundNumber - 6) { /* * Only few sounds are playing. * Increase the outer radius factor. * */ OuterRadiusFactorSpeed += timeElapsed; if (OuterRadiusFactorSpeed > OuterRadiusFactorMaximumSpeed) { OuterRadiusFactorSpeed = OuterRadiusFactorMaximumSpeed; } } else { /* * Neither too many nor too few sounds are playing. * Stabilize the outer radius factor. * */ if (OuterRadiusFactorSpeed < 0.0) { OuterRadiusFactorSpeed += timeElapsed; if (OuterRadiusFactorSpeed > 0.0) { OuterRadiusFactorSpeed = 0.0; } } else { OuterRadiusFactorSpeed -= timeElapsed; if (OuterRadiusFactorSpeed < 0.0) { OuterRadiusFactorSpeed = 0.0; } } } OuterRadiusFactor += OuterRadiusFactorSpeed * timeElapsed; if (OuterRadiusFactor < OuterRadiusFactorMinimum) { OuterRadiusFactor = OuterRadiusFactorMinimum; OuterRadiusFactorSpeed = 0.0; } else if (OuterRadiusFactor > OuterRadiusFactorMaximum) { OuterRadiusFactor = OuterRadiusFactorMaximum; OuterRadiusFactorSpeed = 0.0; } } } } }openbve-1.4.0.10/openBVE/OpenBve/Audio/Sounds.cs000066400000000000000000000325011171674032100210730ustar00rootroot00000000000000using System; using System.Windows.Forms; using Tao.OpenAl; namespace OpenBve { internal static partial class Sounds { // --- enumerations --- /// Represents different sound distance attenuation models. internal enum SoundModels { /// Represents the default Linear Distance Clamped Model. Default = 0, /// Represents the experimental Inverse Distance Clamped Model. Experimental = 1 } // --- members --- /// The current OpenAL device. private static IntPtr OpenAlDevice = IntPtr.Zero; /// The current OpenAL context. private static IntPtr OpenAlContext = IntPtr.Zero; /// A list of all sound buffers. private static SoundBuffer[] Buffers = new SoundBuffer[16]; /// The number of sound buffers. private static int BufferCount = 0; /// A list of all sound sources. private static SoundSource[] Sources = new SoundSource[16]; /// The number of sound sources. private static int SourceCount = 0; /// The gain threshold. Sounds with gains below this value are not played. internal const double GainThreshold = 0.0001; /// Whether all sounds are mute. internal static bool GlobalMute = false; // --- linear distance clamp model --- /// The factor by which the inner radius is multiplied to give the outer radius. internal static double OuterRadiusFactor; internal static double OuterRadiusFactorSpeed; private static double OuterRadiusFactorMaximumSpeed; internal static double OuterRadiusFactorMinimum; internal static double OuterRadiusFactorMaximum; // --- inverse distance clamp model --- internal static double ClampFactorLogarithm = -18.4206807439524; internal static double ClampFactorLogarithmSpeed = 0.0; internal const double ClampFactorLogarithmMinimum = -27.6310211159285; internal const double ClampFactorLogarithmMaximum = -4.60517018598809; // --- initialization and deinitialization --- /// Initializes audio. A call to Deinitialize must be made when terminating the program. /// Whether initializing audio was successful. internal static bool Initialize() { Deinitialize(); switch (Interface.CurrentOptions.SoundRange) { case Interface.SoundRange.Low: OuterRadiusFactorMinimum = 2.0; OuterRadiusFactorMaximum = 8.0; OuterRadiusFactorMaximumSpeed = 1.0; break; case Interface.SoundRange.Medium: OuterRadiusFactorMinimum = 4.0; OuterRadiusFactorMaximum = 16.0; OuterRadiusFactorMaximumSpeed = 2.0; break; case Interface.SoundRange.High: OuterRadiusFactorMinimum = 6.0; OuterRadiusFactorMaximum = 24.0; OuterRadiusFactorMaximumSpeed = 3.0; break; } OuterRadiusFactor = Math.Sqrt(OuterRadiusFactorMinimum * OuterRadiusFactorMaximum); OuterRadiusFactorSpeed = 0.0; OpenAlDevice = Alc.alcOpenDevice(null); if (OpenAlDevice != IntPtr.Zero) { OpenAlContext = Alc.alcCreateContext(OpenAlDevice, IntPtr.Zero); if (OpenAlContext != IntPtr.Zero) { Alc.alcMakeContextCurrent(OpenAlContext); try { Al.alSpeedOfSound(343.0f); } catch { MessageBox.Show("OpenAL 1.1 is required. You seem to have OpenAL 1.0.", "openBVE", MessageBoxButtons.OK, MessageBoxIcon.Hand); } Al.alDistanceModel(Al.AL_NONE); return true; } else { Alc.alcCloseDevice(OpenAlDevice); OpenAlDevice = IntPtr.Zero; MessageBox.Show("The OpenAL context could not be created.", "openBVE", MessageBoxButtons.OK, MessageBoxIcon.Hand); return false; } } else { OpenAlContext = IntPtr.Zero; MessageBox.Show("The OpenAL sound device could not be opened.", "openBVE", MessageBoxButtons.OK, MessageBoxIcon.Hand); return false; } } /// Deinitializes audio. internal static void Deinitialize() { StopAllSounds(); UnloadAllBuffers(); if (OpenAlContext != IntPtr.Zero) { Alc.alcMakeContextCurrent(IntPtr.Zero); Alc.alcDestroyContext(OpenAlContext); OpenAlContext = IntPtr.Zero; } if (OpenAlDevice != IntPtr.Zero) { Alc.alcCloseDevice(OpenAlDevice); OpenAlDevice = IntPtr.Zero; } } // --- registering buffers --- /// Registers a sound buffer and returns a handle to the buffer. /// The path to the sound. /// The default effective radius. /// The handle to the sound buffer. internal static SoundBuffer RegisterBuffer(string path, double radius) { for (int i = 0; i < BufferCount; i++) { if (Buffers[i].Origin is PathOrigin) { if (((PathOrigin)Buffers[i].Origin).Path == path) { return Buffers[i]; } } } if (Buffers.Length == BufferCount) { Array.Resize(ref Buffers, Buffers.Length << 1); } Buffers[BufferCount] = new SoundBuffer(path, radius); BufferCount++; return Buffers[BufferCount - 1]; } /// Registers a sound buffer and returns a handle to the buffer. /// The raw sound data. /// The default effective radius. /// The handle to the sound buffer. internal static SoundBuffer RegisterBuffer(OpenBveApi.Sounds.Sound data, double radius) { if (Buffers.Length == BufferCount) { Array.Resize(ref Buffers, Buffers.Length << 1); } Buffers[BufferCount] = new SoundBuffer(data, radius); BufferCount++; return Buffers[BufferCount - 1]; } // --- loading buffers --- /// Loads the specified sound buffer. /// The sound buffer. /// Whether loading the buffer was successful. internal static bool LoadBuffer(SoundBuffer buffer) { if (buffer.Loaded) { return true; } else if (buffer.Ignore) { return false; } else { OpenBveApi.Sounds.Sound sound; if (buffer.Origin.GetSound(out sound)) { if (sound.BitsPerSample == 8 | sound.BitsPerSample == 16) { byte[] bytes = GetMonoMix(sound); Al.alGenBuffers(1, out buffer.OpenAlBufferName); int format = sound.BitsPerSample == 8 ? Al.AL_FORMAT_MONO8 : Al.AL_FORMAT_MONO16; Al.alBufferData(buffer.OpenAlBufferName, format, bytes, bytes.Length, sound.SampleRate); buffer.Duration = sound.Duration; buffer.Loaded = true; return true; } } } buffer.Ignore = true; return false; } /// Loads all sound buffers immediately. internal static void LoadAllBuffers() { for (int i = 0; i < BufferCount; i++) { LoadBuffer(Buffers[i]); } } // --- unloading buffers --- /// Unloads the specified sound buffer. /// internal static void UnloadBuffer(SoundBuffer buffer) { if (buffer.Loaded) { Al.alDeleteBuffers(1, ref buffer.OpenAlBufferName); buffer.OpenAlBufferName = 0; buffer.Loaded = false; buffer.Ignore = false; } } /// Unloads all sound buffers immediately. internal static void UnloadAllBuffers() { for (int i = 0; i < BufferCount; i++) { UnloadBuffer(Buffers[i]); } } // --- play or stop sounds --- /// Plays a sound. /// The sound buffer. /// The pitch change factor. /// The volume change factor. /// The position. If a train and car are specified, the position is relative to the car, otherwise absolute. /// Whether to play the sound in a loop. /// The sound source. internal static SoundSource PlaySound(SoundBuffer buffer, double pitch, double volume, OpenBveApi.Math.Vector3 position, bool looped) { if (Sources.Length == SourceCount) { Array.Resize(ref Sources, Sources.Length << 1); } Sources[SourceCount] = new SoundSource(buffer, buffer.Radius, pitch, volume, position, null, 0, looped); SourceCount++; return Sources[SourceCount - 1]; } /// Plays a sound. /// The sound buffer. /// The pitch change factor. /// The volume change factor. /// The position. If a train and car are specified, the position is relative to the car, otherwise absolute. /// The train the sound is attached to, or a null reference. /// The car in the train the sound is attached to. /// Whether to play the sound in a loop. /// The sound source. internal static SoundSource PlaySound(SoundBuffer buffer, double pitch, double volume, OpenBveApi.Math.Vector3 position, TrainManager.Train train, int car, bool looped) { if (Sources.Length == SourceCount) { Array.Resize(ref Sources, Sources.Length << 1); } Sources[SourceCount] = new SoundSource(buffer, buffer.Radius, pitch, volume, position, train, car, looped); SourceCount++; return Sources[SourceCount - 1]; } /// Stops the specified sound source. /// The sound source, or a null reference. internal static void StopSound(SoundSource source) { if (source != null) { if (source.State == SoundSourceState.Playing) { Al.alDeleteSources(1, ref source.OpenAlSourceName); source.OpenAlSourceName = 0; } source.State = SoundSourceState.Stopped; } } /// Stops all sounds. internal static void StopAllSounds() { for (int i = 0; i < SourceCount; i++) { if (Sources[i].State == SoundSourceState.Playing) { Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].OpenAlSourceName = 0; } Sources[i].State = SoundSourceState.Stopped; } } /// Stops all sounds that are attached to the specified train. /// The train. internal static void StopAllSounds(TrainManager.Train train) { for (int i = 0; i < SourceCount; i++) { if (Sources[i].Train == train) { if (Sources[i].State == SoundSourceState.Playing) { Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].OpenAlSourceName = 0; } Sources[i].State = SoundSourceState.Stopped; } } } // --- tests --- /// Checks whether the specified sound is playing or supposed to be playing. /// The sound source, or a null reference. /// Whether the sound is playing or supposed to be playing. internal static bool IsPlaying(SoundSource source) { if (source != null) { if (source.State == SoundSourceState.PlayPending | source.State == SoundSourceState.Playing) { return true; } } return false; } /// Checks whether the specified sound is stopped or supposed to be stopped. /// The sound source, or a null reference. /// Whether the sound is stopped or supposed to be stopped. internal static bool IsStopped(SoundSource source) { if (source != null) { if (source.State == SoundSourceState.StopPending | source.State == SoundSourceState.Stopped) { return true; } } return false; } /// Gets the duration of the specified sound buffer in seconds. /// The sound buffer. /// The duration of the sound buffer in seconds, or zero if the buffer could not be loaded. internal static double GetDuration(SoundBuffer buffer) { LoadBuffer(buffer); return buffer.Duration; } // --- statistics --- /// Gets the number of registered sound buffers. /// The number of registered sound buffers. internal static int GetNumberOfRegisteredBuffers() { return BufferCount; } /// Gets the number of loaded sound buffers. /// The number of loaded sound buffers. internal static int GetNumberOfLoadedBuffers() { int count = 0; for (int i = 0; i < BufferCount; i++) { if (Buffers[i].Loaded) { count++; } } return count; } /// Gets the number of registered sound sources. /// The number of registered sound sources. internal static int GetNumberOfRegisteredSources() { return SourceCount; } /// Gets the number of playing sound sources. /// The number of playing sound sources. internal static int GetNumberOfPlayingSources() { int count = 0; for (int i = 0; i < SourceCount; i++) { if (Sources[i].State == SoundSourceState.Playing) { count++; } } return count; } } }openbve-1.4.0.10/openBVE/OpenBve/Graphics/000077500000000000000000000000001171674032100177675ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBve/Graphics/Camera.cs000066400000000000000000000044441171674032100215140ustar00rootroot00000000000000using System; using Tao.OpenGl; namespace OpenBve { internal class Camera { // --- members --- /// The position of the camera. internal OpenBveApi.Geometry.Vector3 Position; /// The orientation of the camera. internal OpenBveApi.Geometry.Orientation3 Orientation; /// The horizontal viewing angle in radians. internal double HorizontalViewingAngle; /// The vertical viewing angle in radians. internal double VerticalViewingAngle; /// The viewing distance. internal double ViewingDistance; // --- constructors --- /// Creates a new camera. A call to SetViewingAngle must be made to set the perspective in OpenGL. /// The viewing distance in meters. internal Camera(double viewingDistance) { const double degrees = 0.0174532925199433; this.Position = OpenBveApi.Geometry.Vector3.Null; this.Orientation = OpenBveApi.Geometry.Orientation3.Default; this.HorizontalViewingAngle = 45.0 * degrees; this.VerticalViewingAngle = 45.0 * degrees; this.ViewingDistance = viewingDistance; } // --- functions --- /// Sets the viewing angle and updates the perspective accordingly. This function changes the current matrix to GL_MODELVIEW once finished. /// The vertical viewing angle in radians. internal void SetViewingAngle(double verticalViewingAngle) { double aspectRatio = (double)Screen.Width / (double)Screen.Height; this.HorizontalViewingAngle = 2.0 * Math.Atan(aspectRatio * Math.Tan(0.5 * this.VerticalViewingAngle)); this.VerticalViewingAngle = verticalViewingAngle; //Gl.glViewport(0, 0, Screen.Width, Screen.Height); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glLoadIdentity(); const double inverseDegrees = 57.295779513082320877; // TODO // // The old renderer assumes a negative aspect ratio. Once the new renderer // is implemented, change the aspect ratio in the following line to positive. Glu.gluPerspective(this.VerticalViewingAngle * inverseDegrees, -aspectRatio, 1.0, this.ViewingDistance); Gl.glMatrixMode(Gl.GL_MODELVIEW); } } }openbve-1.4.0.10/openBVE/OpenBve/Graphics/Fonts.cs000066400000000000000000000200621171674032100214070ustar00rootroot00000000000000using System; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Text; namespace OpenBve { /// Provides font support. internal static class Fonts { // --- structures --- /// Represents a single character. internal struct OpenGlFontChar { // --- members --- /// The texture coordinates that represent the character in the underlying texture. internal RectangleF TextureCoordinates; /// The physical size of the character. internal Size PhysicalSize; /// The typographic size of the character. internal Size TypographicSize; // --- constructors --- /// Creates a new character. /// The texture coordinates that represent the character in the underlying texture. /// The physical size of the character. /// The typographic size of the character. internal OpenGlFontChar(RectangleF textureCoordinates, Size physicalSize, Size typographicSize) { this.TextureCoordinates = textureCoordinates; this.PhysicalSize = physicalSize; this.TypographicSize = typographicSize; } } /// Represents a table of 256 consecutive codepoints rendered into the same texture. internal class OpenGlFontTable { // --- members --- /// The characters stored in this table. internal OpenGlFontChar[] Characters; /// The texture that stores the characters. internal Textures.Texture Texture; // --- constructors --- /// Creates a new table of characters. /// The font. /// The offset from codepoint U+0000. internal OpenGlFontTable(Font font, int offset) { /* * Measure characters. * */ Size[] physicalSizes = new Size[256]; Size[] typographicSizes = new Size[256]; Bitmap bitmap = new Bitmap(1, 1, PixelFormat.Format32bppArgb); Graphics graphics = Graphics.FromImage(bitmap); graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; for (int i = 0; i < 256; i++) { SizeF physicalSize = graphics.MeasureString(char.ConvertFromUtf32(offset + i), font, int.MaxValue, StringFormat.GenericDefault); SizeF typographicSize = graphics.MeasureString(char.ConvertFromUtf32(offset + i), font, int.MaxValue, StringFormat.GenericTypographic); physicalSizes[i] = new Size((int)Math.Ceiling(physicalSize.Width), (int)Math.Ceiling(physicalSize.Height)); typographicSizes[i] = new Size((int)Math.Ceiling(typographicSize.Width == 0.0f ? physicalSize.Width : typographicSize.Width), (int)Math.Ceiling(typographicSize.Height == 0.0f ? physicalSize.Height : typographicSize.Height)); } /* * Find suitable bitmap dimensions. * */ const int width = 256; const int border = 1; int x = border; int y = border; int lineHeight = 0; for (int i = 0; i < 256; i++) { if (x + physicalSizes[i].Width + border > width) { x = border; y += lineHeight; lineHeight = 0; } else { x += physicalSizes[i].Width + 2 * border; } if (physicalSizes[i].Height + border > lineHeight) { lineHeight = physicalSizes[i].Height + 2 * border; } } y += lineHeight; int height = (int)RoundToPowerOfTwo((uint)y); graphics.Dispose(); bitmap.Dispose(); /* * Draw character to bitmap. * */ bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); graphics = Graphics.FromImage(bitmap); graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; graphics.Clear(Color.Black); x = border; y = border; lineHeight = 0; this.Characters = new OpenGlFontChar[256]; for (int i = 0; i < 256; i++) { if (x + physicalSizes[i].Width + border > width) { x = border; y += lineHeight; lineHeight = 0; } graphics.DrawString(char.ConvertFromUtf32(offset + i), font, Brushes.White, new PointF(x, y)); float x0 = (float)(x - border) / (float)width; float x1 = (float)(x + physicalSizes[i].Width + border) / (float)width; float y0 = (float)(y - border) / (float)height; float y1 = (float)(y + physicalSizes[i].Height + border) / (float)height; this.Characters[i] = new OpenGlFontChar(new RectangleF(x0, y0, x1 - x0, y1 - y0), new Size(physicalSizes[i].Width + 2 * border, physicalSizes[i].Height + 2 * border), typographicSizes[i]); x += physicalSizes[i].Width + 2 * border; if (physicalSizes[i].Height + border > lineHeight) { lineHeight = physicalSizes[i].Height + 2 * border; } } graphics.Dispose(); this.Texture = Textures.RegisterTexture(bitmap); } } /// Represents a font. internal class OpenGlFont { // --- members --- /// The underlying font. private Font Font; /// The size of the underlying font in pixels. internal float FontSize; /// The 4352 tables containing 256 character each to make up 1114112 codepoints. private OpenGlFontTable[] Tables; // --- constructors --- /// Creates a new font. /// The font family. /// The size in pixels. internal OpenGlFont(FontFamily family, float size) { this.Font = new Font(family, size, FontStyle.Regular, GraphicsUnit.Pixel); this.FontSize = size; this.Tables = new OpenGlFontTable[4352]; } // --- functions --- /// Gets data associated with the specified codepoint. /// The string containing the codepoint. /// The offset at which to read the codepoint. For surrogate pairs, two characters are read, and one otherwise. /// Receives the texture that contains the codepoint. /// Receives the data that describes the codepoint. /// The number of characters read. internal int GetCharacterData(string text, int offset, out Textures.Texture texture, out OpenGlFontChar data) { int value = char.ConvertToUtf32(text, offset); int hi = value >> 8; int lo = value & 0xFF; if (this.Tables[hi] == null) { this.Tables[hi] = new OpenGlFontTable(this.Font, hi << 8); } texture = this.Tables[hi].Texture; data = this.Tables[hi].Characters[lo]; return value >= 0x10000 ? 2 : 1; } } // --- read-only fields --- /// Represents a very small sans serif font. internal static readonly OpenGlFont VerySmallFont = new OpenGlFont(FontFamily.GenericSansSerif, 9.0f); /// Represents a small sans serif font. internal static readonly OpenGlFont SmallFont = new OpenGlFont(FontFamily.GenericSansSerif, 12.0f); /// Represents a normal-sized sans serif font. internal static readonly OpenGlFont NormalFont = new OpenGlFont(FontFamily.GenericSansSerif, 16.0f); /// Represents a large sans serif font. internal static readonly OpenGlFont LargeFont = new OpenGlFont(FontFamily.GenericSansSerif, 21.0f); /// Represents a very large sans serif font. internal static readonly OpenGlFont VeryLargeFont = new OpenGlFont(FontFamily.GenericSansSerif, 27.0f); // --- functions --- /// Rounds the specified value to the next-highest power of two. /// The value. /// The value rounded to the next-highest power of two. private static uint RoundToPowerOfTwo(uint value) { if (value == 0) { throw new ArgumentException(); } else { value -= 1; for (int i = 1; i < sizeof(int) << 3; i <<= 1) { value |= value >> i; } return value + 1; } } } }openbve-1.4.0.10/openBVE/OpenBve/Graphics/Renderer.Loading.cs000066400000000000000000000035701171674032100234450ustar00rootroot00000000000000using System; using System.Drawing; using OpenBveApi.Colors; using Tao.OpenGl; namespace OpenBve { internal static partial class Renderer { /* -------------------------------------------------------------- * This file contains the drawing routines for the loading screen * -------------------------------------------------------------- */ internal static void DrawLoadingScreen() { // begin HACK // Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; int size = Math.Min(Screen.Width, Screen.Height); DrawRectangle(null, new Point(0, 0), new Size(Screen.Width, Screen.Height), Color128.Black); if (Textures.LoadTexture(TextureLogo, Textures.OpenGlTextureWrapMode.ClampClamp)) { DrawRectangle(TextureLogo, new Point((Screen.Width - size) / 2, (Screen.Height - size) / 2), new Size(size, size), Color128.White); } DrawRectangle(null, new Point((Screen.Width - size) / 2, Screen.Height - (int)Fonts.NormalFont.FontSize - 10), new Size(Screen.Width, (int)Fonts.NormalFont.FontSize + 10), new Color128(0.0f, 0.0f, 0.0f, 0.5f)); // double routeProgress = Math.Max(0.0, Math.Min(1.0, Loading.RouteProgress)); // double trainProgress = Math.Max(0.0, Math.Min(1.0, Loading.TrainProgress)); string text; // if (routeProgress < 1.0) { // text = "Loading route... " + (100.0 * routeProgress).ToString("0") + "%"; // } else if (trainProgress < 1.0) { // text = "Loading train... " + (100.0 * trainProgress).ToString("0") + "%"; // } else { // text = "Loading textures and sounds..."; // } text = Interface.GetInterfaceString("message_loading"); DrawString(Fonts.SmallFont, text, new Point((Screen.Width - size) / 2 + 5, Screen.Height - (int)(Fonts.NormalFont.FontSize / 2) - 5), TextAlignment.CenterLeft, Color128.White); // end HACK // } } }openbve-1.4.0.10/openBVE/OpenBve/Graphics/Renderer.Primitives.cs000066400000000000000000000035401171674032100242200ustar00rootroot00000000000000using System; using System.Drawing; using OpenBveApi.Colors; using Tao.OpenGl; namespace OpenBve { internal static partial class Renderer { /// Draws a rectangle. /// The texture, or a null reference. /// The top-left coordinates in pixels. /// The size in pixels. /// The color, or a null reference. internal static void DrawRectangle(Textures.Texture texture, Point point, Size size, Nullable color) { // TODO: Remove Nullable from color once RenderOverlayTexture and RenderOverlaySolid are fully replaced. if (texture == null || !Textures.LoadTexture(texture, Textures.OpenGlTextureWrapMode.ClampClamp)) { Gl.glDisable(Gl.GL_TEXTURE_2D); if (color.HasValue) { Gl.glColor4d(color.Value.R, color.Value.G, color.Value.B, color.Value.A); } Gl.glBegin(Gl.GL_QUADS); Gl.glVertex2d(point.X, point.Y); Gl.glVertex2d(point.X + size.Width, point.Y); Gl.glVertex2d(point.X + size.Width, point.Y + size.Height); Gl.glVertex2d(point.X, point.Y + size.Height); Gl.glEnd(); } else { Gl.glEnable(Gl.GL_TEXTURE_2D); Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture.OpenGlTextures[(int)Textures.OpenGlTextureWrapMode.ClampClamp].Name); if (color.HasValue) { Gl.glColor4d(color.Value.R, color.Value.G, color.Value.B, color.Value.A); } Gl.glBegin(Gl.GL_QUADS); Gl.glTexCoord2f(0.0f, 0.0f); Gl.glVertex2d(point.X, point.Y); Gl.glTexCoord2f(1.0f, 0.0f); Gl.glVertex2d(point.X + size.Width, point.Y); Gl.glTexCoord2f(1.0f, 1.0f); Gl.glVertex2d(point.X + size.Width, point.Y + size.Height); Gl.glTexCoord2f(0.0f, 1.0f); Gl.glVertex2d(point.X, point.Y + size.Height); Gl.glEnd(); } } } }openbve-1.4.0.10/openBVE/OpenBve/Graphics/Renderer.Strings.cs000066400000000000000000000201471171674032100235200ustar00rootroot00000000000000using System; using System.Drawing; using OpenBveApi.Colors; using Tao.OpenGl; namespace OpenBve { internal static partial class Renderer { // --- structures --- /// Represents the alignment of a text compared to a reference coordinate. private enum TextAlignment { /// The reference coordinate represents the top-left corner. TopLeft = 1, /// The reference coordinate represents the top-middle corner. TopMiddle = 2, /// The reference coordinate represents the top-right corner. TopRight = 4, /// The reference coordinate represents the center-left corner. CenterLeft = 8, /// The reference coordinate represents the center-middle corner. CenterMiddle = 16, /// The reference coordinate represents the center-right corner. CenterRight = 32, /// The reference coordinate represents the bottom-left corner. BottomLeft = 64, /// The reference coordinate represents the bottom-middle corner. BottomMiddle = 128, /// The reference coordinate represents the bottom-right corner. BottomRight = 256, /// Represents the left for bitmasking. Left = TopLeft | CenterLeft | BottomLeft, /// Represents the (horizontal) middle for bitmasking. Middle = TopMiddle | CenterMiddle | BottomMiddle, /// Represents the right for bitmasking. Right = TopRight | CenterRight | BottomRight, /// Represents the top for bitmasking. Top = TopLeft | TopMiddle | TopRight, /// Represents the (vertical) center for bitmasking. Center = CenterLeft | CenterMiddle | CenterRight, /// Represents the bottom for bitmasking. Bottom = BottomLeft | BottomMiddle | BottomRight } // --- functions --- /// Measures the size of a string as it would be rendered using the specified font. /// The font to use. /// The string to render. /// The size of the string. private static Size MeasureString(Fonts.OpenGlFont font, string text) { int width = 0; int height = 0; if (text != null) { for (int i = 0; i < text.Length; i++) { Textures.Texture texture; Fonts.OpenGlFontChar data; i += font.GetCharacterData(text, i, out texture, out data) - 1; width += data.TypographicSize.Width; if (data.TypographicSize.Height > height) { height = data.TypographicSize.Height; } } } return new Size(width, height); } /// Renders a string to the screen. /// The font to use. /// The string to render. /// The location. /// The orientation. /// The color. /// This function sets the OpenGL blend function to glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA). private static void DrawString(Fonts.OpenGlFont font, string text, Point location, TextAlignment alignment, Color128 color) { if (text == null) { return; } /* * Prepare the top-left coordinates for rendering, incorporating the * orientation of the string in relation to the specified location. * */ int left; if ((alignment & TextAlignment.Left) == 0) { int width = 0; for (int i = 0; i < text.Length; i++) { Textures.Texture texture; Fonts.OpenGlFontChar data; i += font.GetCharacterData(text, i, out texture, out data) - 1; width += data.TypographicSize.Width; } if ((alignment & TextAlignment.Right) != 0) { left = location.X - width; } else { left = location.X - width / 2; } } else { left = location.X; } int top; if ((alignment & TextAlignment.Top) == 0) { int height = 0; for (int i = 0; i < text.Length; i++) { Textures.Texture texture; Fonts.OpenGlFontChar data; i += font.GetCharacterData(text, i, out texture, out data) - 1; if (data.TypographicSize.Height > height) { height = data.TypographicSize.Height; } } if ((alignment & TextAlignment.Bottom) != 0) { top = location.Y - height; } else { top = location.Y - height / 2; } } else { top = location.Y; } /* * Render the string. * */ Gl.glEnable(Gl.GL_TEXTURE_2D); for (int i = 0; i < text.Length; i++) { Textures.Texture texture; Fonts.OpenGlFontChar data; i += font.GetCharacterData(text, i, out texture, out data) - 1; if (Textures.LoadTexture(texture, Textures.OpenGlTextureWrapMode.ClampClamp)) { Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture.OpenGlTextures[(int)Textures.OpenGlTextureWrapMode.ClampClamp].Name); int x = left - (data.PhysicalSize.Width - data.TypographicSize.Width) / 2; int y = top - (data.PhysicalSize.Height - data.TypographicSize.Height) / 2; /* * In the first pass, mask off the background with pure black. * */ Gl.glBlendFunc(Gl.GL_ZERO, Gl.GL_ONE_MINUS_SRC_COLOR); Gl.glBegin(Gl.GL_POLYGON); Gl.glColor4f(color.A, color.A, color.A, 1.0f); Gl.glTexCoord2f(data.TextureCoordinates.Left, data.TextureCoordinates.Top); Gl.glVertex2f(x, y); Gl.glColor4f(color.A, color.A, color.A, 1.0f); Gl.glTexCoord2f(data.TextureCoordinates.Right, data.TextureCoordinates.Top); Gl.glVertex2f(x + data.PhysicalSize.Width, y); Gl.glColor4f(color.A, color.A, color.A, 1.0f); Gl.glTexCoord2f(data.TextureCoordinates.Right, data.TextureCoordinates.Bottom); Gl.glVertex2f(x + data.PhysicalSize.Width, y + data.PhysicalSize.Height); Gl.glColor4f(color.A, color.A, color.A, 1.0f); Gl.glTexCoord2f(data.TextureCoordinates.Left, data.TextureCoordinates.Bottom); Gl.glVertex2f(x, y + data.PhysicalSize.Height); Gl.glEnd(); /* * In the second pass, add the character onto the background. * */ Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE); Gl.glBegin(Gl.GL_POLYGON); Gl.glColor4f(color.R, color.G, color.B, color.A); Gl.glTexCoord2f(data.TextureCoordinates.Left, data.TextureCoordinates.Top); Gl.glVertex2f(x, y); Gl.glColor4f(color.R, color.G, color.B, color.A); Gl.glTexCoord2f(data.TextureCoordinates.Right, data.TextureCoordinates.Top); Gl.glVertex2f(x + data.PhysicalSize.Width, y); Gl.glColor4f(color.R, color.G, color.B, color.A); Gl.glTexCoord2f(data.TextureCoordinates.Right, data.TextureCoordinates.Bottom); Gl.glVertex2f(x + data.PhysicalSize.Width, y + data.PhysicalSize.Height); Gl.glColor4f(color.R, color.G, color.B, color.A); Gl.glTexCoord2f(data.TextureCoordinates.Left, data.TextureCoordinates.Bottom); Gl.glVertex2f(x, y + data.PhysicalSize.Height); Gl.glEnd(); } left += data.TypographicSize.Width; } Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA); // HACK // } /// Renders a string to the screen. /// The font to use. /// The string to render. /// The location. /// The orientation. /// The color. /// Whether to draw a shadow. /// This function sets the OpenGL blend function to glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA). private static void DrawString(Fonts.OpenGlFont font, string text, Point location, TextAlignment alignment, Color128 color, bool shadow) { if (shadow) { DrawString(font, text, new Point(location.X - 1, location.Y + 1), alignment, new Color128(0.0f, 0.0f, 0.0f, 0.5f * color.A)); DrawString(font, text, location, alignment, color); } else { DrawString(font, text, location, alignment, color); } } } }openbve-1.4.0.10/openBVE/OpenBve/Graphics/Renderer.cs000066400000000000000000000004311171674032100220620ustar00rootroot00000000000000using System; namespace OpenBve { /// Represents the renderer. internal static partial class Renderer { // The bulk of the renderer is contained in the old Renderer.cs file. // The new renderer will be gradually implemented here. } } openbve-1.4.0.10/openBVE/OpenBve/Graphics/Screen.cs000066400000000000000000000131621171674032100215400ustar00rootroot00000000000000using System; using System.Windows.Forms; using Tao.OpenGl; using Tao.Sdl; namespace OpenBve { internal static class Screen { // --- members --- /// Whether the screen is initialized. private static bool Initialized = false; /// The fixed width of the screen. internal static int Width = 0; /// The fixed height of the screen. internal static int Height = 0; /// Whether the screen is set to fullscreen mode. internal static bool Fullscreen = false; // --- functions --- /// Initializes the screen. A call to SDL_Init must have been made before calling this function. A call to Deinitialize must be made when terminating the program. /// Whether initializing the screen was successful. internal static bool Initialize() { if (Sdl.SDL_Init(Sdl.SDL_INIT_VIDEO) != 0) { return false; } else { Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_DOUBLEBUFFER, 1); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_RED_SIZE, 8); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_GREEN_SIZE, 8); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_BLUE_SIZE, 8); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_ALPHA_SIZE, 0); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_DEPTH_SIZE, 24); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_SWAP_CONTROL, Interface.CurrentOptions.VerticalSynchronization ? 1 : 0); Sdl.SDL_ShowCursor(Sdl.SDL_DISABLE); // --- window caption and icon --- Sdl.SDL_WM_SetCaption(Application.ProductName, null); { string bitmapFile = OpenBveApi.Path.CombineFile(Program.FileSystem.DataFolder, "icon.bmp"); IntPtr bitmap = Sdl.SDL_LoadBMP(bitmapFile); if (bitmap != null) { string maskFile = OpenBveApi.Path.CombineFile(Program.FileSystem.DataFolder, "mask.bin"); byte[] mask = System.IO.File.ReadAllBytes(maskFile); Sdl.SDL_WM_SetIcon(bitmap, mask); } } // --- video mode --- Width = Interface.CurrentOptions.FullscreenMode ? Interface.CurrentOptions.FullscreenWidth : Interface.CurrentOptions.WindowWidth; Height = Interface.CurrentOptions.FullscreenMode ? Interface.CurrentOptions.FullscreenHeight : Interface.CurrentOptions.WindowHeight; Fullscreen = Interface.CurrentOptions.FullscreenMode; int bits = Interface.CurrentOptions.FullscreenMode ? Interface.CurrentOptions.FullscreenBits : 32; int flags = Sdl.SDL_OPENGL | Sdl.SDL_HWSURFACE | Sdl.SDL_ANYFORMAT | Sdl.SDL_DOUBLEBUF; if (Fullscreen) { flags |= Sdl.SDL_FULLSCREEN; } IntPtr video = Sdl.SDL_SetVideoMode(Width, Height, bits, flags); if (video == IntPtr.Zero) { // --- not successful --- Sdl.SDL_QuitSubSystem(Sdl.SDL_INIT_VIDEO); return false; } else { // --- set up anisotropic filtering --- Interface.CurrentOptions.AnisotropicFilteringMaximum = 0; string[] extensions = Gl.glGetString(Gl.GL_EXTENSIONS).Split(new char[] { ' ' }); for (int i = 0; i < extensions.Length; i++) { if (extensions[i] == "GL_EXT_texture_filter_anisotropic") { float n; Gl.glGetFloatv(Gl.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, out n); int m = (int)Math.Round(n); Interface.CurrentOptions.AnisotropicFilteringMaximum = Math.Max(0, m); break; } } if (Interface.CurrentOptions.AnisotropicFilteringLevel <= 0) { Interface.CurrentOptions.AnisotropicFilteringLevel = Interface.CurrentOptions.AnisotropicFilteringMaximum; } else { Interface.CurrentOptions.AnisotropicFilteringLevel = Math.Min(Interface.CurrentOptions.AnisotropicFilteringLevel, Interface.CurrentOptions.AnisotropicFilteringMaximum); } // --- done --- Initialized = true; return true; } } } /// Deinitializes the screen. internal static void Deinitialize() { if (Initialized) { Sdl.SDL_QuitSubSystem(Sdl.SDL_INIT_VIDEO); Initialized = false; } } /// Changes to or from fullscreen mode. internal static void ToggleFullscreen() { Fullscreen = !Fullscreen; // begin HACK // Renderer.ClearDisplayLists(); if (World.MouseGrabEnabled) { Sdl.SDL_WM_GrabInput(Sdl.SDL_GRAB_OFF); } Gl.glDisable(Gl.GL_FOG); Renderer.FogEnabled = false; Gl.glDisable(Gl.GL_LIGHTING); Renderer.LightingEnabled = false; Textures.UnloadAllTextures(); if (Fullscreen) { Sdl.SDL_SetVideoMode(Interface.CurrentOptions.FullscreenWidth, Interface.CurrentOptions.FullscreenHeight, Interface.CurrentOptions.FullscreenBits, Sdl.SDL_OPENGL | Sdl.SDL_DOUBLEBUF | Sdl.SDL_FULLSCREEN); Width = Interface.CurrentOptions.FullscreenWidth; Height = Interface.CurrentOptions.FullscreenHeight; } else { Sdl.SDL_SetVideoMode(Interface.CurrentOptions.WindowWidth, Interface.CurrentOptions.WindowHeight, 32, Sdl.SDL_OPENGL | Sdl.SDL_DOUBLEBUF); Width = Interface.CurrentOptions.WindowWidth; Height = Interface.CurrentOptions.WindowHeight; } Renderer.InitializeLighting(); MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); MainLoop.InitializeMotionBlur(); Timetable.CreateTimetable(); Timetable.UpdateCustomTimetable(null, null); if (World.MouseGrabEnabled) { Sdl.SDL_WM_GrabInput(Sdl.SDL_GRAB_ON); } World.MouseGrabTarget = new World.Vector2D(0.0, 0.0); World.MouseGrabIgnoreOnce = true; World.InitializeCameraRestriction(); if (Renderer.OptionBackfaceCulling) { Gl.glEnable(Gl.GL_CULL_FACE); } else { Gl.glDisable(Gl.GL_CULL_FACE); } Renderer.ReAddObjects(); // end HACK // } } }openbve-1.4.0.10/openBVE/OpenBve/Graphics/Textures.Texture.cs000066400000000000000000000116051171674032100236030ustar00rootroot00000000000000#pragma warning disable 0659, 0661 using System; using System.Drawing; namespace OpenBve { internal static partial class Textures { /// Represents how the texture wraps on each axis. internal enum OpenGlTextureWrapMode { /// The texture is clamped to edge on both axes. /// The numerical value is 0. ClampClamp = 0, /// The texture is clamped to edge on the x-axis and repeats on the y-axis. /// The numerical value is 1. ClampRepeat = 1, /// The texture repeats on the x-axis and is clamped to edge on the y-axis. /// The numerical value is 2. RepeatClamp = 2, /// The texture repeats on both axes. /// The numerical value is 3. RepeatRepeat = 3 } /// Represents an OpenGL texture. internal class OpenGlTexture { /// Whether the texture has been loaded and the OpenGL texture name is valid. internal bool Valid; /// The OpenGL texture name. internal int Name; } /// Represents a texture. internal class Texture : OpenBveApi.Textures.TextureHandle { // --- members --- /// The origin where the texture can be loaded from. internal TextureOrigin Origin; /// The OpenGL textures for the four clamp modes, where 0=repeat/repeat, 1=repeat/clamp, 2=clamp/repeat and 3=clamp/clamp. internal OpenGlTexture[] OpenGlTextures; /// The width of the texture. Only valid if the texture is loaded. internal int Width; /// The height of the texture. Only valid if the texture is loaded. internal int Height; /// The type of transparency encountered in the texture. Only valid if the texture is loaded. internal OpenBveApi.Textures.TextureTransparencyType Transparency; /// Whether to ignore further attemps to load the texture after previous attempts have failed. internal bool Ignore; // --- constructors --- /// Creates a new texture. /// The path to the texture. /// The parameters that specify how to process the texture. internal Texture(string path, OpenBveApi.Textures.TextureParameters parameters) { this.Origin = new PathOrigin(path, parameters); this.OpenGlTextures = new OpenGlTexture[] { new OpenGlTexture(), new OpenGlTexture(), new OpenGlTexture(), new OpenGlTexture() }; } /// Creates a new texture. /// The System.Drawing.Bitmap that contains the texture. internal Texture(Bitmap bitmap) { this.Origin = new BitmapOrigin(bitmap); this.OpenGlTextures = new OpenGlTexture[] { new OpenGlTexture(), new OpenGlTexture(), new OpenGlTexture(), new OpenGlTexture() }; } /// Creates a new texture. /// The texture raw data. internal Texture(OpenBveApi.Textures.Texture texture) { this.Origin = new RawOrigin(texture); this.OpenGlTextures = new OpenGlTexture[] { new OpenGlTexture(), new OpenGlTexture(), new OpenGlTexture(), new OpenGlTexture() }; } // --- operators --- /// Checks whether two textures are equal. /// The first texture. /// The second texture. /// Whether the two textures are equal. public static bool operator ==(Texture a, Texture b) { if (object.ReferenceEquals(a, b)) return true; if (object.ReferenceEquals(a, null)) return false; if (object.ReferenceEquals(b, null)) return false; return a.Origin == b.Origin; } /// Checks whether two textures are unequal. /// The first texture. /// The second texture. /// Whether the two textures are unequal. public static bool operator !=(Texture a, Texture b) { if (object.ReferenceEquals(a, b)) return false; if (object.ReferenceEquals(a, null)) return true; if (object.ReferenceEquals(b, null)) return true; return a.Origin != b.Origin; } /// Checks whether this instance is equal to the specified object. /// The object. /// Whether this instance is equal to the specified object. public override bool Equals(object obj) { if (object.ReferenceEquals(this, obj)) return true; if (object.ReferenceEquals(this, null)) return false; if (object.ReferenceEquals(obj, null)) return false; if (!(obj is Texture)) return false; return this.Origin == ((Texture)obj).Origin; } } } }openbve-1.4.0.10/openBVE/OpenBve/Graphics/Textures.TextureOrigin.cs000066400000000000000000000202661171674032100247560ustar00rootroot00000000000000#pragma warning disable 0659, 0661 using System; using System.Drawing; using System.Drawing.Imaging; namespace OpenBve { internal static partial class Textures { /* * A texture origin defines where to load a texture from. This can be * a path (file or directory), a System.Drawing.Bitmap, or raw data. * */ // --- texture origin --- /// Represents the origin where the texture can be loaded from. internal abstract class TextureOrigin { // --- functions --- /// Gets the texture from this origin. /// Receives the texture. /// Whether the texture could be obtained successfully. internal abstract bool GetTexture(out OpenBveApi.Textures.Texture texture); // --- operators --- /// Checks whether two origins are equal. /// The first origin. /// The second origin. /// Whether the two origins are equal. public static bool operator ==(TextureOrigin a, TextureOrigin b) { if (a is PathOrigin & b is PathOrigin) { return (PathOrigin)a == (PathOrigin)b; } else { return object.ReferenceEquals(a, b); } } /// Checks whether two origins are unequal. /// The first origin. /// The second origin. /// Whether the two origins are unequal. public static bool operator !=(TextureOrigin a, TextureOrigin b) { if (a is PathOrigin & b is PathOrigin) { return (PathOrigin)a != (PathOrigin)b; } else { return !object.ReferenceEquals(a, b); } } /// Checks whether this instance is equal to the specified object. /// The object. /// Whether this instance is equal to the specified object. public override bool Equals(object obj) { if (this is PathOrigin & obj is PathOrigin) { return (PathOrigin)this == (PathOrigin)obj; } else { return object.ReferenceEquals(this, obj); } } } // --- path origin --- /// Represents a file or directory where the texture can be loaded from. internal class PathOrigin : TextureOrigin { // --- members --- internal string Path; internal OpenBveApi.Textures.TextureParameters Parameters; // --- constructors --- /// Creates a new path origin. /// The path to the texture. /// The parameters that specify how to process the texture. internal PathOrigin(string path, OpenBveApi.Textures.TextureParameters parameters) { this.Path = path; this.Parameters = parameters; } // --- functions --- /// Gets the texture from this origin. /// Receives the texture. /// Whether the texture could be obtained successfully. internal override bool GetTexture(out OpenBveApi.Textures.Texture texture) { if (!Program.CurrentHost.LoadTexture(this.Path, this.Parameters, out texture)) { texture = null; return false; } else { return true; } } // --- operators --- /// Checks whether two origins are equal. /// The first origin. /// The second origin. /// Whether the two origins are equal. public static bool operator ==(PathOrigin a, PathOrigin b) { if (object.ReferenceEquals(a, b)) return true; if (object.ReferenceEquals(a, null)) return false; if (object.ReferenceEquals(b, null)) return false; return a.Path == b.Path; } /// Checks whether two origins are unequal. /// The first origin. /// The second origin. /// Whether the two origins are unequal. public static bool operator !=(PathOrigin a, PathOrigin b) { if (object.ReferenceEquals(a, b)) return false; if (object.ReferenceEquals(a, null)) return true; if (object.ReferenceEquals(b, null)) return true; return a.Path != b.Path; } /// Checks whether this instance is equal to the specified object. /// The object. /// Whether this instance is equal to the specified object. public override bool Equals(object obj) { if (object.ReferenceEquals(this, obj)) return true; if (object.ReferenceEquals(this, null)) return false; if (object.ReferenceEquals(obj, null)) return false; if (!(obj is PathOrigin)) return false; return this.Path == ((PathOrigin)obj).Path; } } // --- bitmap origin --- /// Represents a System.Drawing.Bitmap where the texture can be loaded from. internal class BitmapOrigin : TextureOrigin { // --- members --- /// The bitmap. internal Bitmap Bitmap; // --- constructors --- /// Creates a new bitmap origin. /// The bitmap. internal BitmapOrigin(Bitmap bitmap) { this.Bitmap = bitmap; } // --- functions --- /// Gets the texture from this origin. /// Receives the texture. /// Whether the texture could be obtained successfully. internal override bool GetTexture(out OpenBveApi.Textures.Texture texture) { Bitmap bitmap = this.Bitmap; Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); /* * If the bitmap format is not already 32-bit BGRA, * then convert it to 32-bit BGRA. * */ if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) { Bitmap compatibleBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb); Graphics graphics = Graphics.FromImage(compatibleBitmap); graphics.DrawImage(bitmap, rect, rect, GraphicsUnit.Pixel); graphics.Dispose(); bitmap = compatibleBitmap; } /* * Extract the raw bitmap data. * */ BitmapData data = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat); if (data.Stride == 4 * data.Width) { /* * Copy the data from the bitmap * to the array in BGRA format. * */ byte[] raw = new byte[data.Stride * data.Height]; System.Runtime.InteropServices.Marshal.Copy(data.Scan0, raw, 0, data.Stride * data.Height); bitmap.UnlockBits(data); int width = bitmap.Width; int height = bitmap.Height; /* * Change the byte order from BGRA to RGBA. * */ for (int i = 0; i < raw.Length; i += 4) { byte temp = raw[i]; raw[i] = raw[i + 2]; raw[i + 2] = temp; } texture = new OpenBveApi.Textures.Texture(width, height, 32, raw); return true; } else { /* * The stride is invalid. This indicates that the * CLI either does not implement the conversion to * 32-bit BGRA correctly, or that the CLI has * applied additional padding that we do not * support. * */ bitmap.UnlockBits(data); texture = null; return false; } } } // --- raw origin --- /// Represents texture raw data. internal class RawOrigin : TextureOrigin { // --- members --- /// The texture raw data. internal OpenBveApi.Textures.Texture Texture; // --- constructors --- /// Creates a new raw data origin. /// The texture raw data. internal RawOrigin(OpenBveApi.Textures.Texture texture) { this.Texture = texture; } // --- functions --- /// Gets the texture from this origin. /// Receives the texture. /// Whether the texture could be obtained successfully. internal override bool GetTexture(out OpenBveApi.Textures.Texture texture) { texture = this.Texture; return true; } } } }openbve-1.4.0.10/openBVE/OpenBve/Graphics/Textures.cs000066400000000000000000000401451171674032100221450ustar00rootroot00000000000000using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Runtime.InteropServices; using Tao.OpenGl; namespace OpenBve { /// Provides functions for dealing with textures. internal static partial class Textures { // --- members --- /// Holds all currently registered textures. private static Texture[] RegisteredTextures = new Texture[16]; /// The number of currently registered textures. private static int RegisteredTexturesCount = 0; // --- initialize / deinitialize --- /// Initializes the texture component. A call to Deinitialize must be made when terminating the program. internal static void Initialize() { } /// Deinitializes the texture component. internal static void Deinitialize() { UnloadAllTextures(); } // --- register texture --- /// Registeres a texture and returns a handle to the texture. /// The path to the file or directory that contains the texture. /// Receives a handle to the texture. /// Whether registering the texture was successful. internal static bool RegisterTexture(string path, out Texture handle) { return RegisterTexture(path, null, out handle); } /// Registeres a texture and returns a handle to the texture. /// The path to the texture. /// The parameters that specify how to process the texture. /// Receives a handle to the texture. /// Whether registering the texture was successful. internal static bool RegisterTexture(string path, OpenBveApi.Textures.TextureParameters parameters, out Texture handle) { /* * Check if the texture is already registered. * If so, return the existing handle. * */ for (int i = 0; i < RegisteredTexturesCount; i++) { PathOrigin source = RegisteredTextures[i].Origin as PathOrigin; if (source != null && source.Path == path && source.Parameters == parameters) { handle = RegisteredTextures[i]; return true; } } /* * Register the texture and return the newly created handle. * */ if (RegisteredTextures.Length == RegisteredTexturesCount) { Array.Resize(ref RegisteredTextures, RegisteredTextures.Length << 1); } RegisteredTextures[RegisteredTexturesCount] = new Texture(path, parameters); RegisteredTexturesCount++; handle = RegisteredTextures[RegisteredTexturesCount - 1]; return true; } /// Registeres a texture and returns a handle to the texture. /// The texture data. /// The handle to the texture. internal static Texture RegisterTexture(OpenBveApi.Textures.Texture texture) { /* * Register the texture and return the newly created handle. * */ if (RegisteredTextures.Length == RegisteredTexturesCount) { Array.Resize(ref RegisteredTextures, RegisteredTextures.Length << 1); } RegisteredTextures[RegisteredTexturesCount] = new Texture(texture); RegisteredTexturesCount++; return RegisteredTextures[RegisteredTexturesCount - 1]; } /// Registeres a texture and returns a handle to the texture. /// The bitmap that contains the texture. /// The handle to the texture. /// Be sure not to dispose of the bitmap after calling this function. internal static Texture RegisterTexture(Bitmap bitmap) { /* * Register the texture and return the newly created handle. * */ if (RegisteredTextures.Length == RegisteredTexturesCount) { Array.Resize(ref RegisteredTextures, RegisteredTextures.Length << 1); } RegisteredTextures[RegisteredTexturesCount] = new Texture(bitmap); RegisteredTexturesCount++; return RegisteredTextures[RegisteredTexturesCount - 1]; } // --- load texture --- /// Loads the specified texture into OpenGL if not already loaded. /// The handle to the registered texture. /// The texture type indicating the clamp mode. /// Whether loading the texture was successful. internal static bool LoadTexture(Texture handle, OpenGlTextureWrapMode wrap) { if (handle.OpenGlTextures[(int)wrap].Valid) { return true; } else if (handle.Ignore) { return false; } else { OpenBveApi.Textures.Texture texture; if (handle.Origin.GetTexture(out texture)) { if (texture.BitsPerPixel == 32) { int[] names = new int[1]; Gl.glGenTextures(1, names); int error = Gl.glGetError(); Gl.glBindTexture(Gl.GL_TEXTURE_2D, names[0]); error = Gl.glGetError(); handle.OpenGlTextures[(int)wrap].Name = names[0]; handle.Width = texture.Width; handle.Height = texture.Height; handle.Transparency = texture.GetTransparencyType(); texture = UpsizeToPowerOfTwo(texture); switch (Interface.CurrentOptions.Interpolation) { case Interface.InterpolationMode.NearestNeighbor: Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST); Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST); break; case Interface.InterpolationMode.Bilinear: Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR); Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR); break; case Interface.InterpolationMode.NearestNeighborMipmapped: Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST_MIPMAP_NEAREST); Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST); break; case Interface.InterpolationMode.BilinearMipmapped: Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST_MIPMAP_LINEAR); Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR); break; case Interface.InterpolationMode.TrilinearMipmapped: Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR_MIPMAP_LINEAR); Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR); break; default: Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR_MIPMAP_LINEAR); Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR); break; } if ((wrap & OpenGlTextureWrapMode.RepeatClamp) != 0) { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT); } else { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP_TO_EDGE); } if ((wrap & OpenGlTextureWrapMode.ClampRepeat) != 0) { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT); } else { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP_TO_EDGE); } if (Interface.CurrentOptions.Interpolation == Interface.InterpolationMode.NearestNeighbor & Interface.CurrentOptions.Interpolation == Interface.InterpolationMode.Bilinear) { Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_GENERATE_MIPMAP, Gl.GL_FALSE); } else { Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_GENERATE_MIPMAP, Gl.GL_TRUE); } if (Interface.CurrentOptions.Interpolation == Interface.InterpolationMode.AnisotropicFiltering) { Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAX_ANISOTROPY_EXT, Interface.CurrentOptions.AnisotropicFilteringLevel); } if (handle.Transparency == OpenBveApi.Textures.TextureTransparencyType.Opaque) { /* * If the texture is fully opaque, the alpha channel is not used. * If the graphics driver and card support 24-bits per channel, * it is best to convert the bitmap data to that format in order * to save memory on the card. If the card does not support the * format, it will likely be upconverted to 32-bits per channel * again, and this is wasted effort. * */ int width = texture.Width; int height = texture.Height; int stride = (3 * (width + 1) >> 2) << 2; byte[] oldBytes = texture.Bytes; byte[] newBytes = new byte[stride * texture.Height]; int i = 0, j = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { newBytes[j + 0] = oldBytes[i + 0]; newBytes[j + 1] = oldBytes[i + 1]; newBytes[j + 2] = oldBytes[i + 2]; i += 4; j += 3; } j += stride - 3 * width; } Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB8, texture.Width, texture.Height, 0, Gl.GL_RGB, Gl.GL_UNSIGNED_BYTE, newBytes); } else { /* * The texture uses its alpha channel, so send the bitmap data * in 32-bits per channel as-is. * */ Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA8, texture.Width, texture.Height, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, texture.Bytes); } handle.OpenGlTextures[(int)wrap].Valid = true; return true; } } } handle.Ignore = true; return false; } /// Loads all registered textures. internal static void LoadAllTextures() { // for (int i = 0; i < RegisteredTexturesCount; i++) { // LoadTexture(RegisteredTextures[i]); // } } // --- save texture --- /// Saves a texture to a file. /// The file. /// The texture. /// The texture is always saved in PNG format. internal static void SaveTexture(string file, OpenBveApi.Textures.Texture texture) { Bitmap bitmap = new Bitmap(texture.Width, texture.Height, PixelFormat.Format32bppArgb); BitmapData data = bitmap.LockBits(new Rectangle(0, 0, texture.Width, texture.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); byte[] bytes = new byte[texture.Bytes.Length]; for (int i = 0; i < bytes.Length; i += 4) { bytes[i] = texture.Bytes[i + 2]; bytes[i + 1] = texture.Bytes[i + 1]; bytes[i + 2] = texture.Bytes[i]; bytes[i + 3] = texture.Bytes[i + 3]; } Marshal.Copy(bytes, 0, data.Scan0, texture.Bytes.Length); bitmap.UnlockBits(data); bitmap.Save(file, ImageFormat.Png); bitmap.Dispose(); } // --- upsize texture --- /// Upsizes the specified texture to a power of two size and returns the result. /// The texture. /// The upsized texture, or the original if already a power of two size. /// The bits per pixel in the texture is not supported. internal static OpenBveApi.Textures.Texture UpsizeToPowerOfTwo(OpenBveApi.Textures.Texture texture) { int width = RoundUpToPowerOfTwo(texture.Width); int height = RoundUpToPowerOfTwo(texture.Height); return Resize(texture, width, height); } /// Resizes the specified texture to the specified width and height and returns the result. /// The texture. /// The new width. /// The new height. /// The resize texture, or the original if already of the specified size. /// The bits per pixel in the texture is not supported. internal static OpenBveApi.Textures.Texture Resize(OpenBveApi.Textures.Texture texture, int width, int height) { if (width == texture.Width & height == texture.Height) { return texture; } else if (texture.BitsPerPixel != 32) { throw new NotSupportedException("The number of bits per pixel is not supported."); } else { OpenBveApi.Textures.TextureTransparencyType type = texture.GetTransparencyType(); /* * Convert the texture into a bitmap. * */ Bitmap bitmap = new Bitmap(texture.Width, texture.Height, PixelFormat.Format32bppArgb); BitmapData data = bitmap.LockBits(new Rectangle(0, 0, texture.Width, texture.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); Marshal.Copy(texture.Bytes, 0, data.Scan0, texture.Bytes.Length); bitmap.UnlockBits(data); /* * Scale the bitmap. * */ Bitmap scaledBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); Graphics graphics = Graphics.FromImage(scaledBitmap); graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.DrawImage(bitmap, new Rectangle(0, 0, width, height), new Rectangle(0, 0, texture.Width, texture.Height), GraphicsUnit.Pixel); graphics.Dispose(); bitmap.Dispose(); /* * Convert the bitmap into a texture. * */ data = scaledBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, scaledBitmap.PixelFormat); byte[] bytes = new byte[4 * width * height]; Marshal.Copy(data.Scan0, bytes, 0, bytes.Length); scaledBitmap.UnlockBits(data); scaledBitmap.Dispose(); /* * Ensure opaque and partially transparent * textures have valid alpha components. * */ if (type == OpenBveApi.Textures.TextureTransparencyType.Opaque) { for (int i = 3; i < bytes.Length; i += 4) { bytes[i] = 255; } } else if (type == OpenBveApi.Textures.TextureTransparencyType.Partial) { for (int i = 3; i < bytes.Length; i += 4) { if (bytes[i] < 128) { bytes[i] = 0; } else { bytes[i] = 255; } } } OpenBveApi.Textures.Texture result = new OpenBveApi.Textures.Texture(width, height, 32, bytes); return result; } } // --- unload texture --- /// Unloads the specified texture from OpenGL if loaded. /// The handle to the registered texture. internal static void UnloadTexture(Texture handle) { for (int i = 0; i < handle.OpenGlTextures.Length; i++) { if (handle.OpenGlTextures[i].Valid) { Gl.glDeleteTextures(1, new int[] { handle.OpenGlTextures[i].Name }); handle.OpenGlTextures[i].Valid = false; } } handle.Ignore = false; } /// Unloads all registered textures. internal static void UnloadAllTextures() { for (int i = 0; i < RegisteredTexturesCount; i++) { UnloadTexture(RegisteredTextures[i]); } } // --- statistics --- /// Gets the number of registered textures. /// The number of registered textures. internal static int GetNumberOfRegisteredTextures() { return RegisteredTexturesCount; } /// Gets the number of loaded textures. /// The number of loaded textures. internal static int GetNumberOfLoadedTextures() { int count = 0; for (int i = 0; i < RegisteredTexturesCount; i++) { for (int j = 0; j < RegisteredTextures[i].OpenGlTextures.Length; j++) { if (RegisteredTextures[i].OpenGlTextures[j].Valid) { count++; break; } } } return count; } // --- functions --- /// Takes a positive value and rounds it up to the next highest power of two. /// The value. /// The next highest power of two, or the original value if already a power of two. internal static int RoundUpToPowerOfTwo(int value) { if (value <= 0) { throw new ArgumentException("The specified value is not positive."); } else { value -= 1; for (int i = 1; i < sizeof(int) * 8; i <<= 1) { value = value | value >> i; } return value + 1; } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/000077500000000000000000000000001171674032100175405ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBve/OldCode/FunctionScripts.cs000066400000000000000000003067631171674032100232430ustar00rootroot00000000000000using System; using OpenBveApi.Math; namespace OpenBve { internal static class FunctionScripts { // instruction set internal enum Instructions : int { SystemHalt, SystemConstant, SystemConstantArray, SystemValue, SystemDelta, StackCopy, StackSwap, MathPlus, MathSubtract, MathMinus, MathTimes, MathDivide, MathReciprocal, MathPower, MathIncrement, MathDecrement, MathFusedMultiplyAdd, MathQuotient, MathMod, MathFloor, MathCeiling, MathRound, MathMin, MathMax, MathAbs, MathSign, MathExp, MathLog, MathSqrt, MathSin, MathCos, MathTan, MathArcTan, CompareEqual, CompareUnequal, CompareLess, CompareGreater, CompareLessEqual, CompareGreaterEqual, CompareConditional, LogicalNot, LogicalAnd, LogicalOr, LogicalNand, LogicalNor, LogicalXor, TimeSecondsSinceMidnight, CameraDistance, TrainCars, TrainSpeed, TrainSpeedometer, TrainAcceleration, TrainAccelerationMotor, TrainSpeedOfCar, TrainSpeedometerOfCar, TrainAccelerationOfCar, TrainAccelerationMotorOfCar, TrainDistance, TrainDistanceToCar, TrainTrackDistance, TrainTrackDistanceToCar, Doors, DoorsIndex, LeftDoors, LeftDoorsIndex, RightDoors, RightDoorsIndex, LeftDoorsTarget, LeftDoorsTargetIndex, RightDoorsTarget, RightDoorsTargetIndex, ReverserNotch, PowerNotch, PowerNotches, BrakeNotch, BrakeNotches, BrakeNotchLinear, BrakeNotchesLinear, EmergencyBrake, HasAirBrake, HoldBrake, HasHoldBrake, ConstSpeed, HasConstSpeed, BrakeMainReservoir, BrakeEqualizingReservoir, BrakeBrakePipe, BrakeBrakeCylinder, BrakeStraightAirPipe, BrakeMainReservoirOfCar, BrakeEqualizingReservoirOfCar, BrakeBrakePipeOfCar, BrakeBrakeCylinderOfCar, BrakeStraightAirPipeOfCar, SafetyPluginAvailable, SafetyPluginState, TimetableVisible, SectionAspectNumber } // function script internal class FunctionScript { internal Instructions[] Instructions; internal double[] Stack; internal double[] Constants; internal double LastResult; internal double Perform(TrainManager.Train Train, int CarIndex, Vector3 Position, double TrackPosition, int SectionIndex, bool IsPartOfTrain, double TimeElapsed) { ExecuteFunctionScript(this, Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); return this.LastResult; } internal FunctionScript Clone() { return (FunctionScript)this.MemberwiseClone(); } } // execute function script private static void ExecuteFunctionScript(FunctionScript Function, TrainManager.Train Train, int CarIndex, Vector3 Position, double TrackPosition, int SectionIndex, bool IsPartOfTrain, double TimeElapsed) { int s = 0, c = 0; for (int i = 0; i < Function.Instructions.Length; i++) { switch (Function.Instructions[i]) { // system case Instructions.SystemHalt: i = Function.Instructions.Length; break; case Instructions.SystemConstant: Function.Stack[s] = Function.Constants[c]; s++; c++; break; case Instructions.SystemConstantArray: { int n = (int)Function.Instructions[i + 1]; for (int j = 0; j < n; j++) { Function.Stack[s + j] = Function.Constants[c + j]; } s += n; c += n; i++; } break; case Instructions.SystemValue: Function.Stack[s] = Function.LastResult; s++; break; case Instructions.SystemDelta: Function.Stack[s] = TimeElapsed; s++; break; // stack case Instructions.StackCopy: Function.Stack[s] = Function.Stack[s - 1]; s++; break; case Instructions.StackSwap: { double a = Function.Stack[s - 1]; Function.Stack[s - 1] = Function.Stack[s - 2]; Function.Stack[s - 2] = a; } break; // math case Instructions.MathPlus: Function.Stack[s - 2] += Function.Stack[s - 1]; s--; break; case Instructions.MathSubtract: Function.Stack[s - 2] -= Function.Stack[s - 1]; s--; break; case Instructions.MathMinus: Function.Stack[s - 1] = -Function.Stack[s - 1]; break; case Instructions.MathTimes: Function.Stack[s - 2] *= Function.Stack[s - 1]; s--; break; case Instructions.MathDivide: Function.Stack[s - 2] = Function.Stack[s - 1] == 0.0 ? 0.0 : Function.Stack[s - 2] / Function.Stack[s - 1]; s--; break; case Instructions.MathReciprocal: Function.Stack[s - 1] = Function.Stack[s - 1] == 0.0 ? 0.0 : 1.0 / Function.Stack[s - 1]; break; case Instructions.MathPower: { double a = Function.Stack[s - 2]; double b = Function.Stack[s - 1]; if (b == 2.0) { Function.Stack[s - 2] = a * a; } else if (b == 3.0) { Function.Stack[s - 2] = a * a * a; } else if (b == 4.0) { double t = a * a; Function.Stack[s - 2] = t * t; } else if (b == 5.0) { double t = a * a; Function.Stack[s - 2] = t * t * a; } else if (b == 6.0) { double t = a * a * a; Function.Stack[s - 2] = t * t; } else if (b == 7.0) { double t = a * a * a; Function.Stack[s - 2] = t * t * a; } else if (b == 8.0) { double t = a * a; t *= t; Function.Stack[s - 2] = t * t; } else if (b == 0.0) { Function.Stack[s - 2] = 1.0; } else if (b < 0.0) { Function.Stack[s - 2] = 0.0; } else { Function.Stack[s - 2] = Math.Pow(a, b); } s--; break; } case Instructions.MathIncrement: Function.Stack[s - 1] += 1.0; break; case Instructions.MathDecrement: Function.Stack[s - 1] -= 1.0; break; case Instructions.MathFusedMultiplyAdd: Function.Stack[s - 3] = Function.Stack[s - 3] * Function.Stack[s - 2] + Function.Stack[s - 1]; s -= 2; break; case Instructions.MathQuotient: Function.Stack[s - 2] = Function.Stack[s - 1] == 0.0 ? 0.0 : Math.Floor(Function.Stack[s - 2] / Function.Stack[s - 1]); s--; break; case Instructions.MathMod: Function.Stack[s - 2] = Function.Stack[s - 1] == 0.0 ? 0.0 : Function.Stack[s - 2] - Function.Stack[s - 1] * Math.Floor(Function.Stack[s - 2] / Function.Stack[s - 1]); s--; break; case Instructions.MathFloor: Function.Stack[s - 1] = Math.Floor(Function.Stack[s - 1]); break; case Instructions.MathCeiling: Function.Stack[s - 1] = Math.Ceiling(Function.Stack[s - 1]); break; case Instructions.MathRound: Function.Stack[s - 1] = Math.Round(Function.Stack[s - 1]); break; case Instructions.MathMin: Function.Stack[s - 2] = Function.Stack[s - 2] < Function.Stack[s - 1] ? Function.Stack[s - 2] : Function.Stack[s - 1]; s--; break; case Instructions.MathMax: Function.Stack[s - 2] = Function.Stack[s - 2] > Function.Stack[s - 1] ? Function.Stack[s - 2] : Function.Stack[s - 1]; s--; break; case Instructions.MathAbs: Function.Stack[s - 1] = Math.Abs(Function.Stack[s - 1]); break; case Instructions.MathSign: Function.Stack[s - 1] = Math.Sign(Function.Stack[s - 1]); break; case Instructions.MathExp: Function.Stack[s - 1] = Math.Exp(Function.Stack[s - 1]); break; case Instructions.MathLog: Function.Stack[s - 1] = Log(Function.Stack[s - 1]); break; case Instructions.MathSqrt: Function.Stack[s - 1] = Sqrt(Function.Stack[s - 1]); break; case Instructions.MathSin: Function.Stack[s - 1] = Math.Sin(Function.Stack[s - 1]); break; case Instructions.MathCos: Function.Stack[s - 1] = Math.Cos(Function.Stack[s - 1]); break; case Instructions.MathTan: Function.Stack[s - 1] = Tan(Function.Stack[s - 1]); break; case Instructions.MathArcTan: Function.Stack[s - 1] = Math.Atan(Function.Stack[s - 1]); break; // comparisons case Instructions.CompareEqual: Function.Stack[s - 2] = Function.Stack[s - 2] == Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareUnequal: Function.Stack[s - 2] = Function.Stack[s - 2] != Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareLess: Function.Stack[s - 2] = Function.Stack[s - 2] < Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareGreater: Function.Stack[s - 2] = Function.Stack[s - 2] > Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareLessEqual: Function.Stack[s - 2] = Function.Stack[s - 2] <= Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareGreaterEqual: Function.Stack[s - 2] = Function.Stack[s - 2] >= Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareConditional: Function.Stack[s - 3] = Function.Stack[s - 3] != 0.0 ? Function.Stack[s - 2] : Function.Stack[s - 1]; s -= 2; break; // logical case Instructions.LogicalNot: Function.Stack[s - 1] = Function.Stack[s - 1] != 0.0 ? 0.0 : 1.0; break; case Instructions.LogicalAnd: Function.Stack[s - 2] = Function.Stack[s - 2] != 0.0 & Function.Stack[s - 1] != 0.0 ? 1.0 : 0.0; s--; break; case Instructions.LogicalOr: Function.Stack[s - 2] = Function.Stack[s - 2] != 0.0 | Function.Stack[s - 1] != 0.0 ? 1.0 : 0.0; s--; break; case Instructions.LogicalNand: Function.Stack[s - 2] = Function.Stack[s - 2] != 0.0 & Function.Stack[s - 1] != 0.0 ? 0.0 : 1.0; s--; break; case Instructions.LogicalNor: Function.Stack[s - 2] = Function.Stack[s - 2] != 0.0 | Function.Stack[s - 1] != 0.0 ? 0.0 : 1.0; s--; break; case Instructions.LogicalXor: Function.Stack[s - 2] = Function.Stack[s - 2] != 0.0 ^ Function.Stack[s - 1] != 0.0 ? 1.0 : 0.0; s--; break; // time/camera case Instructions.TimeSecondsSinceMidnight: Function.Stack[s] = Game.SecondsSinceMidnight; s++; break; case Instructions.CameraDistance: { double dx = World.AbsoluteCameraPosition.X - Position.X; double dy = World.AbsoluteCameraPosition.Y - Position.Y; double dz = World.AbsoluteCameraPosition.Z - Position.Z; Function.Stack[s] = Math.Sqrt(dx * dx + dy * dy + dz * dz); s++; } break; // train case Instructions.TrainCars: if (Train != null) { Function.Stack[s] = (double)Train.Cars.Length; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainSpeed: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.CurrentSpeed; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainSpeedOfCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.CurrentSpeed; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.TrainSpeedometer: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.CurrentPerceivedSpeed; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainSpeedometerOfCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.CurrentPerceivedSpeed; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.TrainAcceleration: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.CurrentAcceleration; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainAccelerationOfCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.CurrentAcceleration; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.TrainAccelerationMotor: if (Train != null) { Function.Stack[s] = 0.0; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Specs.IsMotorCar) { // hack: CurrentAccelerationOutput does not distinguish between forward/backward if (Train.Cars[j].Specs.CurrentAccelerationOutput < 0.0) { Function.Stack[s] = Train.Cars[j].Specs.CurrentAccelerationOutput * (double)Math.Sign(Train.Cars[j].Specs.CurrentSpeed); } else if (Train.Cars[j].Specs.CurrentAccelerationOutput > 0.0) { Function.Stack[s] = Train.Cars[j].Specs.CurrentAccelerationOutput * (double)Train.Specs.CurrentReverser.Actual; } else { Function.Stack[s] = 0.0; } break; } } } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainAccelerationMotorOfCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { // hack: CurrentAccelerationOutput does not distinguish between forward/backward if (Train.Cars[j].Specs.CurrentAccelerationOutput < 0.0) { Function.Stack[s - 1] = Train.Cars[j].Specs.CurrentAccelerationOutput * (double)Math.Sign(Train.Cars[j].Specs.CurrentSpeed); } else if (Train.Cars[j].Specs.CurrentAccelerationOutput > 0.0) { Function.Stack[s - 1] = Train.Cars[j].Specs.CurrentAccelerationOutput * (double)Train.Specs.CurrentReverser.Actual; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.TrainDistance: if (Train != null) { double dist = double.MaxValue; for (int j = 0; j < Train.Cars.Length; j++) { double fx = Train.Cars[j].FrontAxle.Follower.WorldPosition.X - Position.X; double fy = Train.Cars[j].FrontAxle.Follower.WorldPosition.Y - Position.Y; double fz = Train.Cars[j].FrontAxle.Follower.WorldPosition.Z - Position.Z; double f = fx * fx + fy * fy + fz * fz; if (f < dist) dist = f; double rx = Train.Cars[j].RearAxle.Follower.WorldPosition.X - Position.X; double ry = Train.Cars[j].RearAxle.Follower.WorldPosition.Y - Position.Y; double rz = Train.Cars[j].RearAxle.Follower.WorldPosition.Z - Position.Z; double r = rx * rx + ry * ry + rz * rz; if (r < dist) dist = r; } Function.Stack[s] = Math.Sqrt(dist); } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainDistanceToCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { double x = 0.5 * (Train.Cars[j].FrontAxle.Follower.WorldPosition.X + Train.Cars[j].RearAxle.Follower.WorldPosition.X) - Position.X; double y = 0.5 * (Train.Cars[j].FrontAxle.Follower.WorldPosition.Y + Train.Cars[j].RearAxle.Follower.WorldPosition.Y) - Position.Y; double z = 0.5 * (Train.Cars[j].FrontAxle.Follower.WorldPosition.Z + Train.Cars[j].RearAxle.Follower.WorldPosition.Z) - Position.Z; Function.Stack[s - 1] = Math.Sqrt(x * x + y * y + z * z); } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.TrainTrackDistance: if (Train != null) { int r = Train.Cars.Length - 1; double t0 = Train.Cars[0].FrontAxle.Follower.TrackPosition - Train.Cars[0].FrontAxlePosition + 0.5 * Train.Cars[0].Length; double t1 = Train.Cars[r].RearAxle.Follower.TrackPosition - Train.Cars[r].RearAxlePosition - 0.5 * Train.Cars[r].Length; Function.Stack[s] = TrackPosition > t0 ? TrackPosition - t0 : TrackPosition < t1 ? TrackPosition - t1 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainTrackDistanceToCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { double p = 0.5 * (Train.Cars[j].FrontAxle.Follower.TrackPosition + Train.Cars[j].RearAxle.Follower.TrackPosition); Function.Stack[s - 1] = TrackPosition - p; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; // door case Instructions.Doors: if (Train != null) { double a = 0.0; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s] = a; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.DoorsIndex: if (Train != null) { double a = 0.0; int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s - 1] = a; } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.LeftDoors: if (Train != null) { double a = 0.0; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].Direction == -1 & Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s] = a; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.LeftDoorsIndex: if (Train != null) { double a = 0.0; int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].Direction == -1 & Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s - 1] = a; } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.RightDoors: if (Train != null) { double a = 0.0; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].Direction == 1 & Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s] = a; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.RightDoorsIndex: if (Train != null) { double a = 0.0; int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].Direction == 1 & Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s - 1] = a; } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.LeftDoorsTarget: if (Train != null) { bool q = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Specs.AnticipatedLeftDoorsOpened) { q = true; break; } } Function.Stack[s] = q ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.LeftDoorsTargetIndex: if (Train != null) { bool q = false; int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.AnticipatedLeftDoorsOpened) { q = true; break; } } } Function.Stack[s] = q ? 1.0 : 0.0; } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.RightDoorsTarget: if (Train != null) { bool q = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Specs.AnticipatedRightDoorsOpened) { q = true; break; } } Function.Stack[s] = q ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.RightDoorsTargetIndex: if (Train != null) { bool q = false; int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.AnticipatedRightDoorsOpened) { q = true; break; } } } Function.Stack[s] = q ? 1.0 : 0.0; } else { Function.Stack[s - 1] = 0.0; } break; // handles case Instructions.ReverserNotch: if (Train != null) { Function.Stack[s] = (double)Train.Specs.CurrentReverser.Driver; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.PowerNotch: if (Train != null) { Function.Stack[s] = (double)Train.Specs.CurrentPowerNotch.Driver; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.PowerNotches: if (Train != null) { Function.Stack[s] = (double)Train.Specs.MaximumPowerNotch; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeNotch: if (Train != null) { if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { Function.Stack[s] = (double)Train.Specs.AirBrake.Handle.Driver; } else { Function.Stack[s] = (double)Train.Specs.CurrentBrakeNotch.Driver; } } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeNotches: if (Train != null) { if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { Function.Stack[s] = 2.0; } else { Function.Stack[s] = (double)Train.Specs.MaximumBrakeNotch; } } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeNotchLinear: if (Train != null) { if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { if (Train.Specs.CurrentEmergencyBrake.Driver) { Function.Stack[s] = 3.0; } else { Function.Stack[s] = (double)Train.Specs.AirBrake.Handle.Driver; } } else if (Train.Specs.HasHoldBrake) { if (Train.Specs.CurrentEmergencyBrake.Driver) { Function.Stack[s] = (double)Train.Specs.MaximumBrakeNotch + 2.0; } else if (Train.Specs.CurrentBrakeNotch.Driver > 0) { Function.Stack[s] = (double)Train.Specs.CurrentBrakeNotch.Driver + 1.0; } else { Function.Stack[s] = Train.Specs.CurrentHoldBrake.Driver ? 1.0 : 0.0; } } else { if (Train.Specs.CurrentEmergencyBrake.Driver) { Function.Stack[s] = (double)Train.Specs.MaximumBrakeNotch + 1.0; } else { Function.Stack[s] = (double)Train.Specs.CurrentBrakeNotch.Driver; } } } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeNotchesLinear: if (Train != null) { if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { Function.Stack[s] = 3.0; } else if (Train.Specs.HasHoldBrake) { Function.Stack[s] = Train.Specs.MaximumBrakeNotch + 2.0; } else { Function.Stack[s] = Train.Specs.MaximumBrakeNotch + 1.0; } } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.EmergencyBrake: if (Train != null) { Function.Stack[s] = Train.Specs.CurrentEmergencyBrake.Driver ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.HasAirBrake: if (Train != null) { Function.Stack[s] = Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.HoldBrake: if (Train != null) { Function.Stack[s] = Train.Specs.CurrentHoldBrake.Driver ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.HasHoldBrake: if (Train != null) { Function.Stack[s] = Train.Specs.HasHoldBrake ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.ConstSpeed: if (Train != null) { Function.Stack[s] = Train.Specs.CurrentConstSpeed ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.HasConstSpeed: if (Train != null) { Function.Stack[s] = Train.Specs.HasConstSpeed ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; // brake case Instructions.BrakeMainReservoir: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeMainReservoirOfCar: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.AirBrake.MainReservoirCurrentPressure; } else { Function.Stack[s - 1] = 0.0; } } break; case Instructions.BrakeEqualizingReservoir: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeEqualizingReservoirOfCar: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.AirBrake.EqualizingReservoirCurrentPressure; } else { Function.Stack[s - 1] = 0.0; } } break; case Instructions.BrakeBrakePipe: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeBrakePipeOfCar: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.AirBrake.BrakePipeCurrentPressure; } else { Function.Stack[s - 1] = 0.0; } } break; case Instructions.BrakeBrakeCylinder: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeBrakeCylinderOfCar: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.AirBrake.BrakeCylinderCurrentPressure; } else { Function.Stack[s - 1] = 0.0; } } break; case Instructions.BrakeStraightAirPipe: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeCurrentPressure; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeStraightAirPipeOfCar: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.AirBrake.StraightAirPipeCurrentPressure; } else { Function.Stack[s - 1] = 0.0; } } break; // safety case Instructions.SafetyPluginAvailable: if (Train == TrainManager.PlayerTrain && Train.Plugin != null) { Function.Stack[s] = TrainManager.PlayerTrain.Plugin.IsDefault ? 0.0 : 1.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.SafetyPluginState: if (Train == null || Train.Plugin == null) { Function.Stack[s - 1] = 0.0; } else { int n = (int)Math.Round(Function.Stack[s - 1]); if (n >= 0 & n < Train.Plugin.Panel.Length) { Function.Stack[s - 1] = (double)Train.Plugin.Panel[n]; } else { Function.Stack[s - 1] = 0.0; } } break; // timetable case Instructions.TimetableVisible: Function.Stack[s] = Timetable.CurrentTimetable == Timetable.TimetableState.Custom & Timetable.CustomTimetableAvailable ? 0.0 : -1.0; s++; break; // sections case Instructions.SectionAspectNumber: if (IsPartOfTrain) { int nextSectionIndex = Train.CurrentSectionIndex + 1; if (nextSectionIndex >= 0 & nextSectionIndex < Game.Sections.Length) { int a = Game.Sections[nextSectionIndex].CurrentAspect; if (a >= 0 & a < Game.Sections[nextSectionIndex].Aspects.Length) { Function.Stack[s] = (double)Game.Sections[nextSectionIndex].Aspects[a].Number; } else { Function.Stack[s] = 0; } } } else if (SectionIndex >= 0 & SectionIndex < Game.Sections.Length) { int a = Game.Sections[SectionIndex].CurrentAspect; if (a >= 0 & a < Game.Sections[SectionIndex].Aspects.Length) { Function.Stack[s] = (double)Game.Sections[SectionIndex].Aspects[a].Number; } else { Function.Stack[s] = 0; } } else { Function.Stack[s] = 0; } s++; break; // default default: throw new System.InvalidOperationException("The unknown instruction " + Function.Instructions[i].ToString() + " was encountered in ExecuteFunctionScript."); } } Function.LastResult = Function.Stack[s - 1]; } // get postfix notation from infix notation internal static string GetPostfixNotationFromInfixNotation(string Expression) { string Function = GetFunctionNotationFromInfixNotation(Expression, true); return GetPostfixNotationFromFunctionNotation(Function); } // get function script from infix notation internal static FunctionScript GetFunctionScriptFromInfixNotation(string Expression) { string Function = GetFunctionNotationFromInfixNotation(Expression, true); string Postfix = GetPostfixNotationFromFunctionNotation(Function); return GetFunctionScriptFromPostfixNotation(Postfix); } // get function notation from infix notation private static string GetFunctionNotationFromInfixNotation(string Expression, bool Preprocessing) { // brackets if (Preprocessing) { int s = 0; while (true) { if (s >= Expression.Length) break; int i = Expression.IndexOf('[', s); if (i >= s) { int j = i + 1, t = j, m = 1; string[] p = new string[4]; int n = 0; bool q = false; while (j < Expression.Length) { switch (Expression[j]) { case '[': m++; break; case ']': m--; if (m < 0) { throw new System.IO.InvalidDataException("Unexpected closing bracket encountered in " + Expression); } else if (m == 0) { if (n >= p.Length) Array.Resize(ref p, n << 1); p[n] = Expression.Substring(t, j - t); n++; string a = Expression.Substring(0, i).Trim(); string c = Expression.Substring(j + 1).Trim(); System.Text.StringBuilder r = new System.Text.StringBuilder(); for (int k = 0; k < n; k++) { p[k] = GetFunctionNotationFromInfixNotation(p[k], true); if (k > 0) r.Append(','); r.Append(p[k]); } Expression = a + "[" + r.ToString() + "]" + c; s = i + r.Length + 2; q = true; } break; case ',': if (m == 1) { if (n >= p.Length) Array.Resize(ref p, n << 1); p[n] = Expression.Substring(t, j - t); n++; t = j + 1; } break; } if (q) { break; } j++; } if (!q) { throw new System.IO.InvalidDataException("Missing closing bracket encountered in " + Expression); } } else { break; } } } // parentheses { int i = Expression.IndexOf('('); if (i >= 0) { int j = i + 1; int n = 1; while (j < Expression.Length) { switch (Expression[j]) { case '(': n++; break; case ')': n--; if (n < 0) { throw new System.IO.InvalidDataException("Unexpected closing parenthesis encountered in " + Expression); } else if (n == 0) { string a = Expression.Substring(0, i).Trim(); string b = Expression.Substring(i + 1, j - i - 1).Trim(); string c = Expression.Substring(j + 1).Trim(); return GetFunctionNotationFromInfixNotation(a + GetFunctionNotationFromInfixNotation(b, false) + c, false); } break; } j++; } throw new System.IO.InvalidDataException("No closing parenthesis found in " + Expression); } else { i = Expression.IndexOf(')'); if (i >= 0) { throw new System.IO.InvalidDataException("Unexpected closing parenthesis encountered in " + Expression); } } } // operators { int i = Expression.IndexOf('|'); if (i >= 0) { string a = Expression.Substring(0, i).Trim(); string b = Expression.Substring(i + 1).Trim(); return "Or[" + GetFunctionNotationFromInfixNotation(a, false) + "," + GetFunctionNotationFromInfixNotation(b, false) + "]"; } } { int i = Expression.IndexOf('^'); if (i >= 0) { string a = Expression.Substring(0, i).Trim(); string b = Expression.Substring(i + 1).Trim(); return "Xor[" + GetFunctionNotationFromInfixNotation(a, false) + "," + GetFunctionNotationFromInfixNotation(b, false) + "]"; } } { int i = Expression.IndexOf('&'); if (i >= 0) { string a = Expression.Substring(0, i).Trim(); string b = Expression.Substring(i + 1).Trim(); return "And[" + GetFunctionNotationFromInfixNotation(a, false) + "," + GetFunctionNotationFromInfixNotation(b, false) + "]"; } } { int i = Expression.IndexOf('!'); while (true) { if (i >= 0) { if (i < Expression.Length - 1) { if (Expression[i + 1] == '=') { int j = Expression.IndexOf('!', i + 2); i = j < i + 2 ? -1 : j; } else break; } else break; } else break; } if (i >= 0) { string b = Expression.Substring(i + 1).Trim(); return "Not[" + GetFunctionNotationFromInfixNotation(b, false) + "]"; } } { int[] j = new int[6]; j[0] = Expression.LastIndexOf("=="); j[1] = Expression.LastIndexOf("!="); j[2] = Expression.LastIndexOf("<="); j[3] = Expression.LastIndexOf(">="); j[4] = Expression.LastIndexOf("<"); j[5] = Expression.LastIndexOf(">"); int k = -1; for (int i = 0; i < j.Length; i++) { if (j[i] >= 0) { if (k >= 0) { if (j[i] > j[k]) k = i; } else { k = i; } } } if (k >= 0) { int l = k <= 3 ? 2 : 1; string a = Expression.Substring(0, j[k]).Trim(); string b = Expression.Substring(j[k] + l).Trim(); string f; switch (k) { case 0: f = "Equal"; break; case 1: f = "Unequal"; break; case 2: f = "LessEqual"; break; case 3: f = "GreaterEqual"; break; case 4: f = "Less"; break; case 5: f = "Greater"; break; default: f = "Halt"; break; } return f + "[" + GetFunctionNotationFromInfixNotation(a, false) + "," + GetFunctionNotationFromInfixNotation(b, false) + "]"; } } { int i = Expression.LastIndexOf('+'); int j = Expression.LastIndexOf('-'); if (i >= 0 & (j == -1 | j >= 0 & i > j)) { string a = Expression.Substring(0, i).Trim(); string b = Expression.Substring(i + 1).Trim(); return "Plus[" + GetFunctionNotationFromInfixNotation(a, false) + "," + GetFunctionNotationFromInfixNotation(b, false) + "]"; } else if (j >= 0) { string a = Expression.Substring(0, j).Trim(); string b = Expression.Substring(j + 1).Trim(); if (a.Length != 0) { return "Subtract[" + GetFunctionNotationFromInfixNotation(a, false) + "," + GetFunctionNotationFromInfixNotation(b, false) + "]"; } } } { int i = Expression.IndexOf('*'); if (i >= 0) { string a = Expression.Substring(0, i).Trim(); string b = Expression.Substring(i + 1).Trim(); return "Times[" + GetFunctionNotationFromInfixNotation(a, false) + "," + GetFunctionNotationFromInfixNotation(b, false) + "]"; } } { int i = Expression.IndexOf('/'); if (i >= 0) { string a = Expression.Substring(0, i).Trim(); string b = Expression.Substring(i + 1).Trim(); return "Divide[" + GetFunctionNotationFromInfixNotation(a, false) + "," + GetFunctionNotationFromInfixNotation(b, false) + "]"; } } { int i = Expression.IndexOf('-'); if (i >= 0) { string a = Expression.Substring(0, i).Trim(); string b = Expression.Substring(i + 1).Trim(); if (a.Length == 0) { return "Minus[" + GetFunctionNotationFromInfixNotation(b, false) + "]"; } } } return Expression.Trim(); } // get postfix notation from function notation private static string GetPostfixNotationFromFunctionNotation(string Expression) { int i = Expression.IndexOf('['); if (i >= 0) { if (!Expression.EndsWith("]")) { throw new System.IO.InvalidDataException("Missing closing bracket encountered in " + Expression); } } else { if (Expression.EndsWith("]")) { throw new System.IO.InvalidDataException("Unexpected closing bracket encountered in " + Expression); } else { double value; if (double.TryParse(Expression, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out value)) { return Expression; } else { for (int j = 0; j < Expression.Length; j++) { if (!char.IsLetterOrDigit(Expression[j])) { throw new System.IO.InvalidDataException("Invalid character encountered in variable " + Expression); } } return Expression; } } } string f = Expression.Substring(0, i); string s = Expression.Substring(i + 1, Expression.Length - i - 2); string[] a = new string[4]; int n = 0; int b = 0; for (i = 0; i < s.Length; i++) { switch (s[i]) { case '[': { i++; int m = 1; bool q = false; while (i < s.Length) { switch (s[i]) { case '[': m++; break; case ']': m--; if (m < 0) { throw new System.IO.InvalidDataException("Unexpected closing bracket encountered in " + Expression); } else if (m == 0) { q = true; } break; } if (q) { break; } i++; } if (!q) { throw new System.IO.InvalidDataException("No closing bracket found in " + Expression); } } break; case ']': throw new System.IO.InvalidDataException("Unexpected closing bracket encountered in " + Expression); case ',': if (n == a.Length) { Array.Resize(ref a, n << 1); } a[n] = s.Substring(b, i - b).Trim(); n++; b = i + 1; break; } } if (n == a.Length) { Array.Resize(ref a, n << 1); } a[n] = s.Substring(b).Trim(); n++; if (n == 1 & a[0].Length == 0) { n = 0; } for (i = 0; i < n; i++) { if (a[i].Length == 0) { throw new System.IO.InvalidDataException("An empty argument is invalid in " + f + " in " + Expression); } else if (a[i].IndexOf(' ') >= 0) { throw new System.IO.InvalidDataException("An argument containing a space is invalid in " + f + " in " + Expression); } a[i] = GetPostfixNotationFromFunctionNotation(a[i]).Trim(); } switch (f.ToLowerInvariant()) { // arithmetic case "plus": if (n == 0) { return "0"; } else if (n == 1) { return a[0]; } else if (n == 2) { if (a[1].EndsWith(" *")) { return a[1] + " " + a[0] + " +"; } else { return a[0] + " " + a[1] + " +"; } } else { System.Text.StringBuilder t = new System.Text.StringBuilder(a[0] + " " + a[1] + " +"); for (i = 2; i < n; i++) { t.Append(" " + a[i] + " +"); } return t.ToString(); } case "subtract": if (n == 2) { return a[0] + " " + a[1] + " -"; } else { throw new System.IO.InvalidDataException(f + " is expected to have 2 arguments in " + Expression); } case "times": if (n == 0) { return "1"; } else if (n == 1) { return a[0]; } else if (n == 2) { return a[0] + " " + a[1] + " *"; } else { System.Text.StringBuilder t = new System.Text.StringBuilder(a[0] + " " + a[1] + " *"); for (i = 2; i < n; i++) { t.Append(" " + a[i] + " *"); } return t.ToString(); } case "divide": if (n == 2) { return a[0] + " " + a[1] + " /"; } else { throw new System.IO.InvalidDataException(f + " is expected to have 2 arguments in " + Expression); } case "power": if (n == 0) { return "1"; } else if (n == 1) { return a[0]; } else if (n == 2) { return a[0] + " " + a[1] + " power"; } else { System.Text.StringBuilder t = new System.Text.StringBuilder(a[0] + " " + a[1]); for (i = 2; i < n; i++) { t.Append(" " + a[i]); } for (i = 0; i < n - 1; i++) { t.Append(" power"); } return t.ToString(); } // math case "quotient": case "mod": case "min": case "max": if (n == 2) { return a[0] + " " + a[1] + " " + f; } else { throw new System.IO.InvalidDataException(f + " is expected to have 2 arguments in " + Expression); } case "minus": case "reciprocal": case "floor": case "ceiling": case "round": case "abs": case "sign": case "exp": case "log": case "sqrt": case "sin": case "cos": case "tan": case "arctan": if (n == 1) { return a[0] + " " + f; } else { throw new System.IO.InvalidDataException(f + " is expected to have 1 argument in " + Expression); } // comparisons case "equal": case "unequal": case "less": case "greater": case "lessequal": case "greaterequal": if (n == 2) { string g; switch (f.ToLowerInvariant()) { case "equal": g = "=="; break; case "unequal": g = "!="; break; case "less": g = "<"; break; case "greater": g = ">"; break; case "lessequal": g = "<="; break; case "greaterequal": g = ">="; break; default: g = "halt"; break; } return a[0] + " " + a[1] + " " + g; } else { throw new System.IO.InvalidDataException(f + " is expected to have 2 arguments in " + Expression); } case "if": if (n == 3) { return a[0] + " " + a[1] + " " + a[2] + " ?"; } else { throw new System.IO.InvalidDataException(f + " is expected to have 3 arguments in " + Expression); } // logical case "not": if (n == 1) { return a[0] + " !"; } else { throw new System.IO.InvalidDataException(f + " is expected to have 1 argument in " + Expression); } case "and": if (n == 0) { return "1"; } else if (n == 1) { return a[0]; } else if (n == 2) { return a[0] + " " + a[1] + " &"; } else { System.Text.StringBuilder t = new System.Text.StringBuilder(a[0] + " " + a[1] + " +"); for (i = 2; i < n; i++) { t.Append(" " + a[i] + " &"); } return t.ToString(); } case "or": if (n == 0) { return "0"; } else if (n == 1) { return a[0]; } else if (n == 2) { return a[0] + " " + a[1] + " |"; } else { System.Text.StringBuilder t = new System.Text.StringBuilder(a[0] + " " + a[1] + " +"); for (i = 2; i < n; i++) { t.Append(" " + a[i] + " |"); } return t.ToString(); } case "xor": if (n == 0) { return "0"; } else if (n == 1) { return a[0]; } else if (n == 2) { return a[0] + " " + a[1] + " ^"; } else { System.Text.StringBuilder t = new System.Text.StringBuilder(a[0] + " " + a[1] + " +"); for (i = 2; i < n; i++) { t.Append(" " + a[i] + " ^"); } return t.ToString(); } // train case "distance": case "trackdistance": case "speed": case "speedometer": case "acceleration": case "accelerationmotor": case "doors": case "leftdoors": case "rightdoorstarget": case "leftdoorstarget": case "rightdoors": case "mainreservoir": case "equalizingreservoir": case "brakepipe": case "brakecylinder": case "straightairpipe": if (n == 1) { return a[0] + " " + f.ToLowerInvariant() + "index"; } else { throw new System.IO.InvalidDataException(f + " is expected to have 1 argument in " + Expression); } case "pluginstate": if (n == 1) { return a[0] + " pluginstate"; } else { throw new System.IO.InvalidDataException(f + " is expected to have 1 argument in " + Expression); } // not supported default: throw new System.IO.InvalidDataException("The function " + f + " is not supported in " + Expression); } } // get optimized postfix notation private static string GetOptimizedPostfixNotation(string Expression) { Expression = " " + Expression + " "; Expression = Expression.Replace(" 1 1 == -- ", " 0 "); Expression = Expression.Replace(" 1 doors - 1 == -- ", " doors ! -- "); string[] Arguments = Expression.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); string[] Stack = new string[Arguments.Length]; int StackLength = 0; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; for (int i = 0; i < Arguments.Length; i++) { switch (Arguments[i].ToLowerInvariant()) { case "<>": { bool q = true; if (StackLength >= 1) { if (Stack[StackLength - 1] == "<>") { // <> <> // [n/a] StackLength--; q = false; } else if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { // a b <> // b a string t = Stack[StackLength - 1]; Stack[StackLength - 1] = Stack[StackLength - 2]; Stack[StackLength - 2] = t; } } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "+": { bool q = true; if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { // x y + // (x y +) Stack[StackLength - 2] = (a + b).ToString(Culture); StackLength--; q = false; } else if (StackLength >= 3 && Stack[StackLength - 2] == "+") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { // A x + y + // A (y x +) + Stack[StackLength - 3] = (a + b).ToString(Culture); StackLength--; q = false; } } else if (StackLength >= 3 && Stack[StackLength - 2] == "-") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { // A x - y + // A (y x -) + Stack[StackLength - 3] = (b - a).ToString(Culture); Stack[StackLength - 2] = "+"; StackLength--; q = false; } } else if (Stack[StackLength - 2] == "*") { // A x * y + // A x y fma Stack[StackLength - 2] = Stack[StackLength - 1]; Stack[StackLength - 1] = "fma"; q = false; } else if (Stack[StackLength - 2] == "fma") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { // A B y fma z + // A B (y z +) fma Stack[StackLength - 3] = (a + b).ToString(Culture); StackLength--; q = false; } } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "-": { bool q = true; if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { // x y - // (x y -) Stack[StackLength - 2] = (a - b).ToString(Culture); StackLength--; q = false; } else if (StackLength >= 3 && Stack[StackLength - 2] == "+") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { // A x + y - // A (x y -) + Stack[StackLength - 3] = (a - b).ToString(Culture); Stack[StackLength - 2] = "+"; StackLength--; q = false; } } else if (StackLength >= 3 && Stack[StackLength - 2] == "-") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { // A x - y - // A (x y + minus) - Stack[StackLength - 3] = (-a - b).ToString(Culture); Stack[StackLength - 2] = "+"; StackLength--; q = false; } } else if (Stack[StackLength - 2] == "*") { // A x * y - // A x (y minus) fma Stack[StackLength - 2] = (-b).ToString(Culture); Stack[StackLength - 1] = "fma"; q = false; } else if (Stack[StackLength - 2] == "fma") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { // A B y fma z - // A B (y z -) fma Stack[StackLength - 3] = (a - b).ToString(Culture); StackLength--; q = false; } } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "minus": { bool q = true; if (StackLength >= 1) { if (Stack[StackLength - 1].Equals("minus", StringComparison.InvariantCultureIgnoreCase)) { // minus minus // [n/a] StackLength--; q = false; } else { double a; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out a)) { // x minus // (x minus) Stack[StackLength - 1] = (-a).ToString(Culture); q = false; } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "*": { bool q = true; if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { // x y * // (x y *) Stack[StackLength - 2] = (a * b).ToString(Culture); StackLength--; q = false; } else if (StackLength >= 3 && Stack[StackLength - 2] == "*") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { // A x * y * // A (x y *) * Stack[StackLength - 3] = (a * b).ToString(Culture); StackLength--; q = false; } } else if (StackLength >= 3 && Stack[StackLength - 2] == "+") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { // A x + y * // A y (x y *) fma Stack[StackLength - 3] = Stack[StackLength - 1]; Stack[StackLength - 2] = (a * b).ToString(Culture); Stack[StackLength - 1] = "fma"; q = false; } } else if (StackLength >= 3 && Stack[StackLength - 2] == "-") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { // A x - y * // A y (x y * minus) fma Stack[StackLength - 3] = Stack[StackLength - 1]; Stack[StackLength - 2] = (-a * b).ToString(Culture); Stack[StackLength - 1] = "fma"; q = false; } } else if (StackLength >= 4 && Stack[StackLength - 2] == "fma") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { double c; if (double.TryParse(Stack[StackLength - 4], System.Globalization.NumberStyles.Float, Culture, out c)) { // A x y fma z * // A (x z *) (y z *) fma Stack[StackLength - 4] = (c * b).ToString(Culture); Stack[StackLength - 3] = (a * b).ToString(Culture); StackLength--; q = false; } else { // A B y fma z * // A B * z (y z *) fma Stack[StackLength - 3] = "*"; Stack[StackLength - 2] = Stack[StackLength - 1]; Stack[StackLength - 1] = (a * b).ToString(Culture); Stack[StackLength] = "fma"; StackLength++; q = false; } } } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "reciprocal": { bool q = true; if (StackLength >= 1) { if (Stack[StackLength - 1].Equals("reciprocal", StringComparison.InvariantCultureIgnoreCase)) { // reciprocal reciprocal // [n/a] StackLength--; q = false; } else { double a; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out a)) { // x reciprocal // (x reciprocal) a = a == 0.0 ? 0.0 : 1.0 / a; Stack[StackLength - 1] = a.ToString(Culture); q = false; } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "/": { bool q = true; if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { if (b != 0.0) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { // x y / // (x y /) Stack[StackLength - 2] = (a / b).ToString(Culture); StackLength--; q = false; } else if (StackLength >= 3 && Stack[StackLength - 2] == "*") { if (double.TryParse(Stack[StackLength - 3], System.Globalization.NumberStyles.Float, Culture, out a)) { // A x * y / // A (x y /) * Stack[StackLength - 3] = (a / b).ToString(Culture); StackLength--; q = false; } } } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "++": { bool q = true; if (StackLength >= 1) { double a; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out a)) { // x ++ // (x ++) Stack[StackLength - 1] = (a + 1).ToString(Culture); q = false; } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "--": { bool q = true; if (StackLength >= 1) { double a; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out a)) { // x -- // (x --) Stack[StackLength - 1] = (a - 1).ToString(Culture); q = false; } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "!": { bool q = true; if (StackLength >= 1) { if (Stack[StackLength - 1] == "!") { StackLength--; q = false; } else if (Stack[StackLength - 1] == "==") { Stack[StackLength - 1] = "!="; q = false; } else if (Stack[StackLength - 1] == "!=") { Stack[StackLength - 1] = "=="; q = false; } else if (Stack[StackLength - 1] == "<") { Stack[StackLength - 1] = ">="; q = false; } else if (Stack[StackLength - 1] == ">") { Stack[StackLength - 1] = "<="; q = false; } else if (Stack[StackLength - 1] == "<=") { Stack[StackLength - 1] = ">"; q = false; } else if (Stack[StackLength - 1] == ">=") { Stack[StackLength - 1] = "<"; q = false; } else { double a; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out a)) { Stack[StackLength - 1] = a == 0.0 ? "1" : "0"; q = false; } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "==": { bool q = true; if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { Stack[StackLength - 2] = a == b ? "1" : "0"; StackLength--; q = false; } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "!=": { bool q = true; if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { Stack[StackLength - 2] = a != b ? "1" : "0"; StackLength--; q = false; } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "<": { bool q = true; if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { Stack[StackLength - 2] = a < b ? "1" : "0"; StackLength--; q = false; } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case ">": { bool q = true; if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { Stack[StackLength - 2] = a > b ? "1" : "0"; StackLength--; q = false; } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "<=": { bool q = true; if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { Stack[StackLength - 2] = a <= b ? "1" : "0"; StackLength--; q = false; } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case ">=": { bool q = true; if (StackLength >= 2) { double b; if (double.TryParse(Stack[StackLength - 1], System.Globalization.NumberStyles.Float, Culture, out b)) { double a; if (double.TryParse(Stack[StackLength - 2], System.Globalization.NumberStyles.Float, Culture, out a)) { Stack[StackLength - 2] = a >= b ? "1" : "0"; StackLength--; q = false; } } } if (q) { Stack[StackLength] = Arguments[i]; StackLength++; } } break; case "floor": if (StackLength >= 1 && Stack[StackLength - 1] == "/") { Stack[StackLength - 1] = "quotient"; } else { Stack[StackLength] = Arguments[i]; StackLength++; } break; default: Stack[StackLength] = Arguments[i]; StackLength++; break; } } System.Text.StringBuilder Builder = new System.Text.StringBuilder(); for (int i = 0; i < StackLength; i++) { if (i != 0) Builder.Append(' '); Builder.Append(Stack[i]); } return Builder.ToString(); } // get function script from postfix notation internal static FunctionScript GetFunctionScriptFromPostfixNotation(string Expression) { Expression = GetOptimizedPostfixNotation(Expression); System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; FunctionScript Result = new FunctionScript(); string[] Arguments = Expression.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); Result.Instructions = new Instructions[16]; int n = 0; Result.Stack = new double[16]; int m = 0, s = 0; Result.Constants = new double[16]; int c = 0; for (int i = 0; i < Arguments.Length; i++) { double d; if (double.TryParse(Arguments[i], System.Globalization.NumberStyles.Float, Culture, out d)) { if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.SystemConstant; if (c >= Result.Constants.Length) Array.Resize(ref Result.Constants, Result.Constants.Length << 1); Result.Constants[c] = d; n++; c++; s++; if (s >= m) m = s; } else { switch (Arguments[i].ToLowerInvariant()) { // system case "halt": throw new System.InvalidOperationException("The halt instruction was encountered in function script " + Expression); case "value": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.SystemValue; n++; s++; if (s >= m) m = s; break; case "delta": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.SystemDelta; n++; s++; if (s >= m) m = s; break; // stack case "~": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.StackCopy; n++; s++; if (s >= m) m = s; break; case "<>": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.StackSwap; n++; break; // math case "+": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathPlus; n++; s--; break; case "-": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathSubtract; n++; s--; break; case "minus": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathMinus; n++; break; case "*": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathTimes; n++; s--; break; case "/": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathDivide; n++; s--; break; case "reciprocal": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathReciprocal; n++; break; case "power": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathPower; n++; s--; break; case "++": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathIncrement; n++; break; case "--": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathDecrement; n++; break; case "fma": if (s < 3) throw new System.InvalidOperationException(Arguments[i] + " requires at least 3 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathFusedMultiplyAdd; n++; s -= 2; break; case "quotient": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathQuotient; n++; s--; break; case "mod": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathMod; n++; s--; break; case "floor": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathFloor; n++; break; case "ceiling": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathCeiling; n++; break; case "round": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathRound; n++; break; case "min": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathMin; n++; s--; break; case "max": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathMax; n++; s--; break; case "abs": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathAbs; n++; break; case "sign": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathSign; n++; break; case "exp": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathExp; n++; break; case "log": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathLog; n++; break; case "sqrt": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathSqrt; n++; break; case "sin": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathSin; n++; break; case "cos": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathCos; n++; break; case "tan": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathTan; n++; break; case "arctan": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.MathArcTan; n++; break; case "==": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.CompareEqual; n++; s--; break; case "!=": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.CompareUnequal; n++; s--; break; // conditionals case "<": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.CompareLess; n++; s--; break; case ">": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.CompareGreater; n++; s--; break; case "<=": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.CompareLessEqual; n++; s--; break; case ">=": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.CompareGreaterEqual; n++; s--; break; case "?": if (s < 3) throw new System.InvalidOperationException(Arguments[i] + " requires at least 3 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.CompareConditional; n++; s -= 2; break; // logical case "!": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.LogicalNot; n++; break; case "&": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.LogicalAnd; n++; s--; break; case "|": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.LogicalOr; n++; s--; break; case "!&": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.LogicalNand; n++; s--; break; case "!|": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.LogicalNor; n++; s--; break; case "^": if (s < 2) throw new System.InvalidOperationException(Arguments[i] + " requires at least 2 arguments on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.LogicalXor; n++; s--; break; // time/camera case "time": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TimeSecondsSinceMidnight; n++; s++; if (s >= m) m = s; break; case "cameradistance": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.CameraDistance; n++; s++; if (s >= m) m = s; break; // train case "cars": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainCars; n++; s++; if (s >= m) m = s; break; case "speed": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainSpeed; n++; s++; if (s >= m) m = s; break; case "speedindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainSpeedOfCar; n++; break; case "speedometer": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainSpeedometer; n++; s++; if (s >= m) m = s; break; case "speedometerindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainSpeedometerOfCar; n++; break; case "acceleration": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainAcceleration; n++; s++; if (s >= m) m = s; break; case "accelerationindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainAccelerationOfCar; n++; break; case "accelerationmotor": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainAccelerationMotor; n++; s++; if (s >= m) m = s; break; case "accelerationmotorindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainAccelerationMotorOfCar; n++; break; case "distance": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainDistance; n++; s++; if (s >= m) m = s; break; case "distanceindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainDistanceToCar; n++; break; case "trackdistance": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainTrackDistance; n++; s++; if (s >= m) m = s; break; case "trackdistanceindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TrainTrackDistanceToCar; n++; break; // train: doors case "doors": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.Doors; n++; s++; if (s >= m) m = s; break; case "doorsindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.DoorsIndex; n++; break; case "leftdoors": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.LeftDoors; n++; s++; if (s >= m) m = s; break; case "leftdoorsindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.LeftDoorsIndex; n++; break; case "rightdoors": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.RightDoors; n++; s++; if (s >= m) m = s; break; case "rightdoorsindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.RightDoorsIndex; n++; break; case "leftdoorstarget": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.LeftDoorsTarget; n++; s++; if (s >= m) m = s; break; case "leftdoorstargetindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.LeftDoorsTargetIndex; n++; break; case "rightdoorstarget": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.RightDoorsTarget; n++; s++; if (s >= m) m = s; break; case "rightdoorstargetindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.RightDoorsTargetIndex; n++; break; // train: handles case "reversernotch": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.ReverserNotch; n++; s++; if (s >= m) m = s; break; case "powernotch": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.PowerNotch; n++; s++; if (s >= m) m = s; break; case "powernotches": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.PowerNotches; n++; s++; if (s >= m) m = s; break; case "brakenotch": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeNotch; n++; s++; if (s >= m) m = s; break; case "brakenotches": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeNotches; n++; s++; if (s >= m) m = s; break; case "brakenotchlinear": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeNotchLinear; n++; s++; if (s >= m) m = s; break; case "brakenotcheslinear": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeNotchesLinear; n++; s++; if (s >= m) m = s; break; case "emergencybrake": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.EmergencyBrake; n++; s++; if (s >= m) m = s; break; case "hasairbrake": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.HasAirBrake; n++; s++; if (s >= m) m = s; break; case "holdbrake": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.HoldBrake; n++; s++; if (s >= m) m = s; break; case "hasholdbrake": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.HasHoldBrake; n++; s++; if (s >= m) m = s; break; case "constspeed": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.ConstSpeed; n++; s++; if (s >= m) m = s; break; case "hasconstspeed": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.HasConstSpeed; n++; s++; if (s >= m) m = s; break; // train: brake case "mainreservoir": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeMainReservoir; n++; s++; if (s >= m) m = s; break; case "mainreservoirindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeMainReservoirOfCar; n++; break; case "equalizingreservoir": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeEqualizingReservoir; n++; s++; if (s >= m) m = s; break; case "equalizingreservoirindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeEqualizingReservoirOfCar; n++; break; case "brakepipe": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeBrakePipe; n++; s++; if (s >= m) m = s; break; case "brakepipeindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeBrakePipeOfCar; n++; break; case "brakecylinder": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeBrakeCylinder; n++; s++; if (s >= m) m = s; break; case "brakecylinderindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeBrakeCylinderOfCar; n++; break; case "straightairpipe": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeStraightAirPipe; n++; s++; if (s >= m) m = s; break; case "straightairpipeindex": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.BrakeStraightAirPipeOfCar; n++; break; // train: safety case "hasplugin": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.SafetyPluginAvailable; n++; s++; if (s >= m) m = s; break; case "pluginstate": if (s < 1) throw new System.InvalidOperationException(Arguments[i] + " requires at least 1 argument on the stack in function script " + Expression); if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.SafetyPluginState; n++; break; // train: timetable case "timetable": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.TimetableVisible; n++; s++; if (s >= m) m = s; break; // sections case "section": if (n >= Result.Instructions.Length) Array.Resize(ref Result.Instructions, Result.Instructions.Length << 1); Result.Instructions[n] = Instructions.SectionAspectNumber; n++; s++; if (s >= m) m = s; break; // default default: throw new System.IO.InvalidDataException("Unknown command " + Arguments[i] + " encountered in function script " + Expression); } } } if (s != 1) { throw new System.InvalidOperationException("There must be exactly one argument left on the stack at the end in function script " + Expression); } Array.Resize(ref Result.Instructions, n); Array.Resize(ref Result.Stack, m); Array.Resize(ref Result.Constants, c); return Result; } // mathematical functions private static double Log(double X) { if (X <= 0.0) { return 0.0; /// ComplexInfinity or NonReal } else { return Math.Log(X); } } private static double Sqrt(double X) { if (X < 0.0) { return 0.0; /// NonReal } else { return Math.Sqrt(X); } } private static double Tan(double X) { double c = X / Math.PI; double d = c - Math.Floor(c) - 0.5; double e = Math.Floor(X >= 0.0 ? X : -X) * 1.38462643383279E-16; if (d >= -e & d <= e) { return 0.0; /// ComplexInfinity } else { return Math.Tan(X); } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/Game.cs000066400000000000000000002361771171674032100207600ustar00rootroot00000000000000using System; using OpenBveApi.Colors; using OpenBveApi.Math; namespace OpenBve { internal static class Game { // date and time internal static double SecondsSinceMidnight = 0.0; internal static double StartupTime = 0.0; internal static bool MinimalisticSimulation = false; // fog internal struct Fog { internal float Start; internal float End; internal Color24 Color; internal double TrackPosition; internal Fog(float Start, float End, Color24 Color, double TrackPosition) { this.Start = Start; this.End = End; this.Color = Color; this.TrackPosition = TrackPosition; } } internal static float NoFogStart = 800.0f; // must not be 600 or below internal static float NoFogEnd = 1600.0f; internal static Fog PreviousFog = new Fog(NoFogStart, NoFogEnd, new Color24(128, 128, 128), 0.0); internal static Fog CurrentFog = new Fog(NoFogStart, NoFogEnd, new Color24(128, 128, 128), 0.5); internal static Fog NextFog = new Fog(NoFogStart, NoFogEnd, new Color24(128, 128, 128), 1.0); // route constants internal static string RouteComment = ""; internal static string RouteImage = ""; internal static double RouteAccelerationDueToGravity = 9.80665; internal static double RouteRailGauge = 1.435; internal static double RouteInitialAirPressure = 101325.0; internal static double RouteInitialAirTemperature = 293.15; internal static double RouteInitialElevation = 0.0; internal static double RouteSeaLevelAirPressure = 101325.0; internal static double RouteSeaLevelAirTemperature = 293.15; internal static double[] RouteUnitOfLength = new double[] { 1.0 }; internal const double CoefficientOfGroundFriction = 0.5; internal const double CriticalCollisionSpeedDifference = 8.0; internal const double BrakePipeLeakRate = 500000.0; internal const double MolarMass = 0.0289644; internal const double UniversalGasConstant = 8.31447; internal const double TemperatureLapseRate = -0.0065; internal const double CoefficientOfStiffness = 144117.325646911; // atmospheric functions internal static void CalculateSeaLevelConstants() { RouteSeaLevelAirTemperature = RouteInitialAirTemperature - TemperatureLapseRate * RouteInitialElevation; double Exponent = RouteAccelerationDueToGravity * MolarMass / (UniversalGasConstant * TemperatureLapseRate); double Base = 1.0 + TemperatureLapseRate * RouteInitialElevation / RouteSeaLevelAirTemperature; if (Base >= 0.0) { RouteSeaLevelAirPressure = RouteInitialAirPressure * Math.Pow(Base, Exponent); if (RouteSeaLevelAirPressure < 0.001) RouteSeaLevelAirPressure = 0.001; } else { RouteSeaLevelAirPressure = 0.001; } } internal static double GetAirTemperature(double Elevation) { double x = RouteSeaLevelAirTemperature + TemperatureLapseRate * Elevation; if (x >= 1.0) { return x; } else { return 1.0; } } internal static double GetAirDensity(double AirPressure, double AirTemperature) { double x = AirPressure * MolarMass / (UniversalGasConstant * AirTemperature); if (x >= 0.001) { return x; } else { return 0.001; } } internal static double GetAirPressure(double Elevation, double AirTemperature) { double Exponent = -RouteAccelerationDueToGravity * MolarMass / (UniversalGasConstant * TemperatureLapseRate); double Base = 1.0 + TemperatureLapseRate * Elevation / RouteSeaLevelAirTemperature; if (Base >= 0.0) { double x = RouteSeaLevelAirPressure * Math.Pow(Base, Exponent); if (x >= 0.001) { return x; } else { return 0.001; } } else { return 0.001; } } internal static double GetSpeedOfSound(double AirPressure, double AirTemperature) { double AirDensity = GetAirDensity(AirPressure, AirTemperature); return Math.Sqrt(CoefficientOfStiffness / AirDensity); } internal static double GetSpeedOfSound(double AirDensity) { return Math.Sqrt(CoefficientOfStiffness / AirDensity); } // other trains internal static double[] PrecedingTrainTimeDeltas = new double[] { }; internal static double PrecedingTrainSpeedLimit = double.PositiveInfinity; internal static BogusPretrainInstruction[] BogusPretrainInstructions = new BogusPretrainInstruction[] { }; // startup internal enum TrainStartMode { ServiceBrakesAts = -1, EmergencyBrakesAts = 0, EmergencyBrakesNoAts = 1 } internal static TrainStartMode TrainStart = TrainStartMode.EmergencyBrakesAts; internal static string TrainName = ""; // information internal static double InfoFrameRate = 1.0; internal static string InfoDebugString = ""; internal static int InfoTotalTriangles = 0; internal static int InfoTotalTriangleStrip = 0; internal static int InfoTotalQuadStrip = 0; internal static int InfoTotalQuads = 0; internal static int InfoTotalPolygon = 0; internal static int InfoStaticOpaqueFaceCount = 0; // ================================ // interface type internal enum InterfaceType { Normal, Pause, Menu } internal static InterfaceType CurrentInterface = InterfaceType.Normal; internal enum MenuTag { None, Caption, Back, JumpToStation, ExitToMainMenu, Quit } internal abstract class MenuEntry { internal string Text; internal double Highlight; internal double Alpha; } internal class MenuCaption : MenuEntry { internal MenuCaption(string Text) { this.Text = Text; this.Highlight = 0.0; this.Alpha = 0.0; } } internal class MenuCommand : MenuEntry { internal MenuTag Tag; internal int Data; internal MenuCommand(string Text, MenuTag Tag, int Data) { this.Text = Text; this.Highlight = 0.0; this.Alpha = 0.0; this.Tag = Tag; this.Data = Data; } } internal class MenuSubmenu : MenuEntry { internal MenuEntry[] Entries; internal MenuSubmenu(string Text, MenuEntry[] Entries) { this.Text = Text; this.Highlight = 0.0; this.Alpha = 0.0; this.Entries = Entries; } } internal static MenuEntry[] CurrentMenu = new MenuEntry[] { }; internal static int[] CurrentMenuSelection = new int[] { -1 }; internal static double[] CurrentMenuOffsets = new double[] { double.NegativeInfinity }; internal static void CreateMenu(bool QuitOnly) { if (QuitOnly) { // quit menu only CurrentMenu = new MenuEntry[3]; CurrentMenu[0] = new MenuCaption(Interface.GetInterfaceString("menu_quit_question")); CurrentMenu[1] = new MenuCommand(Interface.GetInterfaceString("menu_quit_no"), MenuTag.Back, 0); CurrentMenu[2] = new MenuCommand(Interface.GetInterfaceString("menu_quit_yes"), MenuTag.Quit, 0); CurrentMenuSelection = new int[] { 1 }; CurrentMenuOffsets = new double[] { double.NegativeInfinity }; CurrentMenu[1].Highlight = 1.0; } else { // full menu int n = 0; for (int i = 0; i < Stations.Length; i++) { if (PlayerStopsAtStation(i) & Stations[i].Stops.Length > 0) { n++; } } MenuEntry[] a = new MenuCommand[n]; n = 0; for (int i = 0; i < Stations.Length; i++) { if (PlayerStopsAtStation(i) & Stations[i].Stops.Length > 0) { a[n] = new MenuCommand(Stations[i].Name, MenuTag.JumpToStation, i); n++; } } if (n != 0) { CurrentMenu = new MenuEntry[4]; CurrentMenu[0] = new MenuCommand(Interface.GetInterfaceString("menu_resume"), MenuTag.Back, 0); CurrentMenu[1] = new MenuSubmenu(Interface.GetInterfaceString("menu_jump"), a); CurrentMenu[2] = new MenuSubmenu(Interface.GetInterfaceString("menu_exit"), new MenuEntry[] { new MenuCaption(Interface.GetInterfaceString("menu_exit_question")), new MenuCommand(Interface.GetInterfaceString("menu_exit_no"), MenuTag.Back, 0), new MenuCommand(Interface.GetInterfaceString("menu_exit_yes"), MenuTag.ExitToMainMenu, 0) }); CurrentMenu[3] = new MenuSubmenu(Interface.GetInterfaceString("menu_quit"), new MenuEntry[] { new MenuCaption(Interface.GetInterfaceString("menu_quit_question")), new MenuCommand(Interface.GetInterfaceString("menu_quit_no"), MenuTag.Back, 0), new MenuCommand(Interface.GetInterfaceString("menu_quit_yes"), MenuTag.Quit, 0) }); } else { CurrentMenu = new MenuEntry[3]; CurrentMenu[0] = new MenuCommand(Interface.GetInterfaceString("menu_resume"), MenuTag.Back, 0); CurrentMenu[1] = new MenuSubmenu(Interface.GetInterfaceString("menu_exit"), new MenuEntry[] { new MenuCaption(Interface.GetInterfaceString("menu_exit_question")), new MenuCommand(Interface.GetInterfaceString("menu_exit_no"), MenuTag.Back, 0), new MenuCommand(Interface.GetInterfaceString("menu_exit_yes"), MenuTag.ExitToMainMenu, 0) }); CurrentMenu[2] = new MenuSubmenu(Interface.GetInterfaceString("menu_quit"), new MenuEntry[] { new MenuCaption(Interface.GetInterfaceString("menu_quit_question")), new MenuCommand(Interface.GetInterfaceString("menu_quit_no"), MenuTag.Back, 0), new MenuCommand(Interface.GetInterfaceString("menu_quit_yes"), MenuTag.Quit, 0) }); } CurrentMenuSelection = new int[] { 0 }; CurrentMenuOffsets = new double[] { double.NegativeInfinity }; CurrentMenu[0].Highlight = 1.0; } } // ================================ internal static void Reset(bool ResetLogs) { // track manager TrackManager.CurrentTrack = new TrackManager.Track(); // train manager TrainManager.Trains = new TrainManager.Train[] { }; // game Interface.ClearMessages(); CurrentInterface = InterfaceType.Normal; RouteComment = ""; RouteImage = ""; RouteAccelerationDueToGravity = 9.80665; RouteRailGauge = 1.435; RouteInitialAirPressure = 101325.0; RouteInitialAirTemperature = 293.15; RouteInitialElevation = 0.0; RouteSeaLevelAirPressure = 101325.0; RouteSeaLevelAirTemperature = 293.15; Stations = new Station[] { }; Sections = new Section[] { }; BufferTrackPositions = new double[] { }; Messages = new Message[] { }; MarkerTextures = new Textures.Texture[] { }; PointsOfInterest = new PointOfInterest[] { }; PrecedingTrainTimeDeltas = new double[] { }; PrecedingTrainSpeedLimit = double.PositiveInfinity; BogusPretrainInstructions = new BogusPretrainInstruction[] { }; TrainName = ""; TrainStart = TrainStartMode.EmergencyBrakesNoAts; NoFogStart = (float)Math.Max(1.33333333333333 * Interface.CurrentOptions.ViewingDistance, 800.0); NoFogEnd = (float)Math.Max(2.66666666666667 * Interface.CurrentOptions.ViewingDistance, 1600.0); PreviousFog = new Fog(NoFogStart, NoFogEnd, new Color24(128, 128, 128), 0.0); CurrentFog = new Fog(NoFogStart, NoFogEnd, new Color24(128, 128, 128), 0.5); NextFog = new Fog(NoFogStart, NoFogEnd, new Color24(128, 128, 128), 1.0); InfoTotalTriangles = 0; InfoTotalTriangleStrip = 0; InfoTotalQuads = 0; InfoTotalQuadStrip = 0; InfoTotalPolygon = 0; InfoStaticOpaqueFaceCount = 0; if (ResetLogs) { LogRouteName = ""; LogTrainName = ""; LogDateTime = DateTime.Now; CurrentScore = new Score(); ScoreMessages = new ScoreMessage[] { }; ScoreLogs = new ScoreLog[64]; ScoreLogCount = 0; BlackBoxEntries = new BlackBoxEntry[256]; BlackBoxEntryCount = 0; BlackBoxNextUpdate = 0.0; } // renderer Renderer.Reset(); } // ================================ // score internal struct Score { internal int Value; internal int Maximum; internal double OpenedDoorsCounter; internal double OverspeedCounter; internal double TopplingCounter; internal bool RedSignal; internal bool Derailed; internal int ArrivalStation; internal int DepartureStation; internal double PassengerTimer; } internal static Score CurrentScore = new Score(); private const double ScoreFactorOpenedDoors = -10.0; private const double ScoreFactorOverspeed = -1.0; private const double ScoreFactorToppling = -10.0; private const double ScoreFactorStationLate = -0.333333333333333; private const double ScoreFactorStationStop = -50.0; private const double ScoreFactorStationDeparture = -1.5; private const int ScoreValueDerailment = -1000; private const int ScoreValueRedSignal = -100; private const int ScoreValueStationPerfectTime = 15; private const int ScoreValueStationPerfectStop = 15; private const int ScoreValuePassengerDiscomfort = -20; internal const int ScoreValueStationArrival = 100; internal static void UpdateScore(double TimeElapsed) { // doors { bool leftopen = false; bool rightopen = false; for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { for (int k = 0; k < TrainManager.PlayerTrain.Cars[j].Specs.Doors.Length; k++) { if (TrainManager.PlayerTrain.Cars[j].Specs.Doors[k].State != 0.0) { if (TrainManager.PlayerTrain.Cars[j].Specs.Doors[k].Direction == -1) { leftopen = true; } else if (TrainManager.PlayerTrain.Cars[j].Specs.Doors[k].Direction == 1) { rightopen = true; } } } } bool bad; if (leftopen | rightopen) { bad = true; int j = TrainManager.PlayerTrain.Station; if (j >= 0) { int p = Game.GetStopIndex(j, TrainManager.PlayerTrain.Cars.Length); if (p >= 0) { if (Math.Abs(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed) < 0.1) { if (leftopen == Stations[j].OpenLeftDoors & rightopen == Stations[j].OpenRightDoors) { bad = false; } } } } } else { bad = false; } if (bad) { CurrentScore.OpenedDoorsCounter += (Math.Abs(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed) + 0.25) * TimeElapsed; } else if (CurrentScore.OpenedDoorsCounter != 0.0) { int x = (int)Math.Ceiling(ScoreFactorOpenedDoors * CurrentScore.OpenedDoorsCounter); CurrentScore.Value += x; if (x != 0) { AddScore(x, ScoreTextToken.DoorsOpened, 5.0); } CurrentScore.OpenedDoorsCounter = 0.0; } } // overspeed double nr = TrainManager.PlayerTrain.CurrentRouteLimit; double ns = TrainManager.PlayerTrain.CurrentSectionLimit; double n = nr < ns ? nr : ns; double a = Math.Abs(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed) - 0.277777777777778; if (a > n) { CurrentScore.OverspeedCounter += (a - n) * TimeElapsed; } else if (CurrentScore.OverspeedCounter != 0.0) { int x = (int)Math.Ceiling(ScoreFactorOverspeed * CurrentScore.OverspeedCounter); CurrentScore.Value += x; if (x != 0) { AddScore(x, ScoreTextToken.Overspeed, 5.0); } CurrentScore.OverspeedCounter = 0.0; } // toppling { bool q = false; for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { if (TrainManager.PlayerTrain.Cars[j].Topples) { q = true; break; } } if (q) { CurrentScore.TopplingCounter += TimeElapsed; } else if (CurrentScore.TopplingCounter != 0.0) { int x = (int)Math.Ceiling(ScoreFactorToppling * CurrentScore.TopplingCounter); CurrentScore.Value += x; if (x != 0) { AddScore(x, ScoreTextToken.Toppling, 5.0); } CurrentScore.TopplingCounter = 0.0; } } // derailment if (!CurrentScore.Derailed) { bool q = false; for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { if (TrainManager.PlayerTrain.Cars[j].Derailed) { q = true; break; } } if (q) { int x = ScoreValueDerailment; if (CurrentScore.Value > 0) x -= CurrentScore.Value; CurrentScore.Value += x; if (x != 0) { AddScore(x, ScoreTextToken.Derailed, 5.0); } CurrentScore.Derailed = true; } } // red signal { if (TrainManager.PlayerTrain.CurrentSectionLimit == 0.0) { if (!CurrentScore.RedSignal) { int x = ScoreValueRedSignal; CurrentScore.Value += x; if (x != 0) { AddScore(x, ScoreTextToken.PassedRedSignal, 5.0); } CurrentScore.RedSignal = true; } } else { CurrentScore.RedSignal = false; } } // arrival { int j = TrainManager.PlayerTrain.Station; if (j >= 0 & j < Stations.Length) { if (j >= CurrentScore.ArrivalStation & TrainManager.PlayerTrain.StationState == TrainManager.TrainStopState.Boarding) { if (j == 0 || Stations[j - 1].StationType != StationType.ChangeEnds) { // arrival int xa = ScoreValueStationArrival; CurrentScore.Value += xa; if (xa != 0) { AddScore(xa, ScoreTextToken.ArrivedAtStation, 10.0); } // early/late int xb; if (Stations[j].ArrivalTime >= 0) { double d = SecondsSinceMidnight - Stations[j].ArrivalTime; if (d >= -5.0 & d <= 0.0) { xb = ScoreValueStationPerfectTime; CurrentScore.Value += xb; AddScore(xb, ScoreTextToken.PerfectTimeBonus, 10.0); } else if (d > 0.0) { xb = (int)Math.Ceiling(ScoreFactorStationLate * (d - 1.0)); CurrentScore.Value += xb; if (xb != 0) { AddScore(xb, ScoreTextToken.Late, 10.0); } } else { xb = 0; } } else { xb = 0; } // position int xc; int p = Game.GetStopIndex(j, TrainManager.PlayerTrain.Cars.Length); if (p >= 0) { double d = TrainManager.PlayerTrain.StationDistanceToStopPoint; double r; if (d >= 0) { double t = Stations[j].Stops[p].BackwardTolerance; r = (Math.Sqrt(d * d + 1.0) - 1.0) / (Math.Sqrt(t * t + 1.0) - 1.0); } else { double t = Stations[j].Stops[p].ForwardTolerance; r = (Math.Sqrt(d * d + 1.0) - 1.0) / (Math.Sqrt(t * t + 1.0) - 1.0); } if (r < 0.01) { xc = ScoreValueStationPerfectStop; CurrentScore.Value += xc; AddScore(xc, ScoreTextToken.PerfectStopBonus, 10.0); } else { if (r > 1.0) r = 1.0; r = (r - 0.01) * 1.01010101010101; xc = (int)Math.Ceiling(ScoreFactorStationStop * r); CurrentScore.Value += xc; if (xc != 0) { AddScore(xc, ScoreTextToken.Stop, 10.0); } } } else { xc = 0; } // sum if (Interface.CurrentOptions.GameMode == Interface.GameMode.Arcade) { int xs = xa + xb + xc; AddScore("", 10.0); AddScore(xs, ScoreTextToken.Total, 10.0, false); AddScore(" ", 10.0); } // evaluation if (Interface.CurrentOptions.GameMode == Interface.GameMode.Arcade) { if (Stations[j].StationType == StationType.Terminal) { double y = (double)CurrentScore.Value / (double)CurrentScore.Maximum; if (y < 0.0) y = 0.0; if (y > 1.0) y = 1.0; int k = (int)Math.Floor(y * (double)Interface.RatingsCount); if (k >= Interface.RatingsCount) k = Interface.RatingsCount - 1; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; AddScore(Interface.GetInterfaceString("score_rating"), 20.0); AddScore(Interface.GetInterfaceString("rating_" + k.ToString(Culture)) + " (" + (100.0 * y).ToString("0.00", Culture) + "%)", 20.0); } } } // finalize CurrentScore.DepartureStation = j; CurrentScore.ArrivalStation = j + 1; } } } // departure { int j = TrainManager.PlayerTrain.Station; if (j >= 0 & j < Stations.Length & j == CurrentScore.DepartureStation) { bool q; if (Stations[j].OpenLeftDoors | Stations[j].OpenRightDoors) { q = TrainManager.PlayerTrain.StationState == TrainManager.TrainStopState.Completed; } else { q = TrainManager.PlayerTrain.StationState != TrainManager.TrainStopState.Pending & (TrainManager.PlayerTrain.Specs.CurrentAverageSpeed < -1.5 | TrainManager.PlayerTrain.Specs.CurrentAverageSpeed > 1.5); } if (q) { double r = TrainManager.PlayerTrain.StationDepartureTime - SecondsSinceMidnight; if (r > 0.0) { int x = (int)Math.Ceiling(ScoreFactorStationDeparture * r); CurrentScore.Value += x; if (x != 0) { AddScore(x, ScoreTextToken.PrematureDeparture, 5.0); } } CurrentScore.DepartureStation = -1; } } } // passengers if (TrainManager.PlayerTrain.Passengers.FallenOver & CurrentScore.PassengerTimer == 0.0) { int x = ScoreValuePassengerDiscomfort; CurrentScore.Value += x; AddScore(x, ScoreTextToken.PassengerDiscomfort, 5.0); CurrentScore.PassengerTimer = 5.0; } else { CurrentScore.PassengerTimer -= TimeElapsed; if (CurrentScore.PassengerTimer <= 0.0) { if (TrainManager.PlayerTrain.Passengers.FallenOver) { CurrentScore.PassengerTimer = 5.0; } else { CurrentScore.PassengerTimer = 0.0; } } } } // score messages and logs internal enum ScoreTextToken : short { Invalid = 0, Overspeed = 1, PassedRedSignal = 2, Toppling = 3, Derailed = 4, PassengerDiscomfort = 5, DoorsOpened = 6, ArrivedAtStation = 7, PerfectTimeBonus = 8, Late = 9, PerfectStopBonus = 10, Stop = 11, PrematureDeparture = 12, Total = 13 } internal struct ScoreMessage { internal int Value; internal string Text; internal double Timeout; internal MessageColor Color; internal World.Vector2D RendererPosition; internal double RendererAlpha; } internal struct ScoreLog { internal int Value; internal ScoreTextToken TextToken; internal double Position; internal double Time; } internal static ScoreLog[] ScoreLogs = new ScoreLog[64]; internal static int ScoreLogCount = 0; internal static ScoreMessage[] ScoreMessages = new ScoreMessage[] { }; internal static World.Vector2D ScoreMessagesRendererSize = new World.Vector2D(16.0, 16.0); internal static string LogRouteName = ""; internal static string LogTrainName = ""; internal static DateTime LogDateTime = DateTime.Now; private static void AddScore(int Value, ScoreTextToken TextToken, double Duration) { AddScore(Value, TextToken, Duration, true); } private static void AddScore(int Value, ScoreTextToken TextToken, double Duration, bool Count) { if (Interface.CurrentOptions.GameMode == Interface.GameMode.Arcade) { int n = ScoreMessages.Length; Array.Resize(ref ScoreMessages, n + 1); ScoreMessages[n].Value = Value; ScoreMessages[n].Text = Interface.GetScoreText(TextToken) + ": " + Value.ToString(System.Globalization.CultureInfo.InvariantCulture); ScoreMessages[n].Timeout = SecondsSinceMidnight + Duration; ScoreMessages[n].RendererPosition = new World.Vector2D(0.0, 0.0); ScoreMessages[n].RendererAlpha = 0.0; if (Value < 0.0) { ScoreMessages[n].Color = MessageColor.Red; } else if (Value > 0.0) { ScoreMessages[n].Color = MessageColor.Green; } else { ScoreMessages[n].Color = MessageColor.White; } } if (Value != 0 & Count) { if (ScoreLogCount == ScoreLogs.Length) { Array.Resize(ref ScoreLogs, ScoreLogs.Length << 1); } ScoreLogs[ScoreLogCount].Value = Value; ScoreLogs[ScoreLogCount].TextToken = TextToken; ScoreLogs[ScoreLogCount].Position = TrainManager.PlayerTrain.Cars[0].FrontAxle.Follower.TrackPosition; ScoreLogs[ScoreLogCount].Time = SecondsSinceMidnight; ScoreLogCount++; } } private static void AddScore(string Text, double Duration) { if (Interface.CurrentOptions.GameMode == Interface.GameMode.Arcade) { int n = ScoreMessages.Length; Array.Resize(ref ScoreMessages, n + 1); ScoreMessages[n].Value = 0; ScoreMessages[n].Text = Text.Length != 0 ? Text : "══════════"; ScoreMessages[n].Timeout = SecondsSinceMidnight + Duration; ScoreMessages[n].RendererPosition = new World.Vector2D(0.0, 0.0); ScoreMessages[n].RendererAlpha = 0.0; ScoreMessages[n].Color = MessageColor.White; } } internal static void UpdateScoreMessages(double TimeElapsed) { if (Interface.CurrentOptions.GameMode == Interface.GameMode.Arcade) { for (int i = 0; i < ScoreMessages.Length; i++) { if (SecondsSinceMidnight >= ScoreMessages[i].Timeout & ScoreMessages[i].RendererAlpha == 0.0) { for (int j = i; j < ScoreMessages.Length - 1; j++) { ScoreMessages[j] = ScoreMessages[j + 1]; } Array.Resize(ref ScoreMessages, ScoreMessages.Length - 1); i--; } } } } // ================================ // black box internal enum BlackBoxEventToken : short { None = 0 } internal enum BlackBoxPower : short { PowerNull = 0 } internal enum BlackBoxBrake : short { BrakeNull = 0, Emergency = -1, HoldBrake = -2, Release = -3, Lap = -4, Service = -5 } internal struct BlackBoxEntry { internal double Time; internal double Position; internal float Speed; internal float Acceleration; internal short ReverserDriver; internal short ReverserSafety; internal BlackBoxPower PowerDriver; internal BlackBoxPower PowerSafety; internal BlackBoxBrake BrakeDriver; internal BlackBoxBrake BrakeSafety; internal BlackBoxEventToken EventToken; } internal static BlackBoxEntry[] BlackBoxEntries = new BlackBoxEntry[256]; internal static int BlackBoxEntryCount = 0; private static double BlackBoxNextUpdate = 0.0; internal static void UpdateBlackBox() { if (SecondsSinceMidnight >= BlackBoxNextUpdate) { AddBlackBoxEntry(BlackBoxEventToken.None); BlackBoxNextUpdate = SecondsSinceMidnight + 1.0; } } internal static void AddBlackBoxEntry(BlackBoxEventToken EventToken) { if (Interface.CurrentOptions.BlackBox) { if (BlackBoxEntryCount >= BlackBoxEntries.Length) { Array.Resize(ref BlackBoxEntries, BlackBoxEntries.Length << 1); } int d = TrainManager.PlayerTrain.DriverCar; BlackBoxEntries[BlackBoxEntryCount].Time = SecondsSinceMidnight; BlackBoxEntries[BlackBoxEntryCount].Position = TrainManager.PlayerTrain.Cars[0].FrontAxle.Follower.TrackPosition; BlackBoxEntries[BlackBoxEntryCount].Speed = (float)TrainManager.PlayerTrain.Specs.CurrentAverageSpeed; BlackBoxEntries[BlackBoxEntryCount].Acceleration = (float)TrainManager.PlayerTrain.Specs.CurrentAverageAcceleration; BlackBoxEntries[BlackBoxEntryCount].ReverserDriver = (short)TrainManager.PlayerTrain.Specs.CurrentReverser.Driver; BlackBoxEntries[BlackBoxEntryCount].ReverserSafety = (short)TrainManager.PlayerTrain.Specs.CurrentReverser.Actual; BlackBoxEntries[BlackBoxEntryCount].PowerDriver = (BlackBoxPower)TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver; BlackBoxEntries[BlackBoxEntryCount].PowerSafety = (BlackBoxPower)TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Safety; if (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver) { BlackBoxEntries[BlackBoxEntryCount].BrakeDriver = BlackBoxBrake.Emergency; } else if (TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { BlackBoxEntries[BlackBoxEntryCount].BrakeDriver = BlackBoxBrake.HoldBrake; } else if (TrainManager.PlayerTrain.Cars[d].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { switch (TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver) { case TrainManager.AirBrakeHandleState.Release: BlackBoxEntries[BlackBoxEntryCount].BrakeDriver = BlackBoxBrake.Release; break; case TrainManager.AirBrakeHandleState.Lap: BlackBoxEntries[BlackBoxEntryCount].BrakeDriver = BlackBoxBrake.Lap; break; case TrainManager.AirBrakeHandleState.Service: BlackBoxEntries[BlackBoxEntryCount].BrakeDriver = BlackBoxBrake.Service; break; default: BlackBoxEntries[BlackBoxEntryCount].BrakeDriver = BlackBoxBrake.Emergency; break; } } else { BlackBoxEntries[BlackBoxEntryCount].BrakeDriver = (BlackBoxBrake)TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver; } if (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Safety) { BlackBoxEntries[BlackBoxEntryCount].BrakeSafety = BlackBoxBrake.Emergency; } else if (TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Actual) { BlackBoxEntries[BlackBoxEntryCount].BrakeSafety = BlackBoxBrake.HoldBrake; } else if (TrainManager.PlayerTrain.Cars[d].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { switch (TrainManager.PlayerTrain.Specs.AirBrake.Handle.Safety) { case TrainManager.AirBrakeHandleState.Release: BlackBoxEntries[BlackBoxEntryCount].BrakeSafety = BlackBoxBrake.Release; break; case TrainManager.AirBrakeHandleState.Lap: BlackBoxEntries[BlackBoxEntryCount].BrakeSafety = BlackBoxBrake.Lap; break; case TrainManager.AirBrakeHandleState.Service: BlackBoxEntries[BlackBoxEntryCount].BrakeSafety = BlackBoxBrake.Service; break; default: BlackBoxEntries[BlackBoxEntryCount].BrakeSafety = BlackBoxBrake.Emergency; break; } } else { BlackBoxEntries[BlackBoxEntryCount].BrakeSafety = (BlackBoxBrake)TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Safety; } BlackBoxEntries[BlackBoxEntryCount].EventToken = EventToken; BlackBoxEntryCount++; } } // ================================ // stations internal struct StationStop { internal double TrackPosition; internal double ForwardTolerance; internal double BackwardTolerance; internal int Cars; } internal enum SafetySystem { Any = -1, Ats = 0, Atc = 1 } internal enum StationStopMode { AllStop = 0, AllPass = 1, PlayerStop = 2, PlayerPass = 3 } internal enum StationType { Normal = 0, ChangeEnds = 1, Terminal = 2 } internal struct Station { internal string Name; internal double ArrivalTime; internal Sounds.SoundBuffer ArrivalSoundBuffer; internal double DepartureTime; internal Sounds.SoundBuffer DepartureSoundBuffer; internal double StopTime; internal Vector3 SoundOrigin; internal StationStopMode StopMode; internal StationType StationType; internal bool ForceStopSignal; internal bool OpenLeftDoors; internal bool OpenRightDoors; internal SafetySystem SafetySystem; internal StationStop[] Stops; internal double PassengerRatio; internal Textures.Texture TimetableDaytimeTexture; internal Textures.Texture TimetableNighttimeTexture; internal double DefaultTrackPosition; } internal static Station[] Stations = new Station[] { }; internal static int GetStopIndex(int StationIndex, int Cars) { int j = -1; for (int i = Stations[StationIndex].Stops.Length - 1; i >= 0; i--) { if (Cars <= Stations[StationIndex].Stops[i].Cars | Stations[StationIndex].Stops[i].Cars == 0) { j = i; } } if (j == -1) { return Stations[StationIndex].Stops.Length - 1; } else return j; } /// Indicates whether the player's train stops at a station. internal static bool PlayerStopsAtStation(int StationIndex) { return Stations[StationIndex].StopMode == StationStopMode.AllStop | Stations[StationIndex].StopMode == StationStopMode.PlayerStop; } /// Indicates whether a train stops at a station. internal static bool StopsAtStation(int StationIndex, TrainManager.Train Train) { if (Train == TrainManager.PlayerTrain) { return Stations[StationIndex].StopMode == StationStopMode.AllStop | Stations[StationIndex].StopMode == StationStopMode.PlayerStop; } else { return Stations[StationIndex].StopMode == StationStopMode.AllStop | Stations[StationIndex].StopMode == StationStopMode.PlayerPass; } } // ================================ // sections internal enum SectionType { ValueBased, IndexBased } internal struct SectionAspect { internal int Number; internal double Speed; internal SectionAspect(int Number, double Speed) { this.Number = Number; this.Speed = Speed; } } internal struct Section { internal int PreviousSection; internal int NextSection; internal TrainManager.Train[] Trains; internal bool TrainReachedStopPoint; internal int StationIndex; internal bool Invisible; internal int[] SignalIndices; internal double TrackPosition; internal SectionType Type; internal SectionAspect[] Aspects; internal int CurrentAspect; internal int FreeSections; internal void Enter(TrainManager.Train Train) { int n = this.Trains.Length; for (int i = 0; i < n; i++) { if (this.Trains[i] == Train) return; } Array.Resize(ref this.Trains, n + 1); this.Trains[n] = Train; } internal void Leave(TrainManager.Train Train) { int n = this.Trains.Length; for (int i = 0; i < n; i++) { if (this.Trains[i] == Train) { for (int j = i; j < n - 1; j++) { this.Trains[j] = this.Trains[j + 1]; } Array.Resize(ref this.Trains, n - 1); return; } } } internal bool Exists(TrainManager.Train Train) { for (int i = 0; i < this.Trains.Length; i++) { if (this.Trains[i] == Train) return true; } return false; } /// Checks whether the section is free, disregarding the specified train. /// The train to disregard. /// Whether the section is free, disregarding the specified train. internal bool IsFree(TrainManager.Train train) { for (int i = 0; i < this.Trains.Length; i++) { if (this.Trains[i] != train & (this.Trains[i].State == TrainManager.TrainState.Available | this.Trains[i].State == TrainManager.TrainState.Bogus)) { return false; } } return true; } internal bool IsFree() { for (int i = 0; i < this.Trains.Length; i++) { if (this.Trains[i].State == TrainManager.TrainState.Available | this.Trains[i].State == TrainManager.TrainState.Bogus) { return false; } } return true; } internal TrainManager.Train GetFirstTrain(bool AllowBogusTrain) { for (int i = 0; i < this.Trains.Length; i++) { if (this.Trains[i].State == TrainManager.TrainState.Available) { return this.Trains[i]; } else if (AllowBogusTrain & this.Trains[i].State == TrainManager.TrainState.Bogus) { return this.Trains[i]; } } return null; } } internal static Section[] Sections = new Section[] { }; internal static void UpdateAllSections() { if (Sections.Length != 0) { UpdateSection(Sections.Length - 1); } } internal static void UpdateSection(int SectionIndex) { // preparations int zeroaspect; bool settored = false; if (Sections[SectionIndex].Type == SectionType.ValueBased) { // value-based zeroaspect = 0; for (int i = 1; i < Sections[SectionIndex].Aspects.Length; i++) { if (Sections[SectionIndex].Aspects[i].Number < Sections[SectionIndex].Aspects[zeroaspect].Number) { zeroaspect = i; } } } else { // index-based zeroaspect = 0; } // hold station departure signal at red int d = Sections[SectionIndex].StationIndex; if (d >= 0) { // look for train in previous blocks int l = Sections[SectionIndex].PreviousSection; TrainManager.Train train = null; while (true) { if (l >= 0) { train = Sections[l].GetFirstTrain(false); if (train != null) { break; } else { l = Sections[l].PreviousSection; } } else { break; } } if (train == null) { double b = -double.MaxValue; for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i].State == TrainManager.TrainState.Available) { if (TrainManager.Trains[i].TimetableDelta > b) { b = TrainManager.Trains[i].TimetableDelta; train = TrainManager.Trains[i]; } } } } // set to red where applicable if (train != null) { if (!Sections[SectionIndex].TrainReachedStopPoint) { if (train.Station == d) { int c = GetStopIndex(d, train.Cars.Length); if (c >= 0) { double p0 = train.Cars[0].FrontAxle.Follower.TrackPosition - train.Cars[0].FrontAxlePosition + 0.5 * train.Cars[0].Length; double p1 = Stations[d].Stops[c].TrackPosition - Stations[d].Stops[c].BackwardTolerance; if (p0 >= p1) { Sections[SectionIndex].TrainReachedStopPoint = true; } } else { Sections[SectionIndex].TrainReachedStopPoint = true; } } } double t = -15.0; if (Stations[d].DepartureTime >= 0.0) { t = Stations[d].DepartureTime - 15.0; } else if (Stations[d].ArrivalTime >= 0.0) { t = Stations[d].ArrivalTime; } if (train == TrainManager.PlayerTrain & Stations[d].StationType != StationType.Normal & Stations[d].DepartureTime < 0.0) { settored = true; } else if (t >= 0.0 & SecondsSinceMidnight < t - train.TimetableDelta) { settored = true; } else if (!Sections[SectionIndex].TrainReachedStopPoint) { settored = true; } } else if (Stations[d].StationType != StationType.Normal) { settored = true; } } // train in block if (!Sections[SectionIndex].IsFree()) { settored = true; } // free sections int newaspect = -1; if (settored) { Sections[SectionIndex].FreeSections = 0; newaspect = zeroaspect; } else { int n = Sections[SectionIndex].NextSection; if (n >= 0) { if (Sections[n].FreeSections == -1) { Sections[SectionIndex].FreeSections = -1; } else { Sections[SectionIndex].FreeSections = Sections[n].FreeSections + 1; } } else { Sections[SectionIndex].FreeSections = -1; } } // change aspect if (newaspect == -1) { if (Sections[SectionIndex].Type == SectionType.ValueBased) { // value-based int n = Sections[SectionIndex].NextSection; int a = Sections[SectionIndex].Aspects[Sections[SectionIndex].Aspects.Length - 1].Number; if (n >= 0 && Sections[n].CurrentAspect >= 0) { a = Sections[n].Aspects[Sections[n].CurrentAspect].Number; } for (int i = Sections[SectionIndex].Aspects.Length - 1; i >= 0; i--) { if (Sections[SectionIndex].Aspects[i].Number > a) { newaspect = i; } } if (newaspect == -1) { newaspect = Sections[SectionIndex].Aspects.Length - 1; } } else { // index-based if (Sections[SectionIndex].FreeSections >= 0 & Sections[SectionIndex].FreeSections < Sections[SectionIndex].Aspects.Length) { newaspect = Sections[SectionIndex].FreeSections; } else { newaspect = Sections[SectionIndex].Aspects.Length - 1; } } } // apply new aspect Sections[SectionIndex].CurrentAspect = newaspect; // update previous section if (Sections[SectionIndex].PreviousSection >= 0) { UpdateSection(Sections[SectionIndex].PreviousSection); } } // get plugin signal /// Gets the signal data for a plugin. /// The train. /// The absolute section index, referencing Game.Sections[]. /// The signal data. internal static OpenBveApi.Runtime.SignalData GetPluginSignal(TrainManager.Train train, int section) { if (Sections[section].Exists(train)) { int aspect; if (Sections[section].IsFree(train)) { if (Sections[section].Type == SectionType.IndexBased) { if (section + 1 < Sections.Length) { int value = Sections[section + 1].FreeSections; if (value == -1) { value = Sections[section].Aspects.Length - 1; } else { value++; if (value >= Sections[section].Aspects.Length) { value = Sections[section].Aspects.Length - 1; } if (value < 0) { value = 0; } } aspect = Sections[section].Aspects[value].Number; } else { aspect = Sections[section].Aspects[Sections[section].Aspects.Length - 1].Number; } } else { aspect = Sections[section].Aspects[Sections[section].Aspects.Length - 1].Number; if (section < Sections.Length - 1) { int value = Sections[section + 1].Aspects[Sections[section + 1].CurrentAspect].Number; for (int i = 0; i < Sections[section].Aspects.Length; i++) { if (Sections[section].Aspects[i].Number > value) { aspect = Sections[section].Aspects[i].Number; break; } } } } } else { aspect = Sections[section].Aspects[Sections[section].CurrentAspect].Number; } double position = train.Cars[0].FrontAxle.Follower.TrackPosition - train.Cars[0].FrontAxlePosition + 0.5 * train.Cars[0].Length; double distance = Sections[section].TrackPosition - position; return new OpenBveApi.Runtime.SignalData(aspect, distance); } else { int aspect = Sections[section].Aspects[Sections[section].CurrentAspect].Number; double position = train.Cars[0].FrontAxle.Follower.TrackPosition - train.Cars[0].FrontAxlePosition + 0.5 * train.Cars[0].Length; double distance = Sections[section].TrackPosition - position; return new OpenBveApi.Runtime.SignalData(aspect, distance); } } // update plugin sections /// Updates the plugin to inform about sections. /// The train. internal static void UpdatePluginSections(TrainManager.Train train) { if (train.Plugin != null) { OpenBveApi.Runtime.SignalData[] data = new OpenBveApi.Runtime.SignalData[16]; int count = 0; int start = train.CurrentSectionIndex >= 0 ? train.CurrentSectionIndex : 0; for (int i = start; i < Sections.Length; i++) { OpenBveApi.Runtime.SignalData signal = GetPluginSignal(train, i); if (data.Length == count) { Array.Resize(ref data, data.Length << 1); } data[count] = signal; count++; if (signal.Aspect == 0 | count == 16) { break; } } Array.Resize(ref data, count); train.Plugin.UpdateSignals(data); } } // buffers internal static double[] BufferTrackPositions = new double[] { }; // ================================ // ai internal abstract class GeneralAI { internal abstract void Trigger(TrainManager.Train Train, double TimeElapsed); } // simple human driver internal class SimpleHumanDriverAI : GeneralAI { // members private double TimeLastProcessed; private double CurrentInterval; private bool BrakeMode; private double CurrentSpeedFactor; private double PersonalitySpeedFactor; private int PowerNotchAtWhichWheelSlipIsObserved; private int LastStation; // functions internal SimpleHumanDriverAI(TrainManager.Train Train) { this.TimeLastProcessed = 0.0; this.CurrentInterval = 1.0; this.BrakeMode = false; this.PersonalitySpeedFactor = 0.90 + 0.10 * Program.RandomNumberGenerator.NextDouble(); this.CurrentSpeedFactor = this.PersonalitySpeedFactor; this.PowerNotchAtWhichWheelSlipIsObserved = Train.Specs.MaximumPowerNotch + 1; if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Boarding) { this.LastStation = Train.Station; } else { this.LastStation = -1; } } private OpenBveApi.Runtime.AIResponse PerformPlugin(TrainManager.Train Train) { OpenBveApi.Runtime.AIResponse response = Train.Plugin.UpdateAI(); if (response == OpenBveApi.Runtime.AIResponse.Short) { CurrentInterval = 0.2 + 0.1 * Program.RandomNumberGenerator.NextDouble(); } else if (response == OpenBveApi.Runtime.AIResponse.Medium) { CurrentInterval = 0.4 + 0.2 * Program.RandomNumberGenerator.NextDouble(); } else if (response == OpenBveApi.Runtime.AIResponse.Long) { CurrentInterval = 0.8 + 0.4 * Program.RandomNumberGenerator.NextDouble(); } return response; } private void PerformDefault(TrainManager.Train Train) { // personality double spd = Train.Specs.CurrentAverageSpeed; if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Boarding) { if (Train.Station != this.LastStation) { this.LastStation = Train.Station; double time; if (Stations[Train.Station].ArrivalTime >= 0.0) { time = Stations[Train.Station].ArrivalTime - Train.TimetableDelta; } else if (Stations[Train.Station].DepartureTime >= 0.0) { time = Stations[Train.Station].DepartureTime - Train.TimetableDelta; if (time > SecondsSinceMidnight) { time -= Stations[Train.Station].StopTime; if (time > SecondsSinceMidnight) { time = double.MinValue; } } } else { time = double.MinValue; } if (time != double.MinValue) { const double largeThreshold = 30.0; const double largeChangeFactor = 0.0025; const double smallThreshold = 15.0; const double smallChange = 0.05; double diff = SecondsSinceMidnight - time; if (diff < -largeThreshold) { /* The AI is too fast. Decrease the preferred speed. */ this.CurrentSpeedFactor -= largeChangeFactor * (-diff - largeThreshold); if (this.CurrentSpeedFactor < 0.7) { this.CurrentSpeedFactor = 0.7; } } else if (diff > largeThreshold) { /* The AI is too slow. Increase the preferred speed. */ this.CurrentSpeedFactor += largeChangeFactor * (diff - largeThreshold); if (this.CurrentSpeedFactor > 1.1) { this.CurrentSpeedFactor = 1.1; } } else if (Math.Abs(diff) < smallThreshold) { /* The AI is at about the right speed. Change the preferred speed toward the personality default. */ if (this.CurrentSpeedFactor < this.PersonalitySpeedFactor) { this.CurrentSpeedFactor += smallChange; if (this.CurrentSpeedFactor > this.PersonalitySpeedFactor) { this.CurrentSpeedFactor = this.PersonalitySpeedFactor; } } else if (this.CurrentSpeedFactor > this.PersonalitySpeedFactor) { this.CurrentSpeedFactor -= smallChange; if (this.CurrentSpeedFactor < this.PersonalitySpeedFactor) { this.CurrentSpeedFactor = this.PersonalitySpeedFactor; } } } } } } // door states bool doorsopen = false; for (int i = 0; i < Train.Cars.Length; i++) { for (int j = 0; j < Train.Cars[i].Specs.Doors.Length; j++) { if (Train.Cars[i].Specs.Doors[j].State != 0.0) { doorsopen = true; break; } if (doorsopen) break; } } // do the ai Train.Specs.CurrentConstSpeed = false; TrainManager.ApplyHoldBrake(Train, false); int stopIndex = Train.Station >= 0 ? GetStopIndex(Train.Station, Train.Cars.Length) : -1; if (Train.CurrentSectionLimit == 0.0) { // passing red signal TrainManager.ApplyEmergencyBrake(Train); TrainManager.ApplyNotch(Train, -1, true, 1, true); CurrentInterval = 0.5; } else if (doorsopen | Train.StationState == TrainManager.TrainStopState.Boarding) { // door opened or boarding at station this.PowerNotchAtWhichWheelSlipIsObserved = Train.Specs.MaximumPowerNotch + 1; if (Train.Station >= 0 && Stations[Train.Station].StationType != StationType.Normal && Train == TrainManager.PlayerTrain) { // player's terminal station TrainManager.ApplyReverser(Train, 0, false); TrainManager.ApplyNotch(Train, -1, true, 1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Service); TrainManager.ApplyEmergencyBrake(Train); CurrentInterval = 1.0; } else { CurrentInterval = 1.0; TrainManager.ApplyNotch(Train, -1, true, 0, true); if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { if (Train.Cars[Train.DriverCar].Specs.AirBrake.BrakeCylinderCurrentPressure < 0.3 * Train.Cars[Train.DriverCar].Specs.AirBrake.BrakeCylinderServiceMaximumPressure) { TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Service); } else if (Train.Cars[Train.DriverCar].Specs.AirBrake.BrakeCylinderCurrentPressure > 0.9 * Train.Cars[Train.DriverCar].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure) { TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Release); } else { TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Lap); } } else { int b; if (Math.Abs(spd) < 0.02) { b = (int)Math.Ceiling(0.5 * (double)Train.Specs.MaximumBrakeNotch); CurrentInterval = 0.3; } else { b = Train.Specs.MaximumBrakeNotch; } if (Train.Specs.CurrentBrakeNotch.Driver < b) { TrainManager.ApplyNotch(Train, 0, true, 1, true); } else if (Train.Specs.CurrentBrakeNotch.Driver > b) { TrainManager.ApplyNotch(Train, 0, true, -1, true); } } TrainManager.UnapplyEmergencyBrake(Train); if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Completed) { // ready for departure - close doors if (Train.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic) { TrainManager.CloseTrainDoors(Train, true, true); } } else if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Boarding) { } else { // not at station - close doors if (Train.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic) { TrainManager.CloseTrainDoors(Train, true, true); } } } } else if (Train.Station >= 0 && stopIndex >= 0 && Train.StationDistanceToStopPoint < Stations[Train.Station].Stops[stopIndex].BackwardTolerance && (StopsAtStation(Train.Station, Train) & (Stations[Train.Station].OpenLeftDoors | Stations[Train.Station].OpenRightDoors) & Math.Abs(Train.Specs.CurrentAverageSpeed) < 0.25 & Train.StationState == TrainManager.TrainStopState.Pending)) { // arrived at station - open doors if (Train.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic) { TrainManager.OpenTrainDoors(Train, Stations[Train.Station].OpenLeftDoors, Stations[Train.Station].OpenRightDoors); } CurrentInterval = 1.0; } else if (Train.Station >= 0 && Stations[Train.Station].StationType != StationType.Normal && Train == TrainManager.PlayerTrain && Train.StationDistanceToStopPoint < Stations[Train.Station].Stops[stopIndex].BackwardTolerance && -Train.StationDistanceToStopPoint < Stations[Train.Station].Stops[stopIndex].ForwardTolerance && Math.Abs(Train.Specs.CurrentAverageSpeed) < 0.25) { // player's terminal station (not boarding any longer) TrainManager.ApplyReverser(Train, 0, false); TrainManager.ApplyNotch(Train, -1, true, 1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Service); TrainManager.ApplyEmergencyBrake(Train); CurrentInterval = 10.0; } else { // drive TrainManager.ApplyReverser(Train, 1, false); if (Train.Cars[Train.DriverCar].FrontAxle.CurrentWheelSlip | Train.Cars[Train.DriverCar].RearAxle.CurrentWheelSlip) { // react to wheel slip if (Train.Specs.CurrentPowerNotch.Driver > 1) { this.PowerNotchAtWhichWheelSlipIsObserved = Train.Specs.CurrentPowerNotch.Driver; TrainManager.ApplyNotch(Train, -1, true, -1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Release); this.CurrentInterval = 2.5; return; } } // initialize double acc = Train.Specs.CurrentAverageAcceleration; double lim = PrecedingTrainSpeedLimit * 1.2; if (Train.CurrentRouteLimit < lim) { lim = Train.CurrentRouteLimit; } if (Train.CurrentSectionLimit < lim) { lim = Train.CurrentSectionLimit; } double powerstart, powerend, brakestart; if (double.IsPositiveInfinity(lim)) { powerstart = lim; powerend = lim; brakestart = lim; } else { lim *= this.CurrentSpeedFactor; if (spd < 8.0) { powerstart = 0.75 * lim; powerend = 0.95 * lim; } else { powerstart = lim - 2.5; powerend = lim - 1.5; } if (this.BrakeMode) { brakestart = powerend; } else { brakestart = lim + 0.5; } } double dec = 0.0; double decelerationCruise; /* power below this deceleration, cruise above */ double decelerationStart; /* brake above this deceleration, cruise below */ double decelerationStep; /* the deceleration step per brake notch */ double BrakeDeceleration = Train.Cars[Train.DriverCar].Specs.BrakeDecelerationAtServiceMaximumPressure; for (int i = 0; i < Train.Cars.Length; i++) { if (Train.Cars[i].Specs.IsMotorCar) { if (Train.Cars[Train.DriverCar].Specs.MotorDeceleration < BrakeDeceleration) { BrakeDeceleration = Train.Cars[Train.DriverCar].Specs.MotorDeceleration; } break; } } if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake | Train.Specs.MaximumBrakeNotch <= 0) { decelerationCruise = 0.3 * BrakeDeceleration; decelerationStart = 0.5 * BrakeDeceleration; decelerationStep = 0.1 * BrakeDeceleration; } else if (Train.Specs.MaximumBrakeNotch <= 2) { decelerationCruise = 0.2 * BrakeDeceleration; decelerationStart = 0.4 * BrakeDeceleration; decelerationStep = 0.5 * BrakeDeceleration; } else { decelerationCruise = 0.2 * BrakeDeceleration; decelerationStart = 0.5 * BrakeDeceleration; decelerationStep = BrakeDeceleration / (double)Train.Specs.MaximumBrakeNotch; } if (this.CurrentSpeedFactor >= 1.0) { decelerationCruise *= 1.25; decelerationStart *= 1.25; decelerationStep *= 1.25; } bool forceBrakeMode = false; if (spd > 0.0 & spd > brakestart) { dec = decelerationStep + 0.1 * (spd - brakestart); } bool reduceDecelerationCruiseAndStart = false; // look ahead double lookahead = (Train.Station >= 0 ? 150.0 : 50.0) + (spd * spd) / (2.0 * decelerationCruise); double tp = Train.Cars[0].FrontAxle.Follower.TrackPosition - Train.Cars[0].FrontAxlePosition + 0.5 * Train.Cars[0].Length; double stopDistance = double.MaxValue; { // next station stop int te = Train.Cars[0].FrontAxle.Follower.LastTrackElement; for (int i = te; i < TrackManager.CurrentTrack.Elements.Length; i++) { double stp = TrackManager.CurrentTrack.Elements[i].StartingTrackPosition; if (tp + lookahead <= stp) break; for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = GetStopIndex(e.StationIndex, Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > 0.0 & dist < stopDistance) { stopDistance = dist; } } } } } } } { // events int te = Train.Cars[0].FrontAxle.Follower.LastTrackElement; for (int i = te; i < TrackManager.CurrentTrack.Elements.Length; i++) { double stp = TrackManager.CurrentTrack.Elements[i].StartingTrackPosition; if (tp + lookahead <= stp) break; for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.LimitChangeEvent) { // speed limit TrackManager.LimitChangeEvent e = (TrackManager.LimitChangeEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (e.NextSpeedLimit < spd) { double dist = stp + e.TrackPositionDelta - tp; double edec = (spd * spd - e.NextSpeedLimit * e.NextSpeedLimit * this.CurrentSpeedFactor) / (2.0 * dist); if (edec > dec) dec = edec; } } else if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.SectionChangeEvent) { // section TrackManager.SectionChangeEvent e = (TrackManager.SectionChangeEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (stp + e.TrackPositionDelta > tp) { if (!Game.Sections[e.NextSectionIndex].Invisible & Game.Sections[e.NextSectionIndex].CurrentAspect >= 0) { double elim = Game.Sections[e.NextSectionIndex].Aspects[Game.Sections[e.NextSectionIndex].CurrentAspect].Speed * this.CurrentSpeedFactor; if (elim < spd | spd <= 0.0) { double dist = stp + e.TrackPositionDelta - tp; double edec; if (elim == 0.0) { double redstopdist; if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Completed & dist < 120.0) { dist = 1.0; redstopdist = 25.0; } else if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Pending | stopDistance < dist) { redstopdist = 1.0; } else if (spd > 9.72222222222222) { redstopdist = 55.0; } else { redstopdist = 35.0; } if (dist > redstopdist) { edec = (spd * spd) / (2.0 * (dist - redstopdist)); } else { edec = BrakeDeceleration; } if (dist < 100.0) { reduceDecelerationCruiseAndStart = true; } } else { if (dist >= 1.0) { edec = (spd * spd - elim * elim) / (2.0 * dist); } else { edec = 0.0; } } if (edec > dec) dec = edec; } } } } else if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { // station start if (Train.Station == -1) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = GetStopIndex(e.StationIndex, Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > -Stations[e.StationIndex].Stops[s].ForwardTolerance) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } double edec; edec = spd * spd / (2.0 * dist); if (edec > dec) dec = edec; } } } } } else if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationEndEvent) { // station end if (Train.Station == -1) { TrackManager.StationEndEvent e = (TrackManager.StationEndEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = GetStopIndex(e.StationIndex, Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > -Stations[e.StationIndex].Stops[s].ForwardTolerance) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } double edec; edec = spd * spd / (2.0 * dist); if (edec > dec) dec = edec; } } } } } else if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.TrackEndEvent) { // track end if (Train == TrainManager.PlayerTrain) { TrackManager.TrackEndEvent e = (TrackManager.TrackEndEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; double dist = stp + e.TrackPositionDelta - tp; double edec; if (dist >= 15.0) { edec = spd * spd / (2.0 * dist); } else { edec = BrakeDeceleration; } if (edec > dec) dec = edec; } } } } } // buffers ahead if (Train == TrainManager.PlayerTrain) { for (int i = 0; i < BufferTrackPositions.Length; i++) { double dist = BufferTrackPositions[i] - tp; if (dist > 0.0) { double edec; if (dist >= 10.0) { edec = spd * spd / (2.0 * dist); } else if (dist >= 5.0) { TrainManager.ApplyNotch(Train, -1, true, 1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Service); this.CurrentInterval = 0.1; return; } else { TrainManager.ApplyNotch(Train, -1, true, 1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Service); TrainManager.ApplyEmergencyBrake(Train); this.CurrentInterval = 10.0; return; } if (edec > dec) dec = edec; } } } // trains ahead for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i] != Train && TrainManager.Trains[i].State == TrainManager.TrainState.Available) { double pos = TrainManager.Trains[i].Cars[TrainManager.Trains[i].Cars.Length - 1].RearAxle.Follower.TrackPosition - TrainManager.Trains[i].Cars[TrainManager.Trains[i].Cars.Length - 1].RearAxlePosition - 0.5 * TrainManager.Trains[i].Cars[TrainManager.Trains[i].Cars.Length - 1].Length; double dist = pos - tp; if (dist > -10.0 & dist < lookahead) { const double minDistance = 10.0; const double maxDistance = 100.0; double edec; if (dist > minDistance) { double shift = 0.75 * minDistance + 1.0 * spd; edec = spd * spd / (2.0 * (dist - shift)); } else if (dist > 0.5 * minDistance) { TrainManager.ApplyNotch(Train, -1, true, 1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Service); this.CurrentInterval = 0.1; return; } else { TrainManager.ApplyNotch(Train, -1, true, 1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Service); TrainManager.ApplyEmergencyBrake(Train); this.CurrentInterval = 1.0; return; } if (dist < maxDistance) { reduceDecelerationCruiseAndStart = true; } if (edec > dec) dec = edec; } } } TrainManager.UnapplyEmergencyBrake(Train); // current station if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Pending) { if (StopsAtStation(Train.Station, Train)) { int s = GetStopIndex(Train.Station, Train.Cars.Length); if (s >= 0) { double dist = Stations[Train.Station].Stops[s].TrackPosition - tp; if (dist > 0.0) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } double edec; edec = spd * spd / (2.0 * dist); if (edec > dec) dec = edec; } else { dec = BrakeDeceleration; } } } } // power / brake if (reduceDecelerationCruiseAndStart) { decelerationCruise *= 0.3; decelerationStart *= 0.3; } double brakeModeBrakeThreshold = 0.75 * decelerationStart + 0.25 * decelerationCruise; if (!BrakeMode & dec > decelerationStart | BrakeMode & dec > brakeModeBrakeThreshold | forceBrakeMode) { // brake BrakeMode = true; double decdiff = -acc - dec; if (decdiff < -decelerationStep) { // brake start if (Train.Specs.CurrentPowerNotch.Driver == 0) { TrainManager.ApplyNotch(Train, 0, true, 1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Service); } else { TrainManager.ApplyNotch(Train, -1, true, 0, true); } CurrentInterval *= 0.4; if (CurrentInterval < 0.3) CurrentInterval = 0.3; } else if (decdiff > decelerationStep) { // brake stop TrainManager.ApplyNotch(Train, -1, true, -1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Release); CurrentInterval *= 0.4; if (CurrentInterval < 0.3) CurrentInterval = 0.3; } else { // keep brake TrainManager.ApplyNotch(Train, -1, true, 0, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Lap); CurrentInterval *= 1.2; if (CurrentInterval > 1.0) CurrentInterval = 1.0; } if (Train.Specs.CurrentPowerNotch.Driver == 0 & Train.Specs.CurrentBrakeNotch.Driver == 0) { TrainManager.ApplyHoldBrake(Train, Train.Specs.HasHoldBrake); } if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { CurrentInterval = 0.1; } } else if (dec > decelerationCruise) { // cut power/brake BrakeMode = false; TrainManager.ApplyNotch(Train, -1, true, -1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Release); if (Train.Specs.CurrentPowerNotch.Driver == 0 & Train.Specs.CurrentBrakeNotch.Driver == 0) { TrainManager.ApplyHoldBrake(Train, Train.Specs.HasHoldBrake); } CurrentInterval *= 0.4; if (CurrentInterval < 0.3) CurrentInterval = 0.3; } else { // power BrakeMode = false; double acclim; if (!double.IsInfinity(lim)) { double d = lim - spd; if (d > 0.0) { acclim = 0.1 / (0.1 * d + 1.0) - 0.12; } else { acclim = -1.0; } } else { acclim = -1.0; } if (spd < powerstart) { // power start (under-speed) if (Train.Specs.CurrentBrakeNotch.Driver == 0) { if (Train.Specs.CurrentPowerNotch.Driver < this.PowerNotchAtWhichWheelSlipIsObserved - 1) { TrainManager.ApplyNotch(Train, 1, true, 0, true); } } else { TrainManager.ApplyNotch(Train, 0, true, -1, true); } TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Release); if (double.IsPositiveInfinity(powerstart)) { CurrentInterval = 0.3 + 0.1 * Train.Specs.CurrentPowerNotch.Driver; } else { double p = (double)Train.Specs.CurrentPowerNotch.Driver / (double)Train.Specs.MaximumPowerNotch; CurrentInterval = 0.3 + 15.0 * p / (powerstart - spd + 1.0); } if (CurrentInterval > 1.3) CurrentInterval = 1.3; } else if (spd > powerend) { // power end (over-speed) TrainManager.ApplyNotch(Train, -1, true, -1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Release); CurrentInterval *= 0.3; if (CurrentInterval < 0.2) CurrentInterval = 0.2; } else if (acc < acclim) { // power start (under-acceleration) if (Train.Specs.CurrentBrakeNotch.Driver == 0) { if (Train.Specs.CurrentPowerNotch.Driver < this.PowerNotchAtWhichWheelSlipIsObserved - 1) { if (Train.Specs.CurrentPowerNotch.Driver == Train.Specs.CurrentPowerNotch.Actual) { TrainManager.ApplyNotch(Train, 1, true, 0, true); } } } else { TrainManager.ApplyNotch(Train, 0, true, -1, true); } TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Release); CurrentInterval = 1.3; } else { // keep power TrainManager.ApplyNotch(Train, 0, true, -1, true); TrainManager.ApplyAirBrakeHandle(Train, TrainManager.AirBrakeHandleState.Release); if (Train.Specs.CurrentPowerNotch.Driver != 0) { Train.Specs.CurrentConstSpeed = Train.Specs.HasConstSpeed; } if (Train.Specs.CurrentPowerNotch.Driver == 0 & Train.Specs.CurrentBrakeNotch.Driver == 0) { TrainManager.ApplyHoldBrake(Train, Train.Specs.HasHoldBrake); } CurrentInterval *= 1.1; if (CurrentInterval > 1.5) CurrentInterval = 1.5; } } } } internal override void Trigger(TrainManager.Train Train, double TimeElapsed) { if (TimeLastProcessed > SecondsSinceMidnight) { TimeLastProcessed = SecondsSinceMidnight; } else if (SecondsSinceMidnight - TimeLastProcessed >= CurrentInterval) { TimeLastProcessed = SecondsSinceMidnight; if (Train.Plugin != null && Train.Plugin.SupportsAI) { if (PerformPlugin(Train) != OpenBveApi.Runtime.AIResponse.None) { return; } } PerformDefault(Train); } } } // bogus pretrain internal struct BogusPretrainInstruction { internal double TrackPosition; internal double Time; } internal class BogusPretrainAI : GeneralAI { private double TimeLastProcessed; private double CurrentInterval; internal BogusPretrainAI(TrainManager.Train Train) { this.TimeLastProcessed = 0.0; this.CurrentInterval = 1.0; } internal override void Trigger(TrainManager.Train Train, double TimeElapsed) { if (SecondsSinceMidnight - TimeLastProcessed >= CurrentInterval) { TimeLastProcessed = SecondsSinceMidnight; CurrentInterval = 5.0; double ap = double.MaxValue, at = double.MaxValue; double bp = double.MinValue, bt = double.MinValue; for (int i = 0; i < BogusPretrainInstructions.Length; i++) { if (BogusPretrainInstructions[i].Time < SecondsSinceMidnight | at == double.MaxValue) { at = BogusPretrainInstructions[i].Time; ap = BogusPretrainInstructions[i].TrackPosition; } } for (int i = BogusPretrainInstructions.Length - 1; i >= 0; i--) { if (BogusPretrainInstructions[i].Time > at | bt == double.MinValue) { bt = BogusPretrainInstructions[i].Time; bp = BogusPretrainInstructions[i].TrackPosition; } } if (at != double.MaxValue & bt != double.MinValue & SecondsSinceMidnight <= BogusPretrainInstructions[BogusPretrainInstructions.Length - 1].Time) { double r = bt - at; if (r > 0.0) { r = (SecondsSinceMidnight - at) / r; if (r < 0.0) r = 0.0; if (r > 1.0) r = 1.0; } else { r = 0.0; } double p = ap + r * (bp - ap); double d = p - Train.Cars[0].FrontAxle.Follower.TrackPosition; for (int j = 0; j < Train.Cars.Length; j++) { TrainManager.MoveCar(Train, j, d, 0.1); } } else { TrainManager.DisposeTrain(Train); } } } } // ================================ // messages internal enum MessageColor { None = 0, Black = 1, Gray = 2, White = 3, Red = 4, Orange = 5, Green = 6, Blue = 7, Magenta = 8 } internal enum MessageDependency { None = 0, RouteLimit = 1, SectionLimit = 2, Station = 3 } internal struct Message { internal string InternalText; internal string DisplayText; internal MessageDependency Depencency; internal double Timeout; internal MessageColor Color; internal World.Vector2D RendererPosition; internal double RendererAlpha; } internal static Message[] Messages = new Message[] { }; internal static World.Vector2D MessagesRendererSize = new World.Vector2D(16.0, 16.0); internal static void AddMessage(string Text, MessageDependency Depencency, Interface.GameMode Mode, MessageColor Color, double Timeout) { if (Interface.CurrentOptions.GameMode <= Mode) { if (Depencency == MessageDependency.RouteLimit | Depencency == MessageDependency.SectionLimit) { for (int i = 0; i < Messages.Length; i++) { if (Messages[i].Depencency == Depencency) return; } } int n = Messages.Length; Array.Resize(ref Messages, n + 1); Messages[n].InternalText = Text; Messages[n].DisplayText = ""; Messages[n].Depencency = Depencency; Messages[n].Timeout = Timeout; Messages[n].Color = Color; Messages[n].RendererPosition = new World.Vector2D(0.0, 0.0); Messages[n].RendererAlpha = 0.0; } } internal static void AddDebugMessage(string text, double duration) { Game.AddMessage(text, Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Magenta, Game.SecondsSinceMidnight + duration); } internal static void UpdateMessages() { for (int i = 0; i < Messages.Length; i++) { bool remove = SecondsSinceMidnight >= Messages[i].Timeout; switch (Messages[i].Depencency) { case MessageDependency.RouteLimit: { double spd = Math.Abs(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed); double lim = TrainManager.PlayerTrain.CurrentRouteLimit; spd = Math.Round(spd * 3.6); lim = Math.Round(lim * 3.6); remove = spd <= lim; string s = Messages[i].InternalText, t; t = spd.ToString(System.Globalization.CultureInfo.InvariantCulture); s = s.Replace("[speed]", t); t = lim.ToString(System.Globalization.CultureInfo.InvariantCulture); s = s.Replace("[limit]", t); Messages[i].DisplayText = s; } break; case MessageDependency.SectionLimit: { double spd = Math.Abs(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed); double lim = TrainManager.PlayerTrain.CurrentSectionLimit; spd = Math.Round(spd * 3.6); lim = Math.Round(lim * 3.6); remove = spd <= lim; string s = Messages[i].InternalText, t; t = spd.ToString(System.Globalization.CultureInfo.InvariantCulture); s = s.Replace("[speed]", t); t = lim.ToString(System.Globalization.CultureInfo.InvariantCulture); s = s.Replace("[limit]", t); Messages[i].DisplayText = s; } break; case MessageDependency.Station: { int j = TrainManager.PlayerTrain.Station; if (j >= 0 & TrainManager.PlayerTrain.StationState != TrainManager.TrainStopState.Completed) { double d = TrainManager.PlayerTrain.StationDepartureTime - SecondsSinceMidnight + 1.0; if (d < 0.0) d = 0.0; string s = Messages[i].InternalText; TimeSpan a = TimeSpan.FromSeconds(d); System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string t = a.Hours.ToString("00", Culture) + ":" + a.Minutes.ToString("00", Culture) + ":" + a.Seconds.ToString("00", Culture); s = s.Replace("[time]", t); s = s.Replace("[name]", Stations[j].Name); Messages[i].DisplayText = s; if (d > 0.0) remove = false; } else { remove = true; } } break; default: Messages[i].DisplayText = Messages[i].InternalText; break; } if (remove) { if (Messages[i].Timeout == double.PositiveInfinity) { Messages[i].Timeout = SecondsSinceMidnight - 1.0; } if (SecondsSinceMidnight >= Messages[i].Timeout & Messages[i].RendererAlpha == 0.0) { for (int j = i; j < Messages.Length - 1; j++) { Messages[j] = Messages[j + 1]; } i--; Array.Resize(ref Messages, Messages.Length - 1); } } } } // ================================ // marker internal static Textures.Texture[] MarkerTextures = new Textures.Texture[] { }; internal static void AddMarker(Textures.Texture Texture) { int n = MarkerTextures.Length; Array.Resize(ref MarkerTextures, n + 1); MarkerTextures[n] = Texture; } internal static void RemoveMarker(Textures.Texture Texture) { int n = MarkerTextures.Length; for (int i = 0; i < n; i++) { if (MarkerTextures[i] == Texture) { for (int j = i; j < n - 1; j++) { MarkerTextures[j] = MarkerTextures[j + 1]; } n--; Array.Resize(ref MarkerTextures, n); break; } } } // ================================ // points of interest internal struct PointOfInterest { internal double TrackPosition; internal Vector3 TrackOffset; internal double TrackYaw; internal double TrackPitch; internal double TrackRoll; internal string Text; } internal static PointOfInterest[] PointsOfInterest = new PointOfInterest[] { }; internal static bool ApplyPointOfInterest(int Value, bool Relative) { double t = 0.0; int j = -1; if (Relative) { // relative if (Value < 0) { // previous poi t = double.NegativeInfinity; for (int i = 0; i < PointsOfInterest.Length; i++) { if (PointsOfInterest[i].TrackPosition < World.CameraTrackFollower.TrackPosition) { if (PointsOfInterest[i].TrackPosition > t) { t = PointsOfInterest[i].TrackPosition; j = i; } } } } else if (Value > 0) { // next poi t = double.PositiveInfinity; for (int i = 0; i < PointsOfInterest.Length; i++) { if (PointsOfInterest[i].TrackPosition > World.CameraTrackFollower.TrackPosition) { if (PointsOfInterest[i].TrackPosition < t) { t = PointsOfInterest[i].TrackPosition; j = i; } } } } } else { // absolute j = Value >= 0 & Value < PointsOfInterest.Length ? Value : -1; } // process poi if (j >= 0) { TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, t, true, false); World.CameraCurrentAlignment.Position = PointsOfInterest[j].TrackOffset; World.CameraCurrentAlignment.Yaw = PointsOfInterest[j].TrackYaw; World.CameraCurrentAlignment.Pitch = PointsOfInterest[j].TrackPitch; World.CameraCurrentAlignment.Roll = PointsOfInterest[j].TrackRoll; World.CameraCurrentAlignment.TrackPosition = t; World.UpdateAbsoluteCamera(0.0); if (PointsOfInterest[j].Text != null) { double n = 3.0 + 0.5 * Math.Sqrt((double)PointsOfInterest[j].Text.Length); Game.AddMessage(PointsOfInterest[j].Text, Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.White, Game.SecondsSinceMidnight + n); } return true; } else { return false; } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/Illustrations.cs000066400000000000000000000336661171674032100227610ustar00rootroot00000000000000using System; using System.Drawing; namespace OpenBve { internal static class Illustrations { // create route map internal static Bitmap CreateRouteMap(int Width, int Height) { // find first and last used element based on stations int n = TrackManager.CurrentTrack.Elements.Length; int n0 = n - 1; int n1 = 0; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { if (i < n0) n0 = i; if (i > n1) n1 = i; } } } n0 -= 4; n1 += 8; if (n0 < 0) n0 = 0; if (n1 >= TrackManager.CurrentTrack.Elements.Length) n1 = TrackManager.CurrentTrack.Elements.Length - 1; if (n1 <= n0) n1 = n0 + 1; // find dimensions double x0 = double.PositiveInfinity, z0 = double.PositiveInfinity; double x1 = double.NegativeInfinity, z1 = double.NegativeInfinity; for (int i = n0; i <= n1; i++) { double x = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double z = TrackManager.CurrentTrack.Elements[i].WorldPosition.Z; if (x < x0) x0 = x; if (x > x1) x1 = x; if (z < z0) z0 = z; if (z > z1) z1 = z; } if (x0 >= x1 - 1.0) x0 = x1 - 1.0; if (z0 >= z1 - 1.0) z0 = z1 - 1.0; double wrh = (double)Width / (double)Height; if ((x1 - x0) / (z1 - z0) <= wrh) { double dx = 0.5 * (z1 - z0) * wrh; double px = 0.5 * (x0 + x1); x0 = px - dx; x1 = px + dx; } else { double dz = 0.5 * (x1 - x0) / wrh; double pz = 0.5 * (z0 + z1); z0 = pz - dz; z1 = pz + dz; } double xd = 1.0 / (x1 - x0); double zd = 1.0 / (z1 - z0); double ox = 8.0, oy = 8.0; double w = (double)(Width - 16); double h = (double)(Height - 16); // create bitmap Bitmap b = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); Graphics g = Graphics.FromImage(b); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.Clear(Color.White); // draw route path { int start = 0; bool atc = false; PointF[] p = new PointF[n]; for (int i = 0; i < n; i++) { double x = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double z = TrackManager.CurrentTrack.Elements[i].WorldPosition.Z; x = ox + w * (x - x0) * xd; z = oy + h + h * zd * (z0 - z); p[i] = new PointF((float)x, (float)z); // ats/atc for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].SafetySystem == Game.SafetySystem.Atc) { if (!atc) { atc = true; if (i - start - 1 > 0) g.DrawCurve(Pens.Black, p, start, i - start - 1); start = i; } } else { if (atc) { atc = false; if (i - start - 1 > 0) g.DrawCurve(Pens.DarkRed, p, start, i - start - 1); start = i; } } } } } DrawSegmentedCurve(g, atc ? Pens.DarkRed : Pens.Black, p, start, n - start - 1); } // draw station circles for (int i = 0; i < n; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].Name != string.Empty) { double x = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double y = TrackManager.CurrentTrack.Elements[i].WorldPosition.Z; x = ox + w * (x - x0) * xd; y = oy + h + h * zd * (z0 - y); // station circle RectangleF r = new RectangleF((float)x - 4.0f, (float)y - 4.0f, 8.0f, 8.0f); bool q = Game.PlayerStopsAtStation(e.StationIndex); g.FillEllipse(q ? Brushes.SkyBlue : Brushes.LightGray, r); g.DrawEllipse(q ? Pens.Black : Pens.Gray, r); } } } } // draw station names { double wh = w * h; Font f = new Font(FontFamily.GenericSansSerif, wh < 65536.0 ? 9.0f : 10.0f, GraphicsUnit.Pixel); for (int i = 0; i < n; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].Name != string.Empty) { double x = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double y = TrackManager.CurrentTrack.Elements[i].WorldPosition.Z; x = ox + w * (x - x0) * xd; y = oy + h + h * zd * (z0 - y); RectangleF r = new RectangleF((float)x - 4.0f, (float)y - 4.0f, 8.0f, 8.0f); bool stop = Game.PlayerStopsAtStation(e.StationIndex); string t = Game.Stations[e.StationIndex].Name; SizeF m = g.MeasureString(t, f, Width, StringFormat.GenericDefault); double sx = TrackManager.CurrentTrack.Elements[i].WorldSide.X; double sz = TrackManager.CurrentTrack.Elements[i].WorldSide.Z; double xt, yt; if (Math.Sign(sx) == Math.Sign(sz)) { // descending bool o = (x - ox) * (h - y) <= (w - x) * (y - oy); if (o) { // up-right xt = x + 6.0; yt = y - 6.0 - m.Height; } else { // down-left xt = x - 6.0 - m.Width; yt = y + 6.0; } } else { // ascending bool o = (h - y) * (w - x) <= (x - ox) * (y - oy); if (o) { // up-left xt = x - 6.0 - m.Width; yt = y - 6.0 - m.Height; } else { // down-right xt = x + 6.0; yt = y + 6.0; } } if (xt < ox) { xt = ox; } else if (xt + m.Width > w) { xt = w - m.Width; } if (yt < oy) { yt = oy; } else if (yt + m.Height > h) { yt = h - m.Height; } r = new RectangleF((float)xt, (float)yt, m.Width, m.Height); g.FillRectangle(stop ? Brushes.White : Brushes.LightGray, r.Left - 1.0f, r.Top - 1.0f, r.Width + 2.0f, r.Height + 2.0f); g.DrawRectangle(stop ? Pens.Black : Pens.Gray, r.Left - 1.0f, r.Top - 1.0f, r.Width + 2.0f, r.Height + 2.0f); g.DrawString(t, f, stop ? Brushes.Black : Brushes.Gray, (float)xt, (float)yt); } } } } } // finalize return b; } // create route gradient profile internal static Bitmap CreateRouteGradientProfile(int Width, int Height) { // find first and last used element based on stations int n = TrackManager.CurrentTrack.Elements.Length; int n0 = n - 1; int n1 = 0; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { if (i < n0) n0 = i; if (i > n1) n1 = i; } } } n0 -= 4; n1 += 8; if (n0 < 0) n0 = 0; if (n1 >= TrackManager.CurrentTrack.Elements.Length) n1 = TrackManager.CurrentTrack.Elements.Length - 1; if (n1 <= n0) n1 = n0 + 1; // find dimensions double y0 = double.PositiveInfinity, y1 = double.NegativeInfinity; for (int i = n0; i <= n1; i++) { double y = TrackManager.CurrentTrack.Elements[i].WorldPosition.Y; if (y < y0) y0 = y; if (y > y1) y1 = y; } if (y0 >= y1 - 1.0) y0 = y1 - 1.0; double nd = 1.0 / (double)(n1 - n0); double yd = 1.0 / (double)(y1 - y0); double ox = 8.0, oy = 8.0; double w = (double)(Width - 16); double h = (double)(Height - 32); // create bitmap Bitmap b = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); Graphics g = Graphics.FromImage(b); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.Clear(Color.White); // draw below sea level { double y = oy + h * (1.0 - 0.5 * (double)(-Game.RouteInitialElevation - y0) * yd); double x0 = ox - w * (double)(n0) * nd; double x1 = ox + w * (double)(n - n0) * nd; g.FillRectangle(Brushes.PaleGoldenrod, (float)x0, (float)y, (float)x1, (float)(oy + h) - (float)y); g.DrawLine(Pens.Gray, (float)x0, (float)y, (float)x1, (float)y); } // draw route path { PointF[] p = new PointF[n + 2]; p[0] = new PointF((float)(ox - w * (double)n0 * nd), (float)(oy + h)); for (int i = 0; i < n; i++) { double x = ox + w * (double)(i - n0) * nd; double y = oy + h * (1.0 - 0.5 * (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); p[i + 1] = new PointF((float)x, (float)y); } p[n + 1] = new PointF((float)(ox + w * (double)(n - n0 - 1) * nd), (float)(oy + h)); g.FillPolygon(Brushes.Tan, p); for (int i = 1; i < n; i++) { g.DrawLine(Pens.Black, p[i], p[i + 1]); } g.DrawLine(Pens.Black, 0.0f, (float)(oy + h), (float)Width, (float)(oy + h)); } // draw station names { Font f = new Font(FontFamily.GenericSansSerif, 12.0f, GraphicsUnit.Pixel); StringFormat m = new StringFormat(); for (int i = 0; i < n; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].Name != string.Empty) { bool stop = Game.PlayerStopsAtStation(e.StationIndex); if (Interface.IsJapanese(Game.Stations[e.StationIndex].Name)) { m.Alignment = StringAlignment.Near; m.LineAlignment = StringAlignment.Near; double x = ox + w * (double)(i - n0) * nd; double y = oy + h * (1.0 - 0.5 * (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); string t = Game.Stations[e.StationIndex].Name; float tx = 0.0f, ty = (float)oy; for (int k = 0; k < t.Length; k++) { SizeF s = g.MeasureString(t.Substring(k, 1), f, 65536, StringFormat.GenericTypographic); if (s.Width > tx) tx = s.Width; } for (int k = 0; k < t.Length; k++) { g.DrawString(t.Substring(k, 1), f, stop ? Brushes.Black : Brushes.LightGray, (float)x - 0.5f * tx, ty); SizeF s = g.MeasureString(t.Substring(k, 1), f, 65536, StringFormat.GenericTypographic); ty += s.Height; } g.DrawLine(stop ? Pens.Gray : Pens.LightGray, new PointF((float)x, ty + 4.0f), new PointF((float)x, (float)y)); } else { m.Alignment = StringAlignment.Far; m.LineAlignment = StringAlignment.Near; double x = ox + w * (double)(i - n0) * nd; double y = oy + h * (1.0 - 0.5 * (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); g.RotateTransform(-90.0f); g.TranslateTransform((float)x, (float)oy, System.Drawing.Drawing2D.MatrixOrder.Append); g.DrawString(Game.Stations[e.StationIndex].Name, f, stop ? Brushes.Black : Brushes.LightGray, new PointF(0.0f, -5.0f), m); g.ResetTransform(); SizeF s = g.MeasureString(Game.Stations[e.StationIndex].Name, f); g.DrawLine(stop ? Pens.Gray : Pens.LightGray, new PointF((float)x, (float)(oy + s.Width + 4)), new PointF((float)x, (float)y)); } } } } } } // draw route markers { Font f = new Font(FontFamily.GenericSansSerif, 10.0f, GraphicsUnit.Pixel); Font fs = new Font(FontFamily.GenericSansSerif, 9.0f, GraphicsUnit.Pixel); StringFormat m = new StringFormat(); m.Alignment = StringAlignment.Far; m.LineAlignment = StringAlignment.Center; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; int k = 48 * n / Width; for (int i = 0; i < n; i += k) { double x = ox + w * (double)(i - n0) * nd; double y = (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd; if (x < w) { string t = ((int)Math.Round(TrackManager.CurrentTrack.Elements[i].StartingTrackPosition)).ToString(Culture); g.DrawString(t + "m", f, Brushes.Black, (float)x, (float)(oy + h + 6.0)); } { y = oy + h * (1.0 - 0.5 * y) + 2.0f; string t = ((int)Math.Round(Game.RouteInitialElevation + TrackManager.CurrentTrack.Elements[i].WorldPosition.Y)).ToString(Culture); SizeF s = g.MeasureString(t, fs); if (y < oy + h - (double)s.Width - 10.0) { g.RotateTransform(-90.0f); g.TranslateTransform((float)x, (float)y + 4.0f, System.Drawing.Drawing2D.MatrixOrder.Append); g.DrawString(t + "m", fs, Brushes.Black, 0.0f, 0.0f, m); g.ResetTransform(); g.DrawLine(Pens.Gray, (float)x, (float)(y + s.Width + 12.0), (float)x, (float)(oy + h)); } } } } // finalize return b; } // draw segmented curve private static void DrawSegmentedCurve(Graphics Graphics, Pen Pen, PointF[] Points, int Start, int Length) { const int Count = 1000; int End = Start + Length - 1; for (int k = Start; k <= End; k += Count) { int m = End - k + 1; if (m > Count) m = Count; Graphics.DrawCurve(Pen, Points, k, m); } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/Interface.cs000066400000000000000000003510641171674032100220000ustar00rootroot00000000000000using System; using System.Globalization; using OpenBveApi.Colors; using Tao.Sdl; namespace OpenBve { internal static class Interface { // messages internal enum MessageType { Warning, Error, Critical } internal struct Message { internal MessageType Type; internal bool FileNotFound; internal string Text; } internal static Message[] Messages = new Message[] { }; internal static int MessageCount = 0; internal static void AddMessage(MessageType Type, bool FileNotFound, string Text) { if (Type == MessageType.Warning & !CurrentOptions.ShowWarningMessages) return; if (Type == MessageType.Error & !CurrentOptions.ShowErrorMessages) return; if (MessageCount == 0) { Messages = new Message[16]; } else if (MessageCount >= Messages.Length) { Array.Resize(ref Messages, Messages.Length << 1); } Messages[MessageCount].Type = Type; Messages[MessageCount].FileNotFound = FileNotFound; Messages[MessageCount].Text = Text; MessageCount++; Program.AppendToLogFile(Text); } internal static void ClearMessages() { Messages = new Message[] { }; MessageCount = 0; } // ================================ // options internal struct EncodingValue { internal int Codepage; internal string Value; } internal enum MotionBlurMode { None = 0, Low = 1, Medium = 2, High = 3 } internal enum SoundRange { Low = 0, Medium = 1, High = 2 } internal enum GameMode { Arcade = 0, Normal = 1, Expert = 2 } internal enum InterpolationMode { NearestNeighbor, Bilinear, NearestNeighborMipmapped, BilinearMipmapped, TrilinearMipmapped, AnisotropicFiltering } internal class Options { internal string LanguageCode; internal bool FullscreenMode; internal bool VerticalSynchronization; internal int WindowWidth; internal int WindowHeight; internal int FullscreenWidth; internal int FullscreenHeight; internal int FullscreenBits; internal string UserInterfaceFolder; internal InterpolationMode Interpolation; internal Renderer.TransparencyMode TransparencyMode; internal int AnisotropicFilteringLevel; internal int AnisotropicFilteringMaximum; internal int ViewingDistance; internal MotionBlurMode MotionBlur; internal int ObjectOptimizationBasicThreshold; internal int ObjectOptimizationFullThreshold; internal bool Toppling; internal bool Collisions; internal bool Derailments; internal bool BlackBox; internal bool UseJoysticks; internal double JoystickAxisThreshold; internal double KeyRepeatDelay; internal double KeyRepeatInterval; internal Sounds.SoundModels SoundModel; internal SoundRange SoundRange; internal int SoundNumber; internal bool ShowWarningMessages; internal bool ShowErrorMessages; internal string RouteFolder; internal string TrainFolder; internal string[] RecentlyUsedRoutes; internal string[] RecentlyUsedTrains; internal int RecentlyUsedLimit; internal EncodingValue[] RouteEncodings; internal EncodingValue[] TrainEncodings; internal GameMode GameMode; internal int MainMenuWidth; internal int MainMenuHeight; internal bool DisableDisplayLists; internal bool LoadInAdvance; internal bool NoTextureResize; internal Options() { this.LanguageCode = "en-US"; this.FullscreenMode = false; this.VerticalSynchronization = true; this.WindowWidth = 960; this.WindowHeight = 600; this.FullscreenWidth = 1024; this.FullscreenHeight = 768; this.FullscreenBits = 32; this.UserInterfaceFolder = "Default"; this.Interpolation = Interface.InterpolationMode.BilinearMipmapped; this.TransparencyMode = Renderer.TransparencyMode.Quality; this.AnisotropicFilteringLevel = 0; this.AnisotropicFilteringMaximum = 0; this.ViewingDistance = 600; this.MotionBlur = MotionBlurMode.None; this.Toppling = true; this.Collisions = true; this.Derailments = true; this.GameMode = GameMode.Normal; this.BlackBox = false; this.UseJoysticks = true; this.JoystickAxisThreshold = 0.0; this.KeyRepeatDelay = 0.5; this.KeyRepeatInterval = 0.1; this.SoundModel = Sounds.SoundModels.Default; this.SoundRange = SoundRange.Low; this.SoundNumber = 16; this.ShowWarningMessages = true; this.ShowErrorMessages = true; this.ObjectOptimizationBasicThreshold = 10000; this.ObjectOptimizationFullThreshold = 1000; this.RouteFolder = ""; this.TrainFolder = ""; this.RecentlyUsedRoutes = new string[] { }; this.RecentlyUsedTrains = new string[] { }; this.RecentlyUsedLimit = 10; this.RouteEncodings = new EncodingValue[] { }; this.TrainEncodings = new EncodingValue[] { }; this.MainMenuWidth = 0; this.MainMenuHeight = 0; this.DisableDisplayLists = false; this.LoadInAdvance = false; this.NoTextureResize = false; } } internal static Options CurrentOptions; internal static void LoadOptions() { CurrentOptions = new Options(); CultureInfo Culture = CultureInfo.InvariantCulture; string File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "options.cfg"); if (System.IO.File.Exists(File)) { // load options string[] Lines = System.IO.File.ReadAllLines(File, new System.Text.UTF8Encoding()); string Section = ""; for (int i = 0; i < Lines.Length; i++) { Lines[i] = Lines[i].Trim(); if (Lines[i].Length != 0 && !Lines[i].StartsWith(";", StringComparison.OrdinalIgnoreCase)) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim().ToLowerInvariant(); } else { int j = Lines[i].IndexOf("=", StringComparison.OrdinalIgnoreCase); string Key, Value; if (j >= 0) { Key = Lines[i].Substring(0, j).TrimEnd().ToLowerInvariant(); Value = Lines[i].Substring(j + 1).TrimStart(); } else { Key = ""; Value = Lines[i]; } switch (Section) { case "language": switch (Key) { case "code": Interface.CurrentOptions.LanguageCode = Value.Length != 0 ? Value : "en-US"; break; } break; case "interface": switch (Key) { case "folder": Interface.CurrentOptions.UserInterfaceFolder = Value.Length != 0 ? Value : "Default"; break; } break; case "display": switch (Key) { case "mode": Interface.CurrentOptions.FullscreenMode = string.Compare(Value, "fullscreen", StringComparison.OrdinalIgnoreCase) == 0; break; case "vsync": Interface.CurrentOptions.VerticalSynchronization = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; case "windowwidth": { int a; if (!int.TryParse(Value, NumberStyles.Integer, Culture, out a)) { a = 960; } Interface.CurrentOptions.WindowWidth = a; } break; case "windowheight": { int a; if (!int.TryParse(Value, NumberStyles.Integer, Culture, out a)) { a = 600; } Interface.CurrentOptions.WindowHeight = a; } break; case "fullscreenwidth": { int a; if (!int.TryParse(Value, NumberStyles.Integer, Culture, out a)) { a = 1024; } Interface.CurrentOptions.FullscreenWidth = a; } break; case "fullscreenheight": { int a; if (!int.TryParse(Value, NumberStyles.Integer, Culture, out a)) { a = 768; } Interface.CurrentOptions.FullscreenHeight = a; } break; case "fullscreenbits": { int a; if (!int.TryParse(Value, NumberStyles.Integer, Culture, out a)) { a = 32; } Interface.CurrentOptions.FullscreenBits = a; } break; case "mainmenuwidth": { int a; int.TryParse(Value, NumberStyles.Integer, Culture, out a); Interface.CurrentOptions.MainMenuWidth = a; } break; case "mainmenuheight": { int a; int.TryParse(Value, NumberStyles.Integer, Culture, out a); Interface.CurrentOptions.MainMenuHeight = a; } break; case "disabledisplaylists": Interface.CurrentOptions.DisableDisplayLists = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; case "loadinadvance": Interface.CurrentOptions.LoadInAdvance = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; case "notextureresize": Interface.CurrentOptions.NoTextureResize = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; } break; case "quality": switch (Key) { case "interpolation": switch (Value.ToLowerInvariant()) { case "nearestneighbor": Interface.CurrentOptions.Interpolation = Interface.InterpolationMode.NearestNeighbor; break; case "bilinear": Interface.CurrentOptions.Interpolation = Interface.InterpolationMode.Bilinear; break; case "nearestneighbormipmapped": Interface.CurrentOptions.Interpolation = Interface.InterpolationMode.NearestNeighborMipmapped; ; break; case "bilinearmipmapped": Interface.CurrentOptions.Interpolation = Interface.InterpolationMode.BilinearMipmapped; break; case "trilinearmipmapped": Interface.CurrentOptions.Interpolation = Interface.InterpolationMode.TrilinearMipmapped; break; case "anisotropicfiltering": Interface.CurrentOptions.Interpolation = Interface.InterpolationMode.AnisotropicFiltering; break; default: Interface.CurrentOptions.Interpolation = Interface.InterpolationMode.BilinearMipmapped; break; } break; case "anisotropicfilteringlevel": { int a; int.TryParse(Value, NumberStyles.Integer, Culture, out a); Interface.CurrentOptions.AnisotropicFilteringLevel = a; } break; case "anisotropicfilteringmaximum": { int a; int.TryParse(Value, NumberStyles.Integer, Culture, out a); Interface.CurrentOptions.AnisotropicFilteringMaximum = a; } break; case "transparencymode": switch (Value.ToLowerInvariant()) { case "sharp": Interface.CurrentOptions.TransparencyMode = Renderer.TransparencyMode.Performance; break; case "smooth": Interface.CurrentOptions.TransparencyMode = Renderer.TransparencyMode.Quality; break; default: { int a; if (int.TryParse(Value, NumberStyles.Integer, Culture, out a)) { Interface.CurrentOptions.TransparencyMode = (Renderer.TransparencyMode)a; } else { Interface.CurrentOptions.TransparencyMode = Renderer.TransparencyMode.Quality; } break; } } break; case "viewingdistance": { int a; int.TryParse(Value, NumberStyles.Integer, Culture, out a); Interface.CurrentOptions.ViewingDistance = a; } break; case "motionblur": switch (Value.ToLowerInvariant()) { case "low": Interface.CurrentOptions.MotionBlur = MotionBlurMode.Low; break; case "medium": Interface.CurrentOptions.MotionBlur = MotionBlurMode.Medium; break; case "high": Interface.CurrentOptions.MotionBlur = MotionBlurMode.High; break; default: Interface.CurrentOptions.MotionBlur = MotionBlurMode.None; break; } break; } break; case "objectoptimization": switch (Key) { case "basicthreshold": { int a; int.TryParse(Value, NumberStyles.Integer, Culture, out a); Interface.CurrentOptions.ObjectOptimizationBasicThreshold = a; } break; case "fullthreshold": { int a; int.TryParse(Value, NumberStyles.Integer, Culture, out a); Interface.CurrentOptions.ObjectOptimizationFullThreshold = a; } break; } break; case "simulation": switch (Key) { case "toppling": Interface.CurrentOptions.Toppling = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; case "collisions": Interface.CurrentOptions.Collisions = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; case "derailments": Interface.CurrentOptions.Derailments = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; case "blackbox": Interface.CurrentOptions.BlackBox = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; case "mode": switch (Value.ToLowerInvariant()) { case "arcade": Interface.CurrentOptions.GameMode = Interface.GameMode.Arcade; break; case "normal": Interface.CurrentOptions.GameMode = Interface.GameMode.Normal; break; case "expert": Interface.CurrentOptions.GameMode = Interface.GameMode.Expert; break; default: Interface.CurrentOptions.GameMode = Interface.GameMode.Normal; break; } break; } break; case "controls": switch (Key) { case "usejoysticks": Interface.CurrentOptions.UseJoysticks = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; case "joystickaxisthreshold": { double a; double.TryParse(Value, NumberStyles.Float, Culture, out a); Interface.CurrentOptions.JoystickAxisThreshold = a; } break; case "keyrepeatdelay": { int a; int.TryParse(Value, NumberStyles.Integer, Culture, out a); if (a <= 0) a = 500; Interface.CurrentOptions.KeyRepeatDelay = 0.001 * (double)a; } break; case "keyrepeatinterval": { int a; int.TryParse(Value, NumberStyles.Integer, Culture, out a); if (a <= 0) a = 100; Interface.CurrentOptions.KeyRepeatInterval = 0.001 * (double)a; } break; } break; case "sound": switch (Key) { case "model": switch (Value.ToLowerInvariant()) { case "experimental": Interface.CurrentOptions.SoundModel = Sounds.SoundModels.Experimental; break; default: Interface.CurrentOptions.SoundModel = Sounds.SoundModels.Default; break; } break; case "range": switch (Value.ToLowerInvariant()) { case "low": Interface.CurrentOptions.SoundRange = SoundRange.Low; break; case "medium": Interface.CurrentOptions.SoundRange = SoundRange.Medium; break; case "high": Interface.CurrentOptions.SoundRange = SoundRange.High; break; default: Interface.CurrentOptions.SoundRange = SoundRange.Low; break; } break; case "number": { int a; int.TryParse(Value, NumberStyles.Integer, Culture, out a); Interface.CurrentOptions.SoundNumber = a < 16 ? 16 : a; } break; } break; case "verbosity": switch (Key) { case "showwarningmessages": Interface.CurrentOptions.ShowWarningMessages = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; case "showerrormessages": Interface.CurrentOptions.ShowErrorMessages = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; } break; case "folders": switch (Key) { case "route": Interface.CurrentOptions.RouteFolder = Value; break; case "train": Interface.CurrentOptions.TrainFolder = Value; break; } break; case "recentlyusedroutes": { int n = Interface.CurrentOptions.RecentlyUsedRoutes.Length; Array.Resize(ref Interface.CurrentOptions.RecentlyUsedRoutes, n + 1); Interface.CurrentOptions.RecentlyUsedRoutes[n] = Value; } break; case "recentlyusedtrains": { int n = Interface.CurrentOptions.RecentlyUsedTrains.Length; Array.Resize(ref Interface.CurrentOptions.RecentlyUsedTrains, n + 1); Interface.CurrentOptions.RecentlyUsedTrains[n] = Value; } break; case "routeencodings": { int a = System.Text.Encoding.UTF8.CodePage; int.TryParse(Key, NumberStyles.Integer, Culture, out a); int n = Interface.CurrentOptions.RouteEncodings.Length; Array.Resize(ref Interface.CurrentOptions.RouteEncodings, n + 1); Interface.CurrentOptions.RouteEncodings[n].Codepage = a; Interface.CurrentOptions.RouteEncodings[n].Value = Value; } break; case "trainencodings": { int a = System.Text.Encoding.UTF8.CodePage; int.TryParse(Key, NumberStyles.Integer, Culture, out a); int n = Interface.CurrentOptions.TrainEncodings.Length; Array.Resize(ref Interface.CurrentOptions.TrainEncodings, n + 1); Interface.CurrentOptions.TrainEncodings[n].Codepage = a; Interface.CurrentOptions.TrainEncodings[n].Value = Value; } break; } } } } } else { // file not found string Code = CultureInfo.CurrentUICulture.Name; if (Code == null || Code.Length == 0) Code = "en-US"; File = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Languages"), Code + ".cfg"); if (System.IO.File.Exists(File)) { CurrentOptions.LanguageCode = Code; } else { try { int i = Code.IndexOf("-", StringComparison.Ordinal); if (i > 0) { Code = Code.Substring(0, i); File = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Languages"), Code + ".cfg"); if (System.IO.File.Exists(File)) { CurrentOptions.LanguageCode = Code; } } } catch { CurrentOptions.LanguageCode = "en-US"; } } } } internal static void SaveOptions() { CultureInfo Culture = CultureInfo.InvariantCulture; System.Text.StringBuilder Builder = new System.Text.StringBuilder(); Builder.AppendLine("; Options"); Builder.AppendLine("; ======="); Builder.AppendLine("; This file was automatically generated. Please modify only if you know what you're doing."); Builder.AppendLine(); Builder.AppendLine("[language]"); Builder.AppendLine("code = " + CurrentOptions.LanguageCode); Builder.AppendLine(); Builder.AppendLine("[interface]"); Builder.AppendLine("folder = " + CurrentOptions.UserInterfaceFolder); Builder.AppendLine(); Builder.AppendLine("[display]"); Builder.AppendLine("mode = " + (CurrentOptions.FullscreenMode ? "fullscreen" : "window")); Builder.AppendLine("vsync = " + (CurrentOptions.VerticalSynchronization ? "true" : "false")); Builder.AppendLine("windowWidth = " + CurrentOptions.WindowWidth.ToString(Culture)); Builder.AppendLine("windowHeight = " + CurrentOptions.WindowHeight.ToString(Culture)); Builder.AppendLine("fullscreenWidth = " + CurrentOptions.FullscreenWidth.ToString(Culture)); Builder.AppendLine("fullscreenHeight = " + CurrentOptions.FullscreenHeight.ToString(Culture)); Builder.AppendLine("fullscreenBits = " + CurrentOptions.FullscreenBits.ToString(Culture)); Builder.AppendLine("mainmenuWidth = " + CurrentOptions.MainMenuWidth.ToString(Culture)); Builder.AppendLine("mainmenuHeight = " + CurrentOptions.MainMenuHeight.ToString(Culture)); Builder.AppendLine("disableDisplayLists = " + (CurrentOptions.DisableDisplayLists ? "true" : "false")); Builder.AppendLine("loadInAdvance = " + (CurrentOptions.LoadInAdvance ? "true" : "false")); Builder.AppendLine("noTextureResize = " + (CurrentOptions.NoTextureResize ? "true" : "false")); Builder.AppendLine(); Builder.AppendLine("[quality]"); { string t; switch (CurrentOptions.Interpolation) { case Interface.InterpolationMode.NearestNeighbor: t = "nearestNeighbor"; break; case Interface.InterpolationMode.Bilinear: t = "bilinear"; break; case Interface.InterpolationMode.NearestNeighborMipmapped: t = "nearestNeighborMipmapped"; break; case Interface.InterpolationMode.BilinearMipmapped: t = "bilinearMipmapped"; break; case Interface.InterpolationMode.TrilinearMipmapped: t = "trilinearMipmapped"; break; case Interface.InterpolationMode.AnisotropicFiltering: t = "anisotropicFiltering"; break; default: t = "bilinearMipmapped"; break; } Builder.AppendLine("interpolation = " + t); } Builder.AppendLine("anisotropicFilteringLevel = " + CurrentOptions.AnisotropicFilteringLevel.ToString(Culture)); Builder.AppendLine("anisotropicFilteringMaximum = " + CurrentOptions.AnisotropicFilteringMaximum.ToString(Culture)); Builder.AppendLine("transparencyMode = " + ((int)CurrentOptions.TransparencyMode).ToString(Culture)); Builder.AppendLine("viewingDistance = " + CurrentOptions.ViewingDistance.ToString(Culture)); { string t; switch (CurrentOptions.MotionBlur) { case MotionBlurMode.Low: t = "low"; break; case MotionBlurMode.Medium: t = "medium"; break; case MotionBlurMode.High: t = "high"; break; default: t = "none"; break; } Builder.AppendLine("motionBlur = " + t); } Builder.AppendLine(); Builder.AppendLine("[objectOptimization]"); Builder.AppendLine("basicThreshold = " + CurrentOptions.ObjectOptimizationBasicThreshold.ToString(Culture)); Builder.AppendLine("fullThreshold = " + CurrentOptions.ObjectOptimizationFullThreshold.ToString(Culture)); Builder.AppendLine(); Builder.AppendLine("[simulation]"); Builder.AppendLine("toppling = " + (CurrentOptions.Toppling ? "true" : "false")); Builder.AppendLine("collisions = " + (CurrentOptions.Collisions ? "true" : "false")); Builder.AppendLine("derailments = " + (CurrentOptions.Derailments ? "true" : "false")); Builder.AppendLine("blackbox = " + (CurrentOptions.BlackBox ? "true" : "false")); Builder.Append("mode = "); switch (CurrentOptions.GameMode) { case Interface.GameMode.Arcade: Builder.AppendLine("arcade"); break; case Interface.GameMode.Normal: Builder.AppendLine("normal"); break; case Interface.GameMode.Expert: Builder.AppendLine("expert"); break; default: Builder.AppendLine("normal"); break; } Builder.AppendLine(); Builder.AppendLine("[verbosity]"); Builder.AppendLine("showWarningMessages = " + (CurrentOptions.ShowWarningMessages ? "true" : "false")); Builder.AppendLine("showErrorMessages = " + (CurrentOptions.ShowErrorMessages ? "true" : "false")); Builder.AppendLine(); Builder.AppendLine("[controls]"); Builder.AppendLine("useJoysticks = " + (CurrentOptions.UseJoysticks ? "true" : "false")); Builder.AppendLine("joystickAxisthreshold = " + CurrentOptions.JoystickAxisThreshold.ToString(Culture)); Builder.AppendLine("keyRepeatDelay = " + (1000.0 * CurrentOptions.KeyRepeatDelay).ToString("0", Culture)); Builder.AppendLine("keyRepeatInterval = " + (1000.0 * CurrentOptions.KeyRepeatInterval).ToString("0", Culture)); Builder.AppendLine(); Builder.AppendLine("[sound]"); Builder.Append("model = "); switch (CurrentOptions.SoundModel) { case Sounds.SoundModels.Experimental: Builder.AppendLine("experimental"); break; default: Builder.AppendLine("default"); break; } Builder.Append("range = "); switch (CurrentOptions.SoundRange) { case SoundRange.Low: Builder.AppendLine("low"); break; case SoundRange.Medium: Builder.AppendLine("medium"); break; case SoundRange.High: Builder.AppendLine("high"); break; default: Builder.AppendLine("low"); break; } Builder.AppendLine("number = " + CurrentOptions.SoundNumber.ToString(Culture)); Builder.AppendLine(); Builder.AppendLine("[folders]"); Builder.AppendLine("route = " + CurrentOptions.RouteFolder); Builder.AppendLine("train = " + CurrentOptions.TrainFolder); Builder.AppendLine(); Builder.AppendLine("[recentlyUsedRoutes]"); for (int i = 0; i < CurrentOptions.RecentlyUsedRoutes.Length; i++) { Builder.AppendLine(CurrentOptions.RecentlyUsedRoutes[i]); } Builder.AppendLine(); Builder.AppendLine("[recentlyUsedTrains]"); for (int i = 0; i < CurrentOptions.RecentlyUsedTrains.Length; i++) { Builder.AppendLine(CurrentOptions.RecentlyUsedTrains[i]); } Builder.AppendLine(); Builder.AppendLine("[routeEncodings]"); for (int i = 0; i < CurrentOptions.RouteEncodings.Length; i++) { Builder.AppendLine(CurrentOptions.RouteEncodings[i].Codepage.ToString(Culture) + " = " + CurrentOptions.RouteEncodings[i].Value); } Builder.AppendLine(); Builder.AppendLine("[trainEncodings]"); for (int i = 0; i < CurrentOptions.TrainEncodings.Length; i++) { Builder.AppendLine(CurrentOptions.TrainEncodings[i].Codepage.ToString(Culture) + " = " + CurrentOptions.TrainEncodings[i].Value); } string File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "options.cfg"); System.IO.File.WriteAllText(File, Builder.ToString(), new System.Text.UTF8Encoding(true)); } // ================================ // load logs internal static void LoadLogs() { string File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "logs.bin"); try { using (System.IO.FileStream Stream = new System.IO.FileStream(File, System.IO.FileMode.Open, System.IO.FileAccess.Read)) { using (System.IO.BinaryReader Reader = new System.IO.BinaryReader(Stream, System.Text.Encoding.UTF8)) { byte[] Identifier = new byte[] { 111, 112, 101, 110, 66, 86, 69, 95, 76, 79, 71, 83 }; const short Version = 1; byte[] Data = Reader.ReadBytes(Identifier.Length); for (int i = 0; i < Identifier.Length; i++) { if (Identifier[i] != Data[i]) throw new System.IO.InvalidDataException(); } short Number = Reader.ReadInt16(); if (Version != Number) throw new System.IO.InvalidDataException(); Game.LogRouteName = Reader.ReadString(); Game.LogTrainName = Reader.ReadString(); Game.LogDateTime = DateTime.FromBinary(Reader.ReadInt64()); Interface.CurrentOptions.GameMode = (Interface.GameMode)Reader.ReadInt16(); Game.BlackBoxEntryCount = Reader.ReadInt32(); Game.BlackBoxEntries = new Game.BlackBoxEntry[Game.BlackBoxEntryCount]; for (int i = 0; i < Game.BlackBoxEntryCount; i++) { Game.BlackBoxEntries[i].Time = Reader.ReadDouble(); Game.BlackBoxEntries[i].Position = Reader.ReadDouble(); Game.BlackBoxEntries[i].Speed = Reader.ReadSingle(); Game.BlackBoxEntries[i].Acceleration = Reader.ReadSingle(); Game.BlackBoxEntries[i].ReverserDriver = Reader.ReadInt16(); Game.BlackBoxEntries[i].ReverserSafety = Reader.ReadInt16(); Game.BlackBoxEntries[i].PowerDriver = (Game.BlackBoxPower)Reader.ReadInt16(); Game.BlackBoxEntries[i].PowerSafety = (Game.BlackBoxPower)Reader.ReadInt16(); Game.BlackBoxEntries[i].BrakeDriver = (Game.BlackBoxBrake)Reader.ReadInt16(); Game.BlackBoxEntries[i].BrakeSafety = (Game.BlackBoxBrake)Reader.ReadInt16(); Game.BlackBoxEntries[i].EventToken = (Game.BlackBoxEventToken)Reader.ReadInt16(); } Game.ScoreLogCount = Reader.ReadInt32(); Game.ScoreLogs = new Game.ScoreLog[Game.ScoreLogCount]; Game.CurrentScore.Value = 0; for (int i = 0; i < Game.ScoreLogCount; i++) { Game.ScoreLogs[i].Time = Reader.ReadDouble(); Game.ScoreLogs[i].Position = Reader.ReadDouble(); Game.ScoreLogs[i].Value = Reader.ReadInt32(); Game.ScoreLogs[i].TextToken = (Game.ScoreTextToken)Reader.ReadInt16(); Game.CurrentScore.Value += Game.ScoreLogs[i].Value; } Game.CurrentScore.Maximum = Reader.ReadInt32(); Identifier = new byte[] { 95, 102, 105, 108, 101, 69, 78, 68 }; Data = Reader.ReadBytes(Identifier.Length); for (int i = 0; i < Identifier.Length; i++) { if (Identifier[i] != Data[i]) throw new System.IO.InvalidDataException(); } Reader.Close(); } Stream.Close(); } } catch { Game.LogRouteName = ""; Game.LogTrainName = ""; Game.LogDateTime = DateTime.Now; Game.BlackBoxEntries = new Game.BlackBoxEntry[256]; Game.BlackBoxEntryCount = 0; Game.ScoreLogs = new Game.ScoreLog[64]; Game.ScoreLogCount = 0; } } // save logs internal static void SaveLogs() { string File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "logs.bin"); using (System.IO.FileStream Stream = new System.IO.FileStream(File, System.IO.FileMode.Create, System.IO.FileAccess.Write)) { using (System.IO.BinaryWriter Writer = new System.IO.BinaryWriter(Stream, System.Text.Encoding.UTF8)) { byte[] Identifier = new byte[] { 111, 112, 101, 110, 66, 86, 69, 95, 76, 79, 71, 83 }; const short Version = 1; Writer.Write(Identifier); Writer.Write(Version); Writer.Write(Game.LogRouteName); Writer.Write(Game.LogTrainName); Writer.Write(Game.LogDateTime.ToBinary()); Writer.Write((short)Interface.CurrentOptions.GameMode); Writer.Write(Game.BlackBoxEntryCount); for (int i = 0; i < Game.BlackBoxEntryCount; i++) { Writer.Write(Game.BlackBoxEntries[i].Time); Writer.Write(Game.BlackBoxEntries[i].Position); Writer.Write(Game.BlackBoxEntries[i].Speed); Writer.Write(Game.BlackBoxEntries[i].Acceleration); Writer.Write(Game.BlackBoxEntries[i].ReverserDriver); Writer.Write(Game.BlackBoxEntries[i].ReverserSafety); Writer.Write((short)Game.BlackBoxEntries[i].PowerDriver); Writer.Write((short)Game.BlackBoxEntries[i].PowerSafety); Writer.Write((short)Game.BlackBoxEntries[i].BrakeDriver); Writer.Write((short)Game.BlackBoxEntries[i].BrakeSafety); Writer.Write((short)Game.BlackBoxEntries[i].EventToken); } Writer.Write(Game.ScoreLogCount); for (int i = 0; i < Game.ScoreLogCount; i++) { Writer.Write(Game.ScoreLogs[i].Time); Writer.Write(Game.ScoreLogs[i].Position); Writer.Write(Game.ScoreLogs[i].Value); Writer.Write((short)Game.ScoreLogs[i].TextToken); } Writer.Write(Game.CurrentScore.Maximum); Identifier = new byte[] { 95, 102, 105, 108, 101, 69, 78, 68 }; Writer.Write(Identifier); Writer.Close(); } Stream.Close(); } } // get score text internal static string GetScoreText(Game.ScoreTextToken TextToken) { switch (TextToken) { case Game.ScoreTextToken.Overspeed: return GetInterfaceString("score_overspeed"); case Game.ScoreTextToken.PassedRedSignal: return GetInterfaceString("score_redsignal"); case Game.ScoreTextToken.Toppling: return GetInterfaceString("score_toppling"); case Game.ScoreTextToken.Derailed: return GetInterfaceString("score_derailed"); case Game.ScoreTextToken.PassengerDiscomfort: return GetInterfaceString("score_discomfort"); case Game.ScoreTextToken.DoorsOpened: return GetInterfaceString("score_doors"); case Game.ScoreTextToken.ArrivedAtStation: return GetInterfaceString("score_station_arrived"); case Game.ScoreTextToken.PerfectTimeBonus: return GetInterfaceString("score_station_perfecttime"); case Game.ScoreTextToken.Late: return GetInterfaceString("score_station_late"); case Game.ScoreTextToken.PerfectStopBonus: return GetInterfaceString("score_station_perfectstop"); case Game.ScoreTextToken.Stop: return GetInterfaceString("score_station_stop"); case Game.ScoreTextToken.PrematureDeparture: return GetInterfaceString("score_station_departure"); case Game.ScoreTextToken.Total: return GetInterfaceString("score_station_total"); default: return "?"; } } // get black box text internal static string GetBlackBoxText(Game.BlackBoxEventToken EventToken) { switch (EventToken) { default: return ""; } } // export score internal static void ExportScore(string File) { CultureInfo Culture = CultureInfo.InvariantCulture; System.Text.StringBuilder Builder = new System.Text.StringBuilder(); string[][] Lines = new string[Game.ScoreLogCount + 1][]; Lines[0] = new string[] { GetInterfaceString("log_time"), GetInterfaceString("log_position"), GetInterfaceString("log_value"), GetInterfaceString("log_cumulative"), GetInterfaceString("log_reason") }; int Columns = Lines[0].Length; int TotalScore = 0; for (int i = 0; i < Game.ScoreLogCount; i++) { int j = i + 1; Lines[j] = new string[Columns]; { double x = Game.ScoreLogs[i].Time; int h = (int)Math.Floor(x / 3600.0); x -= (double)h * 3600.0; int m = (int)Math.Floor(x / 60.0); x -= (double)m * 60.0; int s = (int)Math.Floor(x); Lines[j][0] = h.ToString("00", Culture) + ":" + m.ToString("00", Culture) + ":" + s.ToString("00", Culture); } Lines[j][1] = Game.ScoreLogs[i].Position.ToString("0", Culture); Lines[j][2] = Game.ScoreLogs[i].Value.ToString(Culture); TotalScore += Game.ScoreLogs[i].Value; Lines[j][3] = TotalScore.ToString(Culture); Lines[j][4] = GetScoreText(Game.ScoreLogs[i].TextToken); } int[] Widths = new int[Columns]; for (int i = 0; i < Lines.Length; i++) { for (int j = 0; j < Columns; j++) { if (Lines[i][j].Length > Widths[j]) { Widths[j] = Lines[i][j].Length; } } } { /// header rows int TotalWidth = 0; for (int j = 0; j < Columns; j++) { TotalWidth += Widths[j] + 2; } TotalWidth += Columns - 1; Builder.Append('╔'); Builder.Append('═', TotalWidth); Builder.Append("╗\n"); { Builder.Append('║'); Builder.Append((" " + GetInterfaceString("log_route") + " " + Game.LogRouteName).PadRight(TotalWidth, ' ')); Builder.Append("║\n║"); Builder.Append((" " + GetInterfaceString("log_train") + " " + Game.LogTrainName).PadRight(TotalWidth, ' ')); Builder.Append("║\n║"); Builder.Append((" " + GetInterfaceString("log_date") + " " + Game.LogDateTime.ToString("yyyy-MM-dd HH:mm:ss", Culture)).PadRight(TotalWidth, ' ')); Builder.Append("║\n"); } Builder.Append('╠'); Builder.Append('═', TotalWidth); Builder.Append("╣\n"); { double ratio = Game.CurrentScore.Maximum == 0 ? 0.0 : (double)Game.CurrentScore.Value / (double)Game.CurrentScore.Maximum; if (ratio < 0.0) ratio = 0.0; if (ratio > 1.0) ratio = 1.0; int index = (int)Math.Floor(ratio * (double)Interface.RatingsCount); if (index >= Interface.RatingsCount) index = Interface.RatingsCount - 1; string s; switch (Interface.CurrentOptions.GameMode) { case Interface.GameMode.Arcade: s = GetInterfaceString("mode_arcade"); break; case Interface.GameMode.Normal: s = GetInterfaceString("mode_normal"); break; case Interface.GameMode.Expert: s = GetInterfaceString("mode_expert"); break; default: s = GetInterfaceString("mode_unknown"); break; } Builder.Append('║'); Builder.Append((" " + GetInterfaceString("log_mode") + " " + s).PadRight(TotalWidth, ' ')); Builder.Append("║\n║"); Builder.Append((" " + GetInterfaceString("log_score") + " " + Game.CurrentScore.Value.ToString(Culture) + " / " + Game.CurrentScore.Maximum.ToString(Culture)).PadRight(TotalWidth, ' ')); Builder.Append("║\n║"); Builder.Append((" " + GetInterfaceString("log_rating") + " " + GetInterfaceString("rating_" + index.ToString(Culture)) + " (" + (100.0 * ratio).ToString("0.00") + "%)").PadRight(TotalWidth, ' ')); Builder.Append("║\n"); } } { /// top border row Builder.Append('╠'); for (int j = 0; j < Columns; j++) { if (j != 0) { Builder.Append('╤'); } Builder.Append('═', Widths[j] + 2); } Builder.Append("╣\n"); } for (int i = 0; i < Lines.Length; i++) { /// center border row if (i != 0) { Builder.Append('╟'); for (int j = 0; j < Columns; j++) { if (j != 0) { Builder.Append('┼'); } Builder.Append('─', Widths[j] + 2); } Builder.Append("╢\n"); } /// cell content Builder.Append('║'); for (int j = 0; j < Columns; j++) { if (j != 0) Builder.Append('│'); Builder.Append(' '); if (i != 0 & j <= 3) { Builder.Append(Lines[i][j].PadLeft(Widths[j], ' ')); } else { Builder.Append(Lines[i][j].PadRight(Widths[j], ' ')); } Builder.Append(' '); } Builder.Append("║\n"); } { /// bottom border row Builder.Append('╚'); for (int j = 0; j < Columns; j++) { if (j != 0) { Builder.Append('╧'); } Builder.Append('═', Widths[j] + 2); } Builder.Append('╝'); } System.IO.File.WriteAllText(File, Builder.ToString(), new System.Text.UTF8Encoding(true)); } // export black box internal enum BlackBoxFormat { CommaSeparatedValue = 0, FormattedText = 1 } internal static void ExportBlackBox(string File, BlackBoxFormat Format) { switch (Format) { // comma separated value case BlackBoxFormat.CommaSeparatedValue: { CultureInfo Culture = CultureInfo.InvariantCulture; System.Text.StringBuilder Builder = new System.Text.StringBuilder(); for (int i = 0; i < Game.BlackBoxEntryCount; i++) { Builder.Append(Game.BlackBoxEntries[i].Time.ToString(Culture) + ","); Builder.Append(Game.BlackBoxEntries[i].Position.ToString(Culture) + ","); Builder.Append(Game.BlackBoxEntries[i].Speed.ToString(Culture) + ","); Builder.Append(Game.BlackBoxEntries[i].Acceleration.ToString(Culture) + ","); Builder.Append(((short)Game.BlackBoxEntries[i].ReverserDriver).ToString(Culture) + ","); Builder.Append(((short)Game.BlackBoxEntries[i].ReverserSafety).ToString(Culture) + ","); Builder.Append(((short)Game.BlackBoxEntries[i].PowerDriver).ToString(Culture) + ","); Builder.Append(((short)Game.BlackBoxEntries[i].PowerSafety).ToString(Culture) + ","); Builder.Append(((short)Game.BlackBoxEntries[i].BrakeDriver).ToString(Culture) + ","); Builder.Append(((short)Game.BlackBoxEntries[i].BrakeSafety).ToString(Culture) + ","); Builder.Append(((short)Game.BlackBoxEntries[i].EventToken).ToString(Culture)); Builder.Append("\r\n"); } System.IO.File.WriteAllText(File, Builder.ToString(), new System.Text.UTF8Encoding(true)); } break; // formatted text case BlackBoxFormat.FormattedText: { CultureInfo Culture = CultureInfo.InvariantCulture; System.Text.StringBuilder Builder = new System.Text.StringBuilder(); string[][] Lines = new string[Game.BlackBoxEntryCount + 1][]; Lines[0] = new string[] { GetInterfaceString("log_time"), GetInterfaceString("log_position"), GetInterfaceString("log_speed"), GetInterfaceString("log_acceleration"), GetInterfaceString("log_reverser"), GetInterfaceString("log_power"), GetInterfaceString("log_brake"), GetInterfaceString("log_event"), }; int Columns = Lines[0].Length; for (int i = 0; i < Game.BlackBoxEntryCount; i++) { int j = i + 1; Lines[j] = new string[Columns]; { double x = Game.BlackBoxEntries[i].Time; int h = (int)Math.Floor(x / 3600.0); x -= (double)h * 3600.0; int m = (int)Math.Floor(x / 60.0); x -= (double)m * 60.0; int s = (int)Math.Floor(x); x -= (double)s; int n = (int)Math.Floor(1000.0 * x); Lines[j][0] = h.ToString("00", Culture) + ":" + m.ToString("00", Culture) + ":" + s.ToString("00", Culture) + ":" + n.ToString("000", Culture); } Lines[j][1] = Game.BlackBoxEntries[i].Position.ToString("0.000", Culture); Lines[j][2] = Game.BlackBoxEntries[i].Speed.ToString("0.0000", Culture); Lines[j][3] = Game.BlackBoxEntries[i].Acceleration.ToString("0.0000", Culture); { string[] reverser = new string[2]; for (int k = 0; k < 2; k++) { short r = k == 0 ? Game.BlackBoxEntries[i].ReverserDriver : Game.BlackBoxEntries[i].ReverserSafety; switch (r) { case -1: reverser[k] = QuickReferences.HandleBackward; break; case 0: reverser[k] = QuickReferences.HandleNeutral; break; case 1: reverser[k] = QuickReferences.HandleForward; break; default: reverser[k] = r.ToString(Culture); break; } } Lines[j][4] = reverser[0] + " → " + reverser[1]; } { string[] power = new string[2]; for (int k = 0; k < 2; k++) { Game.BlackBoxPower p = k == 0 ? Game.BlackBoxEntries[i].PowerDriver : Game.BlackBoxEntries[i].PowerSafety; switch (p) { case Game.BlackBoxPower.PowerNull: power[k] = GetInterfaceString(QuickReferences.HandlePowerNull); break; default: power[k] = GetInterfaceString(QuickReferences.HandlePower) + ((short)p).ToString(Culture); break; } } Lines[j][5] = power[0] + " → " + power[1]; } { string[] brake = new string[2]; for (int k = 0; k < 2; k++) { Game.BlackBoxBrake b = k == 0 ? Game.BlackBoxEntries[i].BrakeDriver : Game.BlackBoxEntries[i].BrakeSafety; switch (b) { case Game.BlackBoxBrake.BrakeNull: brake[k] = GetInterfaceString(QuickReferences.HandleBrakeNull); break; case Game.BlackBoxBrake.Emergency: brake[k] = GetInterfaceString(QuickReferences.HandleEmergency); break; case Game.BlackBoxBrake.HoldBrake: brake[k] = GetInterfaceString(QuickReferences.HandleHoldBrake); break; case Game.BlackBoxBrake.Release: brake[k] = GetInterfaceString(QuickReferences.HandleRelease); break; case Game.BlackBoxBrake.Lap: brake[k] = GetInterfaceString(QuickReferences.HandleLap); break; case Game.BlackBoxBrake.Service: brake[k] = GetInterfaceString(QuickReferences.HandleService); break; default: brake[k] = GetInterfaceString(QuickReferences.HandleBrake) + ((short)b).ToString(Culture); break; } } Lines[j][6] = brake[0] + " → " + brake[1]; } Lines[j][7] = GetBlackBoxText(Game.BlackBoxEntries[i].EventToken); } int[] Widths = new int[Columns]; for (int i = 0; i < Lines.Length; i++) { for (int j = 0; j < Columns; j++) { if (Lines[i][j].Length > Widths[j]) { Widths[j] = Lines[i][j].Length; } } } { /// header rows int TotalWidth = 0; for (int j = 0; j < Columns; j++) { TotalWidth += Widths[j] + 2; } TotalWidth += Columns - 1; Builder.Append('╔'); Builder.Append('═', TotalWidth); Builder.Append("╗\r\n"); { Builder.Append('║'); Builder.Append((" " + GetInterfaceString("log_route") + " " + Game.LogRouteName).PadRight(TotalWidth, ' ')); Builder.Append("║\r\n║"); Builder.Append((" " + GetInterfaceString("log_train") + " " + Game.LogTrainName).PadRight(TotalWidth, ' ')); Builder.Append("║\r\n║"); Builder.Append((" " + GetInterfaceString("log_date") + " " + Game.LogDateTime.ToString("yyyy-MM-dd HH:mm:ss", Culture)).PadRight(TotalWidth, ' ')); Builder.Append("║\r\n"); } } { /// top border row Builder.Append('╠'); for (int j = 0; j < Columns; j++) { if (j != 0) { Builder.Append('╤'); } Builder.Append('═', Widths[j] + 2); } Builder.Append("╣\r\n"); } for (int i = 0; i < Lines.Length; i++) { /// center border row if (i != 0) { Builder.Append('╟'); for (int j = 0; j < Columns; j++) { if (j != 0) { Builder.Append('┼'); } Builder.Append('─', Widths[j] + 2); } Builder.Append("╢\r\n"); } /// cell content Builder.Append('║'); for (int j = 0; j < Columns; j++) { if (j != 0) Builder.Append('│'); Builder.Append(' '); if (i != 0 & j <= 3) { Builder.Append(Lines[i][j].PadLeft(Widths[j], ' ')); } else { Builder.Append(Lines[i][j].PadRight(Widths[j], ' ')); } Builder.Append(' '); } Builder.Append("║\r\n"); } { /// bottom border row Builder.Append('╚'); for (int j = 0; j < Columns; j++) { if (j != 0) { Builder.Append('╧'); } Builder.Append('═', Widths[j] + 2); } Builder.Append('╝'); } System.IO.File.WriteAllText(File, Builder.ToString(), new System.Text.UTF8Encoding(true)); } break; } } // ================================ // interface strings private struct InterfaceString { internal string Name; internal string Text; } private static InterfaceString[] InterfaceStrings = new InterfaceString[16]; private static int InterfaceStringCount = 0; private static int CurrentInterfaceStringIndex = 0; private static void AddInterfaceString(string Name, string Text) { if (InterfaceStringCount >= InterfaceStrings.Length) { Array.Resize(ref InterfaceStrings, InterfaceStrings.Length << 1); } InterfaceStrings[InterfaceStringCount].Name = Name; InterfaceStrings[InterfaceStringCount].Text = Text; InterfaceStringCount++; } internal static string GetInterfaceString(string Name) { int n = Name.Length; for (int k = 0; k < InterfaceStringCount; k++) { int i; if ((k & 1) == 0) { i = (CurrentInterfaceStringIndex + (k >> 1) + InterfaceStringCount) % InterfaceStringCount; } else { i = (CurrentInterfaceStringIndex - (k + 1 >> 1) + InterfaceStringCount) % InterfaceStringCount; } if (InterfaceStrings[i].Name.Length == n) { if (InterfaceStrings[i].Name == Name) { CurrentInterfaceStringIndex = (i + 1) % InterfaceStringCount; return InterfaceStrings[i].Text; } } } return Name; } internal struct InterfaceQuickReference { internal string HandleForward; internal string HandleNeutral; internal string HandleBackward; internal string HandlePower; internal string HandlePowerNull; internal string HandleBrake; internal string HandleBrakeNull; internal string HandleRelease; internal string HandleLap; internal string HandleService; internal string HandleEmergency; internal string HandleHoldBrake; internal string DoorsLeft; internal string DoorsRight; internal string Score; } internal static InterfaceQuickReference QuickReferences; internal static int RatingsCount = 10; // load language internal static void LoadLanguage(string File) { string[] Lines = System.IO.File.ReadAllLines(File, new System.Text.UTF8Encoding()); string Section = ""; InterfaceStrings = new InterfaceString[16]; InterfaceStringCount = 0; QuickReferences.HandleForward = "F"; QuickReferences.HandleNeutral = "N"; QuickReferences.HandleBackward = "B"; QuickReferences.HandlePower = "P"; QuickReferences.HandlePowerNull = "N"; QuickReferences.HandleBrake = "B"; QuickReferences.HandleBrakeNull = "N"; QuickReferences.HandleRelease = "RL"; QuickReferences.HandleLap = "LP"; QuickReferences.HandleService = "SV"; QuickReferences.HandleEmergency = "EM"; QuickReferences.HandleHoldBrake = "HB"; QuickReferences.DoorsLeft = "L"; QuickReferences.DoorsRight = "R"; QuickReferences.Score = "Score: "; for (int i = 0; i < Lines.Length; i++) { Lines[i] = Lines[i].Trim(); if (!Lines[i].StartsWith(";")) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim().ToLowerInvariant(); } else { int j = Lines[i].IndexOf('='); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd().ToLowerInvariant(); string b = Interface.Unescape(Lines[i].Substring(j + 1).TrimStart()); switch (Section) { case "handles": switch (a) { case "forward": Interface.QuickReferences.HandleForward = b; break; case "neutral": Interface.QuickReferences.HandleNeutral = b; break; case "backward": Interface.QuickReferences.HandleBackward = b; break; case "power": Interface.QuickReferences.HandlePower = b; break; case "powernull": Interface.QuickReferences.HandlePowerNull = b; break; case "brake": Interface.QuickReferences.HandleBrake = b; break; case "brakenull": Interface.QuickReferences.HandleBrakeNull = b; break; case "release": Interface.QuickReferences.HandleRelease = b; break; case "lap": Interface.QuickReferences.HandleLap = b; break; case "service": Interface.QuickReferences.HandleService = b; break; case "emergency": Interface.QuickReferences.HandleEmergency = b; break; case "holdbrake": Interface.QuickReferences.HandleHoldBrake = b; break; } break; case "doors": switch (a) { case "left": Interface.QuickReferences.DoorsLeft = b; break; case "right": Interface.QuickReferences.DoorsRight = b; break; } break; case "misc": switch (a) { case "score": Interface.QuickReferences.Score = b; break; } break; case "commands": { for (int k = 0; k < CommandInfos.Length; k++) { if (string.Compare(CommandInfos[k].Name, a, StringComparison.OrdinalIgnoreCase) == 0) { CommandInfos[k].Description = b; break; } } } break; case "keys": { for (int k = 0; k < Keys.Length; k++) { if (string.Compare(Keys[k].Name, a, StringComparison.OrdinalIgnoreCase) == 0) { Keys[k].Description = b; break; } } } break; default: AddInterfaceString(Section + "_" + a, b); break; } } } } } } // ================================ // commands internal enum Command { None = 0, PowerIncrease, PowerDecrease, PowerHalfAxis, PowerFullAxis, BrakeIncrease, BrakeDecrease, BrakeEmergency, BrakeHalfAxis, BrakeFullAxis, SinglePower, SingleNeutral, SingleBrake, SingleEmergency, SingleFullAxis, ReverserForward, ReverserBackward, ReverserFullAxis, DoorsLeft, DoorsRight, HornPrimary, HornSecondary, HornMusic, DeviceConstSpeed, SecurityS, SecurityA1, SecurityA2, SecurityB1, SecurityB2, SecurityC1, SecurityC2, SecurityD, SecurityE, SecurityF, SecurityG, SecurityH, SecurityI, SecurityJ, SecurityK, SecurityL, CameraInterior, CameraExterior, CameraTrack, CameraFlyBy, CameraMoveForward, CameraMoveBackward, CameraMoveLeft, CameraMoveRight, CameraMoveUp, CameraMoveDown, CameraRotateLeft, CameraRotateRight, CameraRotateUp, CameraRotateDown, CameraRotateCCW, CameraRotateCW, CameraZoomIn, CameraZoomOut, CameraPreviousPOI, CameraNextPOI, CameraReset, CameraRestriction, TimetableToggle, TimetableUp, TimetableDown, MiscClock, MiscSpeed, MiscFps, MiscAI, MiscInterfaceMode, MiscBackfaceCulling, MiscCPUMode, MiscTimeFactor, MiscPause, MiscMute, MiscFullscreen, MiscQuit, MenuActivate, MenuUp, MenuDown, MenuEnter, MenuBack, DebugWireframe, DebugNormals, DebugBrakeSystems } internal enum CommandType { Digital, AnalogHalf, AnalogFull } internal struct CommandInfo { internal Command Command; internal CommandType Type; internal string Name; internal string Description; internal CommandInfo(Command Command, CommandType Type, string Name) { this.Command = Command; this.Type = Type; this.Name = Name; this.Description = "N/A"; } } // key infos internal struct KeyInfo { internal int Value; internal string Name; internal string Description; internal KeyInfo(int Value, string Name, string Description) { this.Value = Value; this.Name = Name; this.Description = Description; } } internal static KeyInfo[] Keys = new KeyInfo[] { new KeyInfo(Sdl.SDLK_0, "0", "0"), new KeyInfo(Sdl.SDLK_1, "1", "1"), new KeyInfo(Sdl.SDLK_2, "2", "2"), new KeyInfo(Sdl.SDLK_3, "3", "3"), new KeyInfo(Sdl.SDLK_4, "4", "4"), new KeyInfo(Sdl.SDLK_5, "5", "5"), new KeyInfo(Sdl.SDLK_6, "6", "6"), new KeyInfo(Sdl.SDLK_7, "7", "7"), new KeyInfo(Sdl.SDLK_8, "8", "8"), new KeyInfo(Sdl.SDLK_9, "9", "9"), new KeyInfo(Sdl.SDLK_AMPERSAND, "AMPERSAND", "Ampersand"), new KeyInfo(Sdl.SDLK_ASTERISK, "ASTERISK", "Asterisk"), new KeyInfo(Sdl.SDLK_AT, "AT", "At"), new KeyInfo(Sdl.SDLK_BACKQUOTE, "BACKQUOTE", "Backquote"), new KeyInfo(Sdl.SDLK_BACKSLASH, "BACKSLASH", "Backslash"), new KeyInfo(Sdl.SDLK_BACKSPACE, "BACKSPACE", "Backspace"), new KeyInfo(Sdl.SDLK_BREAK, "BREAK", "Break"), new KeyInfo(Sdl.SDLK_CAPSLOCK, "CAPSLOCK", "Capslock"), new KeyInfo(Sdl.SDLK_CARET, "CARET", "Caret"), new KeyInfo(Sdl.SDLK_CLEAR, "CLEAR", "Clear"), new KeyInfo(Sdl.SDLK_COLON, "COLON", "Colon"), new KeyInfo(Sdl.SDLK_COMMA, "COMMA", "Comma"), new KeyInfo(Sdl.SDLK_DELETE, "DELETE", "Delete"), new KeyInfo(Sdl.SDLK_DOLLAR, "DOLLAR", "Dollar"), new KeyInfo(Sdl.SDLK_DOWN, "DOWN", "Down"), new KeyInfo(Sdl.SDLK_END, "END", "End"), new KeyInfo(Sdl.SDLK_EQUALS, "EQUALS", "Equals"), new KeyInfo(Sdl.SDLK_ESCAPE, "ESCAPE", "Escape"), new KeyInfo(Sdl.SDLK_EURO, "EURO", "Euro"), new KeyInfo(Sdl.SDLK_EXCLAIM, "EXCLAIM", "Exclamation"), new KeyInfo(Sdl.SDLK_F1, "F1", "F1"), new KeyInfo(Sdl.SDLK_F2, "F2", "F2"), new KeyInfo(Sdl.SDLK_F3, "F3", "F3"), new KeyInfo(Sdl.SDLK_F4, "F4", "F4"), new KeyInfo(Sdl.SDLK_F5, "F5", "F5"), new KeyInfo(Sdl.SDLK_F6, "F6", "F6"), new KeyInfo(Sdl.SDLK_F7, "F7", "F7"), new KeyInfo(Sdl.SDLK_F8, "F8", "F8"), new KeyInfo(Sdl.SDLK_F9, "F9", "F9"), new KeyInfo(Sdl.SDLK_F10, "F10", "F10"), new KeyInfo(Sdl.SDLK_F11, "F11", "F11"), new KeyInfo(Sdl.SDLK_F12, "F12", "F12"), new KeyInfo(Sdl.SDLK_F13, "F13", "F13"), new KeyInfo(Sdl.SDLK_F14, "F14", "F14"), new KeyInfo(Sdl.SDLK_F15, "F15", "F15"), new KeyInfo(Sdl.SDLK_GREATER, "GREATER", "Greater"), new KeyInfo(Sdl.SDLK_HASH, "HASH", "Hash"), new KeyInfo(Sdl.SDLK_HELP, "HELP", "Help"), new KeyInfo(Sdl.SDLK_HOME, "HOME", "Home"), new KeyInfo(Sdl.SDLK_INSERT, "INSERT", "Insert"), new KeyInfo(Sdl.SDLK_KP0, "KP0", "Keypad 0"), new KeyInfo(Sdl.SDLK_KP1, "KP1", "Keypad 1"), new KeyInfo(Sdl.SDLK_KP2, "KP2", "Keypad 2"), new KeyInfo(Sdl.SDLK_KP3, "KP3", "Keypad 3"), new KeyInfo(Sdl.SDLK_KP4, "KP4", "Keypad 4"), new KeyInfo(Sdl.SDLK_KP5, "KP5", "Keypad 5"), new KeyInfo(Sdl.SDLK_KP6, "KP6", "Keypad 6"), new KeyInfo(Sdl.SDLK_KP7, "KP7", "Keypad 7"), new KeyInfo(Sdl.SDLK_KP8, "KP8", "Keypad 8"), new KeyInfo(Sdl.SDLK_KP9, "KP9", "Keypad 9"), new KeyInfo(Sdl.SDLK_KP_DIVIDE, "KP_DIVIDE", "Keypad Divide"), new KeyInfo(Sdl.SDLK_KP_ENTER, "KP_ENTER", "Keypad Enter"), new KeyInfo(Sdl.SDLK_KP_EQUALS, "KP_EQUALS", "Keypad Equals"), new KeyInfo(Sdl.SDLK_KP_MINUS, "KP_MINUS", "Keypad Minus"), new KeyInfo(Sdl.SDLK_KP_MULTIPLY, "KP_MULTIPLY", "Keypad Multiply"), new KeyInfo(Sdl.SDLK_KP_PERIOD, "KP_PERIOD", "Keypad Period"), new KeyInfo(Sdl.SDLK_KP_PLUS, "KP_PLUS", "Keypad Plus"), new KeyInfo(Sdl.SDLK_LALT, "LALT", "Left Alt"), new KeyInfo(Sdl.SDLK_LCTRL, "LCTRL", "Left Ctrl"), new KeyInfo(Sdl.SDLK_LEFT, "LEFT", "Left"), new KeyInfo(Sdl.SDLK_LEFTBRACKET, "LEFTBRACKET", "Left bracket"), new KeyInfo(Sdl.SDLK_LEFTPAREN, "LEFTPAREN", "Left parenthesis"), new KeyInfo(Sdl.SDLK_LESS, "LESS", "Less"), new KeyInfo(Sdl.SDLK_LMETA, "LMETA", "Left Meta"), new KeyInfo(Sdl.SDLK_LSHIFT, "LSHIFT", "Left Shift"), new KeyInfo(Sdl.SDLK_LSUPER, "LSUPER", "Left Application"), new KeyInfo(Sdl.SDLK_MENU, "MENU", "Menu"), new KeyInfo(Sdl.SDLK_MINUS, "MINUS", "Minus"), new KeyInfo(Sdl.SDLK_MODE, "MODE", "Alt Gr"), new KeyInfo(Sdl.SDLK_NUMLOCK, "NUMLOCK", "Numlock"), new KeyInfo(Sdl.SDLK_PAGEDOWN, "PAGEDOWN", "Page down"), new KeyInfo(Sdl.SDLK_PAGEUP, "PAGEUP", "Page up"), new KeyInfo(Sdl.SDLK_PAUSE, "PAUSE", "Pause"), new KeyInfo(Sdl.SDLK_PERIOD, "PERIOD", "Period"), new KeyInfo(Sdl.SDLK_PLUS, "PLUS", "Plus"), new KeyInfo(Sdl.SDLK_POWER, "POWER", "Power"), new KeyInfo(Sdl.SDLK_PRINT, "PRINT", "Print"), new KeyInfo(Sdl.SDLK_QUESTION, "QUESTION", "Question"), new KeyInfo(Sdl.SDLK_QUOTE, "QUOTE", "Quote"), new KeyInfo(Sdl.SDLK_QUOTEDBL, "QUOTEDBL", "Quote double"), new KeyInfo(Sdl.SDLK_RALT, "RALT", "Right Alt"), new KeyInfo(Sdl.SDLK_RCTRL, "RCTRL", "Right Ctrl"), new KeyInfo(Sdl.SDLK_RETURN, "RETURN", "Return"), new KeyInfo(Sdl.SDLK_RIGHT, "RIGHT", "Right"), new KeyInfo(Sdl.SDLK_RIGHTBRACKET, "RIGHTBRACKET", "Right bracket"), new KeyInfo(Sdl.SDLK_RIGHTPAREN, "RIGHTPAREN", "Right parenthesis"), new KeyInfo(Sdl.SDLK_RMETA, "RMETA", "Right Meta"), new KeyInfo(Sdl.SDLK_RSHIFT, "RSHIFT", "Right Shift"), new KeyInfo(Sdl.SDLK_RSUPER, "RSUPER", "Right Application"), new KeyInfo(Sdl.SDLK_SCROLLOCK, "SCROLLLOCK", "Scrolllock"), new KeyInfo(Sdl.SDLK_SEMICOLON, "SEMICOLON", "Semicolon"), new KeyInfo(Sdl.SDLK_SLASH, "SLASH", "Slash"), new KeyInfo(Sdl.SDLK_SPACE, "SPACE", "Space"), new KeyInfo(Sdl.SDLK_SYSREQ, "SYSREQ", "SysRq"), new KeyInfo(Sdl.SDLK_TAB, "TAB", "Tab"), new KeyInfo(Sdl.SDLK_UNDERSCORE, "UNDERSCORE", "Underscore"), new KeyInfo(Sdl.SDLK_UP, "UP", "Up"), new KeyInfo(Sdl.SDLK_a, "a", "A"), new KeyInfo(Sdl.SDLK_b, "b", "B"), new KeyInfo(Sdl.SDLK_c, "c", "C"), new KeyInfo(Sdl.SDLK_d, "d", "D"), new KeyInfo(Sdl.SDLK_e, "e", "E"), new KeyInfo(Sdl.SDLK_f, "f", "F"), new KeyInfo(Sdl.SDLK_g, "g", "G"), new KeyInfo(Sdl.SDLK_h, "h", "H"), new KeyInfo(Sdl.SDLK_i, "i", "I"), new KeyInfo(Sdl.SDLK_j, "j", "J"), new KeyInfo(Sdl.SDLK_k, "k", "K"), new KeyInfo(Sdl.SDLK_l, "l", "L"), new KeyInfo(Sdl.SDLK_m, "m", "M"), new KeyInfo(Sdl.SDLK_n, "n", "N"), new KeyInfo(Sdl.SDLK_o, "o", "O"), new KeyInfo(Sdl.SDLK_p, "p", "P"), new KeyInfo(Sdl.SDLK_q, "q", "Q"), new KeyInfo(Sdl.SDLK_r, "r", "R"), new KeyInfo(Sdl.SDLK_s, "s", "S"), new KeyInfo(Sdl.SDLK_t, "t", "T"), new KeyInfo(Sdl.SDLK_u, "u", "U"), new KeyInfo(Sdl.SDLK_v, "v", "V"), new KeyInfo(Sdl.SDLK_w, "w", "W"), new KeyInfo(Sdl.SDLK_x, "x", "X"), new KeyInfo(Sdl.SDLK_y, "y", "Y"), new KeyInfo(Sdl.SDLK_z, "z", "Z") }; // controls internal enum ControlMethod { Invalid = 0, Keyboard = 1, Joystick = 2 } internal enum KeyboardModifier { None = 0, Shift = 1, Ctrl = 2, Alt = 4 } internal enum JoystickComponent { Invalid, Axis, Ball, Hat, Button } internal enum DigitalControlState { ReleasedAcknowledged = 0, Released = 1, Pressed = 2, PressedAcknowledged = 3 } internal struct Control { internal Command Command; internal CommandType InheritedType; internal ControlMethod Method; internal KeyboardModifier Modifier; internal int Device; internal JoystickComponent Component; internal int Element; internal int Direction; internal DigitalControlState DigitalState; internal double AnalogState; } // control descriptions internal static string[] ControlDescriptions = new string[] { }; internal static CommandInfo[] CommandInfos = new CommandInfo[] { new CommandInfo(Command.PowerIncrease, CommandType.Digital, "POWER_INCREASE"), new CommandInfo(Command.PowerDecrease, CommandType.Digital, "POWER_DECREASE"), new CommandInfo(Command.PowerHalfAxis, CommandType.AnalogHalf, "POWER_HALFAXIS"), new CommandInfo(Command.PowerFullAxis, CommandType.AnalogFull, "POWER_FULLAXIS"), new CommandInfo(Command.BrakeDecrease, CommandType.Digital, "BRAKE_DECREASE"), new CommandInfo(Command.BrakeIncrease, CommandType.Digital, "BRAKE_INCREASE"), new CommandInfo(Command.BrakeHalfAxis, CommandType.AnalogHalf, "BRAKE_HALFAXIS"), new CommandInfo(Command.BrakeFullAxis, CommandType.AnalogFull, "BRAKE_FULLAXIS"), new CommandInfo(Command.BrakeEmergency, CommandType.Digital, "BRAKE_EMERGENCY"), new CommandInfo(Command.SinglePower, CommandType.Digital, "SINGLE_POWER"), new CommandInfo(Command.SingleNeutral, CommandType.Digital, "SINGLE_NEUTRAL"), new CommandInfo(Command.SingleBrake, CommandType.Digital, "SINGLE_BRAKE"), new CommandInfo(Command.SingleEmergency, CommandType.Digital, "SINGLE_EMERGENCY"), new CommandInfo(Command.SingleFullAxis, CommandType.AnalogFull, "SINGLE_FULLAXIS"), new CommandInfo(Command.ReverserForward, CommandType.Digital, "REVERSER_FORWARD"), new CommandInfo(Command.ReverserBackward, CommandType.Digital, "REVERSER_BACKWARD"), new CommandInfo(Command.ReverserFullAxis, CommandType.AnalogFull, "REVERSER_FULLAXIS"), new CommandInfo(Command.DoorsLeft, CommandType.Digital, "DOORS_LEFT"), new CommandInfo(Command.DoorsRight, CommandType.Digital, "DOORS_RIGHT"), new CommandInfo(Command.HornPrimary, CommandType.Digital, "HORN_PRIMARY"), new CommandInfo(Command.HornSecondary, CommandType.Digital, "HORN_SECONDARY"), new CommandInfo(Command.HornMusic, CommandType.Digital, "HORN_MUSIC"), new CommandInfo(Command.DeviceConstSpeed, CommandType.Digital, "DEVICE_CONSTSPEED"), new CommandInfo(Command.SecurityS, CommandType.Digital, "SECURITY_S"), new CommandInfo(Command.SecurityA1, CommandType.Digital, "SECURITY_A1"), new CommandInfo(Command.SecurityA2, CommandType.Digital, "SECURITY_A2"), new CommandInfo(Command.SecurityB1, CommandType.Digital, "SECURITY_B1"), new CommandInfo(Command.SecurityB2, CommandType.Digital, "SECURITY_B2"), new CommandInfo(Command.SecurityC1, CommandType.Digital, "SECURITY_C1"), new CommandInfo(Command.SecurityC2, CommandType.Digital, "SECURITY_C2"), new CommandInfo(Command.SecurityD, CommandType.Digital, "SECURITY_D"), new CommandInfo(Command.SecurityE, CommandType.Digital, "SECURITY_E"), new CommandInfo(Command.SecurityF, CommandType.Digital, "SECURITY_F"), new CommandInfo(Command.SecurityG, CommandType.Digital, "SECURITY_G"), new CommandInfo(Command.SecurityH, CommandType.Digital, "SECURITY_H"), new CommandInfo(Command.SecurityI, CommandType.Digital, "SECURITY_I"), new CommandInfo(Command.SecurityJ, CommandType.Digital, "SECURITY_J"), new CommandInfo(Command.SecurityK, CommandType.Digital, "SECURITY_K"), new CommandInfo(Command.SecurityL, CommandType.Digital, "SECURITY_L"), new CommandInfo(Command.CameraInterior, CommandType.Digital, "CAMERA_INTERIOR"), new CommandInfo(Command.CameraExterior, CommandType.Digital, "CAMERA_EXTERIOR"), new CommandInfo(Command.CameraTrack, CommandType.Digital, "CAMERA_TRACK"), new CommandInfo(Command.CameraFlyBy, CommandType.Digital, "CAMERA_FLYBY"), new CommandInfo(Command.CameraMoveForward, CommandType.AnalogHalf, "CAMERA_MOVE_FORWARD"), new CommandInfo(Command.CameraMoveBackward, CommandType.AnalogHalf, "CAMERA_MOVE_BACKWARD"), new CommandInfo(Command.CameraMoveLeft, CommandType.AnalogHalf, "CAMERA_MOVE_LEFT"), new CommandInfo(Command.CameraMoveRight, CommandType.AnalogHalf, "CAMERA_MOVE_RIGHT"), new CommandInfo(Command.CameraMoveUp, CommandType.AnalogHalf, "CAMERA_MOVE_UP"), new CommandInfo(Command.CameraMoveDown, CommandType.AnalogHalf, "CAMERA_MOVE_DOWN"), new CommandInfo(Command.CameraRotateLeft, CommandType.AnalogHalf, "CAMERA_ROTATE_LEFT"), new CommandInfo(Command.CameraRotateRight, CommandType.AnalogHalf, "CAMERA_ROTATE_RIGHT"), new CommandInfo(Command.CameraRotateUp, CommandType.AnalogHalf, "CAMERA_ROTATE_UP"), new CommandInfo(Command.CameraRotateDown, CommandType.AnalogHalf, "CAMERA_ROTATE_DOWN"), new CommandInfo(Command.CameraRotateCCW, CommandType.AnalogHalf, "CAMERA_ROTATE_CCW"), new CommandInfo(Command.CameraRotateCW, CommandType.AnalogHalf, "CAMERA_ROTATE_CW"), new CommandInfo(Command.CameraZoomIn, CommandType.AnalogHalf, "CAMERA_ZOOM_IN"), new CommandInfo(Command.CameraZoomOut, CommandType.AnalogHalf, "CAMERA_ZOOM_OUT"), new CommandInfo(Command.CameraPreviousPOI, CommandType.Digital, "CAMERA_POI_PREVIOUS"), new CommandInfo(Command.CameraNextPOI, CommandType.Digital, "CAMERA_POI_NEXT"), new CommandInfo(Command.CameraReset, CommandType.Digital, "CAMERA_RESET"), new CommandInfo(Command.CameraRestriction, CommandType.Digital, "CAMERA_RESTRICTION"), new CommandInfo(Command.TimetableToggle, CommandType.Digital, "TIMETABLE_TOGGLE"), new CommandInfo(Command.TimetableUp, CommandType.AnalogHalf, "TIMETABLE_UP"), new CommandInfo(Command.TimetableDown, CommandType.AnalogHalf, "TIMETABLE_DOWN"), new CommandInfo(Command.MenuActivate, CommandType.Digital, "MENU_ACTIVATE"), new CommandInfo(Command.MenuUp, CommandType.Digital, "MENU_UP"), new CommandInfo(Command.MenuDown, CommandType.Digital, "MENU_DOWN"), new CommandInfo(Command.MenuEnter, CommandType.Digital, "MENU_ENTER"), new CommandInfo(Command.MenuBack, CommandType.Digital, "MENU_BACK"), new CommandInfo(Command.MiscClock, CommandType.Digital, "MISC_CLOCK"), new CommandInfo(Command.MiscSpeed, CommandType.Digital, "MISC_SPEED"), new CommandInfo(Command.MiscFps, CommandType.Digital, "MISC_FPS"), new CommandInfo(Command.MiscAI, CommandType.Digital, "MISC_AI"), new CommandInfo(Command.MiscFullscreen, CommandType.Digital, "MISC_FULLSCREEN"), new CommandInfo(Command.MiscMute, CommandType.Digital, "MISC_MUTE"), new CommandInfo(Command.MiscPause, CommandType.Digital, "MISC_PAUSE"), new CommandInfo(Command.MiscTimeFactor, CommandType.Digital, "MISC_TIMEFACTOR"), new CommandInfo(Command.MiscQuit, CommandType.Digital, "MISC_QUIT"), new CommandInfo(Command.MiscInterfaceMode, CommandType.Digital, "MISC_INTERFACE"), new CommandInfo(Command.MiscBackfaceCulling, CommandType.Digital, "MISC_BACKFACE"), new CommandInfo(Command.MiscCPUMode, CommandType.Digital, "MISC_CPUMODE"), new CommandInfo(Command.DebugWireframe, CommandType.Digital, "DEBUG_WIREFRAME"), new CommandInfo(Command.DebugNormals, CommandType.Digital, "DEBUG_NORMALS"), new CommandInfo(Command.DebugBrakeSystems, CommandType.Digital, "DEBUG_BRAKE"), }; internal static Control[] CurrentControls = new Control[] { }; // try get command info internal static bool TryGetCommandInfo(Command Value, out CommandInfo Info) { for (int i = 0; i < CommandInfos.Length; i++) { if (CommandInfos[i].Command == Value) { Info = CommandInfos[i]; return true; } } Info.Command = Value; Info.Type = CommandType.Digital; Info.Name = "N/A"; Info.Description = "N/A"; return false; } // save controls internal static void SaveControls(string FileOrNull) { CultureInfo Culture = CultureInfo.InvariantCulture; System.Text.StringBuilder Builder = new System.Text.StringBuilder(); Builder.AppendLine("; Current control configuration"); Builder.AppendLine("; ============================="); Builder.AppendLine("; This file was automatically generated. Please modify only if you know what you're doing."); Builder.AppendLine(); for (int i = 0; i < CurrentControls.Length; i++) { CommandInfo Info; TryGetCommandInfo(CurrentControls[i].Command, out Info); Builder.Append(Info.Name + ", "); switch (CurrentControls[i].Method) { case ControlMethod.Keyboard: Builder.Append("keyboard, " + CurrentControls[i].Element.ToString(Culture) + ", " + ((int)CurrentControls[i].Modifier).ToString(Culture)); break; case ControlMethod.Joystick: Builder.Append("joystick, " + CurrentControls[i].Device.ToString(Culture) + ", "); switch (CurrentControls[i].Component) { case JoystickComponent.Axis: Builder.Append("axis, " + CurrentControls[i].Element.ToString(Culture) + ", " + CurrentControls[i].Direction.ToString(Culture)); break; case JoystickComponent.Ball: Builder.Append("ball, " + CurrentControls[i].Element.ToString(Culture) + ", " + CurrentControls[i].Direction.ToString(Culture)); break; case JoystickComponent.Hat: Builder.Append("hat, " + CurrentControls[i].Element.ToString(Culture) + ", " + CurrentControls[i].Direction.ToString(Culture)); break; case JoystickComponent.Button: Builder.Append("button, " + CurrentControls[i].Element.ToString(Culture)); break; default: Builder.Append("invalid"); break; } break; default: break; } Builder.Append("\n"); } string File; if (FileOrNull == null) { File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "controls.cfg"); } else { File = FileOrNull; } System.IO.File.WriteAllText(File, Builder.ToString(), new System.Text.UTF8Encoding(true)); } // load controls internal static void LoadControls(string FileOrNull, out Control[] Controls) { string File; if (FileOrNull == null) { File = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "controls.cfg"); if (!System.IO.File.Exists(File)) { File = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Controls"), "Default keyboard assignment.controls"); } } else { File = FileOrNull; } Controls = new Control[256]; int Length = 0; CultureInfo Culture = CultureInfo.InvariantCulture; if (System.IO.File.Exists(File)) { string[] Lines = System.IO.File.ReadAllLines(File, new System.Text.UTF8Encoding()); for (int i = 0; i < Lines.Length; i++) { Lines[i] = Lines[i].Trim(); if (Lines[i].Length != 0 && !Lines[i].StartsWith(";", StringComparison.OrdinalIgnoreCase)) { string[] Terms = Lines[i].Split(new char[] { ',' }); for (int j = 0; j < Terms.Length; j++) { Terms[j] = Terms[j].Trim(); } if (Terms.Length >= 2) { if (Length >= Controls.Length) { Array.Resize(ref Controls, Controls.Length << 1); } int j; for (j = 0; j < CommandInfos.Length; j++) { if (string.Compare(CommandInfos[j].Name, Terms[0], StringComparison.OrdinalIgnoreCase) == 0) break; } if (j == CommandInfos.Length) { Controls[Length].Command = Command.None; Controls[Length].InheritedType = CommandType.Digital; Controls[Length].Method = ControlMethod.Invalid; Controls[Length].Device = -1; Controls[Length].Component = JoystickComponent.Invalid; Controls[Length].Element = -1; Controls[Length].Direction = 0; Controls[Length].Modifier = KeyboardModifier.None; } else { Controls[Length].Command = CommandInfos[j].Command; Controls[Length].InheritedType = CommandInfos[j].Type; string Method = Terms[1].ToLowerInvariant(); bool Valid = false; if (Method == "keyboard" & Terms.Length == 4) { int Element, Modifiers; if (int.TryParse(Terms[2], NumberStyles.Integer, Culture, out Element)) { if (int.TryParse(Terms[3], NumberStyles.Integer, Culture, out Modifiers)) { Controls[Length].Method = ControlMethod.Keyboard; Controls[Length].Device = -1; Controls[Length].Component = JoystickComponent.Invalid; Controls[Length].Element = Element; Controls[Length].Direction = 0; Controls[Length].Modifier = (KeyboardModifier)Modifiers; Valid = true; } } } else if (Method == "joystick" & Terms.Length >= 4) { int Device; if (int.TryParse(Terms[2], NumberStyles.Integer, Culture, out Device)) { string Component = Terms[3].ToLowerInvariant(); if (Component == "axis" & Terms.Length == 6) { int Element, Direction; if (int.TryParse(Terms[4], NumberStyles.Integer, Culture, out Element)) { if (int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Direction)) { Controls[Length].Method = ControlMethod.Joystick; Controls[Length].Device = Device; Controls[Length].Component = JoystickComponent.Axis; Controls[Length].Element = Element; Controls[Length].Direction = Direction; Controls[Length].Modifier = KeyboardModifier.None; Valid = true; } } } else if (Component == "ball" & Terms.Length == 6) { int Element, Direction; if (int.TryParse(Terms[4], NumberStyles.Integer, Culture, out Element)) { if (int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Direction)) { Controls[Length].Method = ControlMethod.Joystick; Controls[Length].Device = Device; Controls[Length].Component = JoystickComponent.Ball; Controls[Length].Element = Element; Controls[Length].Direction = Direction; Controls[Length].Modifier = KeyboardModifier.None; Valid = true; } } } else if (Component == "hat" & Terms.Length == 6) { int Element, Direction; if (int.TryParse(Terms[4], NumberStyles.Integer, Culture, out Element)) { if (int.TryParse(Terms[5], NumberStyles.Integer, Culture, out Direction)) { Controls[Length].Method = ControlMethod.Joystick; Controls[Length].Device = Device; Controls[Length].Component = JoystickComponent.Hat; Controls[Length].Element = Element; Controls[Length].Direction = Direction; Controls[Length].Modifier = KeyboardModifier.None; Valid = true; } } } else if (Component == "button" & Terms.Length == 5) { int Element; if (int.TryParse(Terms[4], NumberStyles.Integer, Culture, out Element)) { Controls[Length].Method = ControlMethod.Joystick; Controls[Length].Device = Device; Controls[Length].Component = JoystickComponent.Button; Controls[Length].Element = Element; Controls[Length].Direction = 0; Controls[Length].Modifier = KeyboardModifier.None; Valid = true; } } } } if (!Valid) { Controls[Length].Method = ControlMethod.Invalid; Controls[Length].Device = -1; Controls[Length].Component = JoystickComponent.Invalid; Controls[Length].Element = -1; Controls[Length].Direction = 0; Controls[Length].Modifier = KeyboardModifier.None; } } Length++; } } } } Array.Resize(ref Controls, Length); } // add controls internal static void AddControls(ref Control[] Base, Control[] Add) { for (int i = 0; i < Add.Length; i++) { int j; for (j = 0; j < Base.Length; j++) { if (Add[i].Command == Base[j].Command) break; } if (j == Base.Length) { Array.Resize(ref Base, Base.Length + 1); Base[Base.Length - 1] = Add[i]; } } } // ================================ // hud elements internal struct HudVector { internal int X; internal int Y; } internal struct HudVectorF { internal float X; internal float Y; } internal struct HudImage { internal Textures.Texture BackgroundTexture; internal Textures.Texture OverlayTexture; } internal enum HudTransition { None = 0, Move = 1, Fade = 2, MoveAndFade = 3 } internal class HudElement { internal string Subject; internal HudVectorF Position; internal HudVector Alignment; internal HudImage TopLeft; internal HudImage TopMiddle; internal HudImage TopRight; internal HudImage CenterLeft; internal HudImage CenterMiddle; internal HudImage CenterRight; internal HudImage BottomLeft; internal HudImage BottomMiddle; internal HudImage BottomRight; internal Color32 BackgroundColor; internal Color32 OverlayColor; internal Color32 TextColor; internal HudVectorF TextPosition; internal HudVector TextAlignment; internal Fonts.OpenGlFont Font; internal bool TextShadow; internal string Text; internal float Value1; internal float Value2; internal HudTransition Transition; internal HudVectorF TransitionVector; internal double TransitionState; internal HudElement() { this.Subject = null; this.Position.X = 0.0f; this.Position.Y = 0.0f; this.Alignment.X = -1; this.Alignment.Y = -1; this.BackgroundColor = new Color32(255, 255, 255, 255); this.OverlayColor = new Color32(255, 255, 255, 255); this.TextColor = new Color32(255, 255, 255, 255); this.TextPosition.X = 0.0f; this.TextPosition.Y = 0.0f; this.TextAlignment.X = -1; this.TextAlignment.Y = 0; this.Font = Fonts.VerySmallFont; this.TextShadow = true; this.Text = null; this.Value1 = 0.0f; this.Value2 = 0.0f; this.Transition = HudTransition.None; this.TransitionState = 1.0; } } internal static HudElement[] CurrentHudElements = new HudElement[] { }; // load hud internal static void LoadHUD() { CultureInfo Culture = CultureInfo.InvariantCulture; string Folder = Program.FileSystem.GetDataFolder("In-game", CurrentOptions.UserInterfaceFolder); string File = OpenBveApi.Path.CombineFile(Folder, "interface.cfg"); CurrentHudElements = new HudElement[16]; int Length = 0; if (System.IO.File.Exists(File)) { string[] Lines = System.IO.File.ReadAllLines(File, new System.Text.UTF8Encoding()); 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].Length != 0) { if (!Lines[i].StartsWith(";", StringComparison.Ordinal)) { if (Lines[i].Equals("[element]", StringComparison.OrdinalIgnoreCase)) { Length++; if (Length > CurrentHudElements.Length) { Array.Resize(ref CurrentHudElements, CurrentHudElements.Length << 1); } CurrentHudElements[Length - 1] = new HudElement(); } else if (Length == 0) { System.Windows.Forms.MessageBox.Show("Line outside of [element] structure encountered at line " + (i + 1).ToString(Culture) + " in " + File); } else { j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string Command = Lines[i].Substring(0, j).TrimEnd(); string[] Arguments = Lines[i].Substring(j + 1).TrimStart().Split(new char[] { ',' }, StringSplitOptions.None); for (j = 0; j < Arguments.Length; j++) { Arguments[j] = Arguments[j].Trim(); } switch (Command.ToLowerInvariant()) { case "subject": if (Arguments.Length == 1) { CurrentHudElements[Length - 1].Subject = Arguments[0]; } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "position": if (Arguments.Length == 2) { float x, y; if (!float.TryParse(Arguments[0], NumberStyles.Float, Culture, out x)) { System.Windows.Forms.MessageBox.Show("X is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!float.TryParse(Arguments[1], NumberStyles.Float, Culture, out y)) { System.Windows.Forms.MessageBox.Show("Y is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { CurrentHudElements[Length - 1].Position.X = x; CurrentHudElements[Length - 1].Position.Y = y; } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "alignment": if (Arguments.Length == 2) { int x, y; if (!int.TryParse(Arguments[0], NumberStyles.Integer, Culture, out x)) { System.Windows.Forms.MessageBox.Show("X is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[1], NumberStyles.Integer, Culture, out y)) { System.Windows.Forms.MessageBox.Show("Y is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { CurrentHudElements[Length - 1].Alignment.X = Math.Sign(x); CurrentHudElements[Length - 1].Alignment.Y = Math.Sign(y); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "topleft": if (Arguments.Length == 2) { if (Arguments[0].Length != 0 & !Arguments[0].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[0]), out CurrentHudElements[Length - 1].TopLeft.BackgroundTexture); } if (Arguments[1].Length != 0 & !Arguments[1].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[1]), out CurrentHudElements[Length - 1].TopLeft.OverlayTexture); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "topmiddle": if (Arguments.Length == 2) { if (Arguments[0].Length != 0 & !Arguments[0].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[0]), out CurrentHudElements[Length - 1].TopMiddle.BackgroundTexture); } if (Arguments[1].Length != 0 & !Arguments[1].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[1]), out CurrentHudElements[Length - 1].TopMiddle.OverlayTexture); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "topright": if (Arguments.Length == 2) { if (Arguments[0].Length != 0 & !Arguments[0].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[0]), out CurrentHudElements[Length - 1].TopRight.BackgroundTexture); } if (Arguments[1].Length != 0 & !Arguments[1].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[1]), out CurrentHudElements[Length - 1].TopRight.OverlayTexture); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "centerleft": if (Arguments.Length == 2) { if (Arguments[0].Length != 0 & !Arguments[0].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[0]), out CurrentHudElements[Length - 1].CenterLeft.BackgroundTexture); } if (Arguments[1].Length != 0 & !Arguments[1].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[1]), out CurrentHudElements[Length - 1].CenterLeft.OverlayTexture); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "centermiddle": if (Arguments.Length == 2) { if (Arguments[0].Length != 0 & !Arguments[0].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[0]), out CurrentHudElements[Length - 1].CenterMiddle.BackgroundTexture); } if (Arguments[1].Length != 0 & !Arguments[1].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[1]), out CurrentHudElements[Length - 1].CenterMiddle.OverlayTexture); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "centerright": if (Arguments.Length == 2) { if (Arguments[0].Length != 0 & !Arguments[0].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[0]), out CurrentHudElements[Length - 1].CenterRight.BackgroundTexture); } if (Arguments[1].Length != 0 & !Arguments[1].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[1]), out CurrentHudElements[Length - 1].CenterRight.OverlayTexture); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "bottomleft": if (Arguments.Length == 2) { if (Arguments[0].Length != 0 & !Arguments[0].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[0]), out CurrentHudElements[Length - 1].BottomLeft.BackgroundTexture); } if (Arguments[1].Length != 0 & !Arguments[1].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[1]), out CurrentHudElements[Length - 1].BottomLeft.OverlayTexture); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "bottommiddle": if (Arguments.Length == 2) { if (Arguments[0].Length != 0 & !Arguments[0].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[0]), out CurrentHudElements[Length - 1].BottomMiddle.BackgroundTexture); } if (Arguments[1].Length != 0 & !Arguments[1].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[1]), out CurrentHudElements[Length - 1].BottomMiddle.OverlayTexture); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "bottomright": if (Arguments.Length == 2) { if (Arguments[0].Length != 0 & !Arguments[0].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[0]), out CurrentHudElements[Length - 1].BottomRight.BackgroundTexture); } if (Arguments[1].Length != 0 & !Arguments[1].Equals("null", StringComparison.OrdinalIgnoreCase)) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Folder, Arguments[1]), out CurrentHudElements[Length - 1].BottomRight.OverlayTexture); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "backcolor": if (Arguments.Length == 4) { int r, g, b, a; if (!int.TryParse(Arguments[0], NumberStyles.Integer, Culture, out r)) { System.Windows.Forms.MessageBox.Show("R is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[1], NumberStyles.Integer, Culture, out g)) { System.Windows.Forms.MessageBox.Show("G is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[2], NumberStyles.Integer, Culture, out b)) { System.Windows.Forms.MessageBox.Show("B is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[3], NumberStyles.Integer, Culture, out a)) { System.Windows.Forms.MessageBox.Show("A is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { r = r < 0 ? 0 : r > 255 ? 255 : r; g = g < 0 ? 0 : g > 255 ? 255 : g; b = b < 0 ? 0 : b > 255 ? 255 : b; a = a < 0 ? 0 : a > 255 ? 255 : a; CurrentHudElements[Length - 1].BackgroundColor = new Color32((byte)r, (byte)g, (byte)b, (byte)a); } break; } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "overlaycolor": if (Arguments.Length == 4) { int r, g, b, a; if (!int.TryParse(Arguments[0], NumberStyles.Integer, Culture, out r)) { System.Windows.Forms.MessageBox.Show("R is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[1], NumberStyles.Integer, Culture, out g)) { System.Windows.Forms.MessageBox.Show("G is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[2], NumberStyles.Integer, Culture, out b)) { System.Windows.Forms.MessageBox.Show("B is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[3], NumberStyles.Integer, Culture, out a)) { System.Windows.Forms.MessageBox.Show("A is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { r = r < 0 ? 0 : r > 255 ? 255 : r; g = g < 0 ? 0 : g > 255 ? 255 : g; b = b < 0 ? 0 : b > 255 ? 255 : b; a = a < 0 ? 0 : a > 255 ? 255 : a; CurrentHudElements[Length - 1].OverlayColor = new Color32((byte)r, (byte)g, (byte)b, (byte)a); } break; } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "textcolor": if (Arguments.Length == 4) { int r, g, b, a; if (!int.TryParse(Arguments[0], NumberStyles.Integer, Culture, out r)) { System.Windows.Forms.MessageBox.Show("R is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[1], NumberStyles.Integer, Culture, out g)) { System.Windows.Forms.MessageBox.Show("G is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[2], NumberStyles.Integer, Culture, out b)) { System.Windows.Forms.MessageBox.Show("B is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[3], NumberStyles.Integer, Culture, out a)) { System.Windows.Forms.MessageBox.Show("A is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { r = r < 0 ? 0 : r > 255 ? 255 : r; g = g < 0 ? 0 : g > 255 ? 255 : g; b = b < 0 ? 0 : b > 255 ? 255 : b; a = a < 0 ? 0 : a > 255 ? 255 : a; CurrentHudElements[Length - 1].TextColor = new Color32((byte)r, (byte)g, (byte)b, (byte)a); } break; } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "textposition": if (Arguments.Length == 2) { float x, y; if (!float.TryParse(Arguments[0], NumberStyles.Float, Culture, out x)) { System.Windows.Forms.MessageBox.Show("X is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!float.TryParse(Arguments[1], NumberStyles.Float, Culture, out y)) { System.Windows.Forms.MessageBox.Show("Y is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { CurrentHudElements[Length - 1].TextPosition.X = x; CurrentHudElements[Length - 1].TextPosition.Y = y; } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "textalignment": if (Arguments.Length == 2) { int x, y; if (!int.TryParse(Arguments[0], NumberStyles.Integer, Culture, out x)) { System.Windows.Forms.MessageBox.Show("X is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!int.TryParse(Arguments[1], NumberStyles.Integer, Culture, out y)) { System.Windows.Forms.MessageBox.Show("Y is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { CurrentHudElements[Length - 1].TextAlignment.X = Math.Sign(x); CurrentHudElements[Length - 1].TextAlignment.Y = Math.Sign(y); } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "textsize": if (Arguments.Length == 1) { int s; if (!int.TryParse(Arguments[0], NumberStyles.Integer, Culture, out s)) { System.Windows.Forms.MessageBox.Show("SIZE is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { switch (s) { case 0: CurrentHudElements[Length - 1].Font = Fonts.VerySmallFont; break; case 1: CurrentHudElements[Length - 1].Font = Fonts.SmallFont; break; case 2: CurrentHudElements[Length - 1].Font = Fonts.NormalFont; break; case 3: CurrentHudElements[Length - 1].Font = Fonts.LargeFont; break; case 4: CurrentHudElements[Length - 1].Font = Fonts.VeryLargeFont; break; default: CurrentHudElements[Length - 1].Font = Fonts.NormalFont; break; } } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "textshadow": if (Arguments.Length == 1) { int s; if (!int.TryParse(Arguments[0], NumberStyles.Integer, Culture, out s)) { System.Windows.Forms.MessageBox.Show("SHADOW is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { CurrentHudElements[Length - 1].TextShadow = s != 0; } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "text": if (Arguments.Length == 1) { CurrentHudElements[Length - 1].Text = Arguments[0]; } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "value": if (Arguments.Length == 1) { int n; if (!int.TryParse(Arguments[0], NumberStyles.Integer, Culture, out n)) { System.Windows.Forms.MessageBox.Show("VALUE1 is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { CurrentHudElements[Length - 1].Value1 = n; } } else if (Arguments.Length == 2) { float a, b; if (!float.TryParse(Arguments[0], NumberStyles.Float, Culture, out a)) { System.Windows.Forms.MessageBox.Show("VALUE1 is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!float.TryParse(Arguments[1], NumberStyles.Float, Culture, out b)) { System.Windows.Forms.MessageBox.Show("VALUE2 is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { CurrentHudElements[Length - 1].Value1 = a; CurrentHudElements[Length - 1].Value2 = b; } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "transition": if (Arguments.Length == 1) { int n; if (!int.TryParse(Arguments[0], NumberStyles.Integer, Culture, out n)) { System.Windows.Forms.MessageBox.Show("TRANSITION is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { CurrentHudElements[Length - 1].Transition = (HudTransition)n; } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; case "transitionvector": if (Arguments.Length == 2) { float x, y; if (!float.TryParse(Arguments[0], NumberStyles.Float, Culture, out x)) { System.Windows.Forms.MessageBox.Show("X is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else if (!float.TryParse(Arguments[1], NumberStyles.Float, Culture, out y)) { System.Windows.Forms.MessageBox.Show("Y is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } else { CurrentHudElements[Length - 1].TransitionVector.X = x; CurrentHudElements[Length - 1].TransitionVector.Y = y; } } else { System.Windows.Forms.MessageBox.Show("Incorrect number of arguments supplied in " + Command + " at line " + (i + 1).ToString(Culture) + " in " + File); } break; default: System.Windows.Forms.MessageBox.Show("Invalid command encountered at line " + (i + 1).ToString(Culture) + " in " + File); break; } } else { System.Windows.Forms.MessageBox.Show("Invalid statement encountered at line " + (i + 1).ToString(Culture) + " in " + File); } } } } } } Array.Resize(ref CurrentHudElements, Length); } // ================================ // encodings internal enum Encoding { Unknown = 0, Utf8 = 1, Utf16Le = 2, Utf16Be = 3, Utf32Le = 4, Utf32Be = 5, } internal static Encoding GetEncodingFromFile(string File) { try { byte[] Data = System.IO.File.ReadAllBytes(File); if (Data.Length >= 3) { if (Data[0] == 0xEF & Data[1] == 0xBB & Data[2] == 0xBF) return Encoding.Utf8; } if (Data.Length >= 2) { if (Data[0] == 0xFE & Data[1] == 0xFF) return Encoding.Utf16Be; if (Data[0] == 0xFF & Data[1] == 0xFE) return Encoding.Utf16Le; } if (Data.Length >= 4) { if (Data[0] == 0x00 & Data[1] == 0x00 & Data[2] == 0xFE & Data[3] == 0xFF) return Encoding.Utf32Be; if (Data[0] == 0xFF & Data[1] == 0xFE & Data[2] == 0x00 & Data[3] == 0x00) return Encoding.Utf32Le; } return Encoding.Unknown; } catch { return Encoding.Unknown; } } internal static Encoding GetEncodingFromFile(string Folder, string File) { return GetEncodingFromFile(OpenBveApi.Path.CombineFile(Folder, File)); } // ================================ // try parse vb6 internal static bool TryParseDoubleVb6(string Expression, out double Value) { Expression = TrimInside(Expression); CultureInfo Culture = CultureInfo.InvariantCulture; for (int n = Expression.Length; n > 0; n--) { double a; if (double.TryParse(Expression.Substring(0, n), NumberStyles.Float, Culture, out a)) { Value = a; return true; } } Value = 0.0; return false; } internal static bool TryParseFloatVb6(string Expression, out float Value) { Expression = TrimInside(Expression); CultureInfo Culture = CultureInfo.InvariantCulture; for (int n = Expression.Length; n > 0; n--) { float a; if (float.TryParse(Expression.Substring(0, n), NumberStyles.Float, Culture, out a)) { Value = a; return true; } } Value = 0.0f; return false; } internal static bool TryParseIntVb6(string Expression, out int Value) { Expression = TrimInside(Expression); CultureInfo Culture = CultureInfo.InvariantCulture; for (int n = Expression.Length; n > 0; n--) { double a; if (double.TryParse(Expression.Substring(0, n), NumberStyles.Float, Culture, out a)) { if (a >= -2147483648.0 & a <= 2147483647.0) { Value = (int)Math.Round(a); return true; } else break; } } Value = 0; return false; } // try parse time internal static bool TryParseTime(string Expression, out double Value) { Expression = TrimInside(Expression); if (Expression.Length != 0) { CultureInfo Culture = CultureInfo.InvariantCulture; int i = Expression.IndexOf('.'); if (i >= 1) { int h; if (int.TryParse(Expression.Substring(0, i), NumberStyles.Integer, Culture, out h)) { int n = Expression.Length - i - 1; if (n == 1 | n == 2) { uint m; if (uint.TryParse(Expression.Substring(i + 1, n), NumberStyles.None, Culture, out m)) { Value = 3600.0 * (double)h + 60.0 * (double)m; return true; } } else if (n == 3 | n == 4) { uint m; if (uint.TryParse(Expression.Substring(i + 1, 2), NumberStyles.None, Culture, out m)) { uint s; if (uint.TryParse(Expression.Substring(i + 3, n - 2), NumberStyles.None, Culture, out s)) { Value = 3600.0 * (double)h + 60.0 * (double)m + (double)s; return true; } } } } } else if (i == -1) { int h; if (int.TryParse(Expression, NumberStyles.Integer, Culture, out h)) { Value = 3600.0 * (double)h; return true; } } } Value = 0.0; return false; } // try parse hex color internal static bool TryParseHexColor(string Expression, out Color24 Color) { if (Expression.StartsWith("#")) { string a = Expression.Substring(1).TrimStart(); int x; if (int.TryParse(a, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out x)) { int r = (x >> 16) & 0xFF; int g = (x >> 8) & 0xFF; int b = x & 0xFF; if (r >= 0 & r <= 255 & g >= 0 & g <= 255 & b >= 0 & b <= 255) { Color = new Color24((byte)r, (byte)g, (byte)b); return true; } else { Color = new Color24(0, 0, 255); return false; } } else { Color = new Color24(0, 0, 255); return false; } } else { Color = new Color24(0, 0, 255); return false; } } internal static bool TryParseHexColor(string Expression, out Color32 Color) { if (Expression.StartsWith("#")) { string a = Expression.Substring(1).TrimStart(); int x; if (int.TryParse(a, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out x)) { int r = (x >> 16) & 0xFF; int g = (x >> 8) & 0xFF; int b = x & 0xFF; if (r >= 0 & r <= 255 & g >= 0 & g <= 255 & b >= 0 & b <= 255) { Color = new Color32((byte)r, (byte)g, (byte)b, 255); return true; } else { Color = new Color32(0, 0, 255, 255); return false; } } else { Color = new Color32(0, 0, 255, 255); return false; } } else { Color = new Color32(0, 0, 255, 255); return false; } } // try parse with unit factors internal static bool TryParseDouble(string Expression, double[] UnitFactors, out double Value) { double a; if (double.TryParse(Expression, NumberStyles.Any, CultureInfo.InvariantCulture, out a)) { Value = a * UnitFactors[UnitFactors.Length - 1]; return true; } else { string[] parameters = Expression.Split(':'); if (parameters.Length <= UnitFactors.Length) { Value = 0.0; for (int i = 0; i < parameters.Length; i++) { if (double.TryParse(parameters[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out a)) { int j = i + UnitFactors.Length - parameters.Length; Value += a * UnitFactors[j]; } else { return false; } } return true; } else { Value = 0.0; return false; } } } internal static bool TryParseDoubleVb6(string Expression, double[] UnitFactors, out double Value) { double a; if (double.TryParse(Expression, NumberStyles.Any, CultureInfo.InvariantCulture, out a)) { Value = a * UnitFactors[UnitFactors.Length - 1]; return true; } else { string[] parameters = Expression.Split(':'); Value = 0.0; if (parameters.Length <= UnitFactors.Length) { for (int i = 0; i < parameters.Length; i++) { if (TryParseDoubleVb6(parameters[i].Trim(), out a)) { int j = i + UnitFactors.Length - parameters.Length; Value += a * UnitFactors[j]; } else { return false; } } return true; } else { return false; } } } // trim inside private static string TrimInside(string Expression) { System.Text.StringBuilder Builder = new System.Text.StringBuilder(Expression.Length); for (int i = 0; i < Expression.Length; i++) { char c = Expression[i]; if (!char.IsWhiteSpace(c)) { Builder.Append(c); } } return Builder.ToString(); } // is japanese internal static bool IsJapanese(string Name) { for (int i = 0; i < Name.Length; i++) { int a = char.ConvertToUtf32(Name, i); if (a < 0x10000) { bool q = false; while (true) { if (a >= 0x2E80 & a <= 0x2EFF) break; if (a >= 0x3000 & a <= 0x30FF) break; if (a >= 0x31C0 & a <= 0x4DBF) break; if (a >= 0x4E00 & a <= 0x9FFF) break; if (a >= 0xF900 & a <= 0xFAFF) break; if (a >= 0xFE30 & a <= 0xFE4F) break; if (a >= 0xFF00 & a <= 0xFFEF) break; q = true; break; } if (q) return false; } else { return false; } } return true; } // unescape internal static string Unescape(string Text) { System.Text.StringBuilder Builder = new System.Text.StringBuilder(Text.Length); int Start = 0; for (int i = 0; i < Text.Length; i++) { if (Text[i] == '\\') { Builder.Append(Text, Start, i - Start); if (i + 1 < Text.Length) { switch (Text[i + 1]) { case 'a': Builder.Append('\a'); break; case 'b': Builder.Append('\b'); break; case 't': Builder.Append('\t'); break; case 'n': Builder.Append('\n'); break; case 'v': Builder.Append('\v'); break; case 'f': Builder.Append('\f'); break; case 'r': Builder.Append('\r'); break; case 'e': Builder.Append('\x1B'); break; case 'c': if (i + 2 < Text.Length) { int CodePoint = char.ConvertToUtf32(Text, i + 2); if (CodePoint >= 0x40 & CodePoint <= 0x5F) { Builder.Append(char.ConvertFromUtf32(CodePoint - 64)); } else if (CodePoint == 0x3F) { Builder.Append('\x7F'); } else { //Interface.AddMessage(MessageType.Error, false, "Unrecognized control character found in " + Text.Substring(i, 3)); return Text; } i++; } else { //Interface.AddMessage(MessageType.Error, false, "Insufficient characters available in " + Text + " to decode control character escape sequence"); return Text; } break; case '"': Builder.Append('"'); break; case '\\': Builder.Append('\\'); break; case 'x': if (i + 3 < Text.Length) { Builder.Append(char.ConvertFromUtf32(Convert.ToInt32(Text.Substring(i + 2, 2), 16))); i += 2; } else { //Interface.AddMessage(MessageType.Error, false, "Insufficient characters available in " + Text + " to decode hexadecimal escape sequence."); return Text; } break; case 'u': if (i + 5 < Text.Length) { Builder.Append(char.ConvertFromUtf32(Convert.ToInt32(Text.Substring(i + 2, 4), 16))); i += 4; } else { //Interface.AddMessage(MessageType.Error, false, "Insufficient characters available in " + Text + " to decode hexadecimal escape sequence."); return Text; } break; default: //Interface.AddMessage(MessageType.Error, false, "Unrecognized escape sequence found in " + Text + "."); return Text; } i++; Start = i + 1; } else { //Interface.AddMessage(MessageType.Error, false, "Insufficient characters available in " + Text + " to decode escape sequence."); return Text; } } } Builder.Append(Text, Start, Text.Length - Start); return Builder.ToString(); } // ================================ // convert newlines to crlf internal static string ConvertNewlinesToCrLf(string Text) { System.Text.StringBuilder Builder = new System.Text.StringBuilder(); for (int i = 0; i < Text.Length; i++) { int a = char.ConvertToUtf32(Text, i); if (a == 0xD & i < Text.Length - 1) { int b = char.ConvertToUtf32(Text, i + 1); if (b == 0xA) { Builder.Append("\r\n"); i++; } else { Builder.Append("\r\n"); } } else if (a == 0xA | a == 0xC | a == 0xD | a == 0x85 | a == 0x2028 | a == 0x2029) { Builder.Append("\r\n"); } else if (a < 0x10000) { Builder.Append(Text[i]); } else { Builder.Append(Text.Substring(i, 2)); i++; } } return Builder.ToString(); } // ================================ // // get corrected path separation // internal static string GetCorrectedPathSeparation(string Expression) { // if (Program.CurrentPlatform == Program.Platform.Windows) { // if (Expression.Length != 0 && Expression[0] == '\\') { // return Expression.Substring(1); // } else { // return Expression; // } // } else { // if (Expression.Length != 0 && Expression[0] == '\\') { // return Expression.Substring(1).Replace("\\", new string(new char[] { System.IO.Path.DirectorySeparatorChar })); // } else { // return Expression.Replace("\\", new string(new char[] { System.IO.Path.DirectorySeparatorChar })); // } // } // } // // get corected folder name // internal static string GetCorrectedFolderName(string Folder) { // if (Folder.Length == 0) { // return ""; // } else if (Program.CurrentPlatform == Program.Platform.Linux) { // // find folder case-insensitively // if (System.IO.Directory.Exists(Folder)) { // return Folder; // } else { // string Parent = GetCorrectedFolderName(System.IO.Path.GetDirectoryName(Folder)); // Folder = System.IO.Path.Combine(Parent, System.IO.Path.GetFileName(Folder)); // if (Folder != null && System.IO.Directory.Exists(Parent)) { // if (System.IO.Directory.Exists(Folder)) { // return Folder; // } else { // string[] Folders = System.IO.Directory.GetDirectories(Parent); // for (int i = 0; i < Folders.Length; i++) { // if (string.Compare(Folder, Folders[i], StringComparison.OrdinalIgnoreCase) == 0) { // return Folders[i]; // } // } // } // } // return Folder; // } // } else { // return Folder; // } // } // // get corrected file name // internal static string GetCorrectedFileName(string File) { // if (File.Length == 0) { // return ""; // } else if (Program.CurrentPlatform == Program.Platform.Linux) { // // find file case-insensitively // if (System.IO.File.Exists(File)) { // return File; // } else { // string Folder = GetCorrectedFolderName(System.IO.Path.GetDirectoryName(File)); // File = System.IO.Path.Combine(Folder, System.IO.Path.GetFileName(File)); // if (System.IO.Directory.Exists(Folder)) { // if (System.IO.File.Exists(File)) { // return File; // } else { // string[] Files = System.IO.Directory.GetFiles(Folder); // for (int i = 0; i < Files.Length; i++) { // if (string.Compare(File, Files[i], StringComparison.OrdinalIgnoreCase) == 0) { // return Files[i]; // } // } // } // } // return File; // } // } else { // return File; // } // } // // get combined file name // internal static string OpenBveApi.Path.CombineFile(string SafeFolderPart, string UnsafeFilePart) { // return GetCorrectedFileName(System.IO.Path.Combine(SafeFolderPart, GetCorrectedPathSeparation(UnsafeFilePart))); // } // // get combined folder name // internal static string OpenBveApi.Path.CombineDirectory(string SafeFolderPart, string UnsafeFolderPart) { // return GetCorrectedFolderName(System.IO.Path.Combine(SafeFolderPart, GetCorrectedPathSeparation(UnsafeFolderPart))); // } // contains invalid path characters internal static bool ContainsInvalidPathChars(string Expression) { char[] a = System.IO.Path.GetInvalidFileNameChars(); char[] b = System.IO.Path.GetInvalidPathChars(); for (int i = 0; i < Expression.Length; i++) { for (int j = 0; j < a.Length; j++) { if (Expression[i] == a[j]) { for (int k = 0; k < b.Length; k++) { if (Expression[i] == b[k]) { return true; } } } } } return false; } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/LegacyPlugin.cs000066400000000000000000000344211171674032100224560ustar00rootroot00000000000000using System; using System.Runtime.InteropServices; using OpenBveApi.Runtime; namespace OpenBve { /// Represents a legacy Win32 plugin. internal class Win32Plugin : PluginManager.Plugin { // --- win32 proxy calls --- [DllImport("AtsPluginProxy.dll", EntryPoint = "LoadDLL", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static int Win32LoadDLL([MarshalAs(UnmanagedType.LPWStr)]string UnicodeFileName, [MarshalAs(UnmanagedType.LPStr)]string AnsiFileName); [DllImport("AtsPluginProxy.dll", EntryPoint = "UnloadDLL", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static int Win32UnloadDLL(); [DllImport("AtsPluginProxy.dll", EntryPoint = "Load", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32Load(); [DllImport("AtsPluginProxy.dll", EntryPoint = "Dispose", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32Dispose(); [DllImport("AtsPluginProxy.dll", EntryPoint = "GetPluginVersion", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static int Win32GetPluginVersion(); [DllImport("AtsPluginProxy.dll", EntryPoint = "SetVehicleSpec", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32SetVehicleSpec(ref int spec); [DllImport("AtsPluginProxy.dll", EntryPoint = "Initialize", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32Initialize(int brake); [DllImport("AtsPluginProxy.dll", EntryPoint = "Elapse", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32Elapse(ref int handles, ref double state, ref int panel, ref int sound); [DllImport("AtsPluginProxy.dll", EntryPoint = "SetPower", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32SetPower(int notch); [DllImport("AtsPluginProxy.dll", EntryPoint = "SetBrake", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32SetBrake(int notch); [DllImport("AtsPluginProxy.dll", EntryPoint = "SetReverser", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32SetReverser(int pos); [DllImport("AtsPluginProxy.dll", EntryPoint = "KeyDown", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32KeyDown(int atsKeyCode); [DllImport("AtsPluginProxy.dll", EntryPoint = "KeyUp", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32KeyUp(int atsKeyCode); [DllImport("AtsPluginProxy.dll", EntryPoint = "HornBlow", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32HornBlow(int hornType); [DllImport("AtsPluginProxy.dll", EntryPoint = "DoorOpen", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32DoorOpen(); [DllImport("AtsPluginProxy.dll", EntryPoint = "DoorClose", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32DoorClose(); [DllImport("AtsPluginProxy.dll", EntryPoint = "SetSignal", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32SetSignal(int signal); [DllImport("AtsPluginProxy.dll", EntryPoint = "SetBeaconData", ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private extern static void Win32SetBeaconData(ref int beacon); [StructLayout(LayoutKind.Sequential, Size = 20)] private struct Win32VehicleSpec { internal int BrakeNotches; internal int PowerNotches; internal int AtsNotch; internal int B67Notch; internal int Cars; } [StructLayout(LayoutKind.Sequential, Size = 40)] private struct Win32VehicleState { internal double Location; internal float Speed; internal int Time; internal float BcPressure; internal float MrPressure; internal float ErPressure; internal float BpPressure; internal float SapPressure; internal float Current; } [StructLayout(LayoutKind.Sequential, Size = 16)] private struct Win32Handles { internal int Brake; internal int Power; internal int Reverser; internal int ConstantSpeed; } [StructLayout(LayoutKind.Sequential, Size = 16)] private struct Win32BeaconData { internal int Type; internal int Signal; internal float Distance; internal int Optional; } private static class SoundInstructions { internal const int Stop = -10000; internal const int PlayLooping = 0; internal const int PlayOnce = 1; internal const int Continue = 2; } private static class ConstSpeedInstructions { internal const int Continue = 0; internal const int Enable = 1; internal const int Disable = 2; } // --- members --- private string PluginFile; private int[] Sound; private int[] LastSound; private GCHandle PanelHandle; private GCHandle SoundHandle; // --- constructors --- internal Win32Plugin(string pluginFile, TrainManager.Train train) { base.PluginTitle = System.IO.Path.GetFileName(pluginFile); base.PluginValid = true; base.PluginMessage = null; base.Train = train; base.Panel = new int[256]; base.SupportsAI = false; base.LastTime = 0.0; base.LastReverser = -2; base.LastPowerNotch = -1; base.LastBrakeNotch = -1; base.LastAspects = new int[] { }; base.LastSection = -1; base.LastException = null; this.PluginFile = pluginFile; this.Sound = new int[256]; this.LastSound = new int[256]; this.PanelHandle = new GCHandle(); this.SoundHandle = new GCHandle(); } // --- functions --- internal override bool Load(VehicleSpecs specs, InitializationModes mode) { int result; try { result = Win32LoadDLL(this.PluginFile, this.PluginFile); } catch (Exception ex) { base.LastException = ex; throw; } if (result == 0) { return false; } try { Win32Load(); } catch (Exception ex) { base.LastException = ex; throw; } int version; try { version = Win32GetPluginVersion(); } catch (Exception ex) { base.LastException = ex; throw; } if (version != 131072) { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + base.PluginTitle + " is of an unsupported version."); try { Win32Dispose(); } catch (Exception ex) { base.LastException = ex; throw; } Win32UnloadDLL(); return false; } try { Win32VehicleSpec win32Spec; win32Spec.BrakeNotches = specs.BrakeNotches; win32Spec.PowerNotches = specs.PowerNotches; win32Spec.AtsNotch = specs.AtsNotch; win32Spec.B67Notch = specs.B67Notch; win32Spec.Cars = specs.Cars; Win32SetVehicleSpec(ref win32Spec.BrakeNotches); } catch (Exception ex) { base.LastException = ex; throw; } try { Win32Initialize((int)mode); } catch (Exception ex) { base.LastException = ex; throw; } UpdatePower(); UpdateBrake(); UpdateReverser(); if (PanelHandle.IsAllocated) { PanelHandle.Free(); } if (SoundHandle.IsAllocated) { SoundHandle.Free(); } PanelHandle = GCHandle.Alloc(Panel, GCHandleType.Pinned); SoundHandle = GCHandle.Alloc(Sound, GCHandleType.Pinned); return true; } internal override void Unload() { if (PanelHandle.IsAllocated) { PanelHandle.Free(); } if (SoundHandle.IsAllocated) { SoundHandle.Free(); } try { Win32UnloadDLL(); } catch (Exception ex) { base.LastException = ex; throw; } } internal override void BeginJump(InitializationModes mode) { try { Win32Initialize((int)mode); } catch (Exception ex) { base.LastException = ex; throw; } } internal override void EndJump() { } internal override void Elapse(ElapseData data) { try { double time = data.TotalTime.Milliseconds; Win32VehicleState win32State; win32State.Location = data.Vehicle.Location; win32State.Speed = (float)data.Vehicle.Speed.KilometersPerHour; win32State.Time = (int)Math.Floor(time - 2073600000.0 * Math.Floor(time / 2073600000.0)); win32State.BcPressure = (float)data.Vehicle.BcPressure; win32State.MrPressure = (float)data.Vehicle.MrPressure; win32State.ErPressure = (float)data.Vehicle.ErPressure; win32State.BpPressure = (float)data.Vehicle.BpPressure; win32State.SapPressure = (float)data.Vehicle.SapPressure; win32State.Current = 0.0f; Win32Handles win32Handles; win32Handles.Brake = data.Handles.BrakeNotch; win32Handles.Power = data.Handles.PowerNotch; win32Handles.Reverser = data.Handles.Reverser; win32Handles.ConstantSpeed = data.Handles.ConstSpeed ? ConstSpeedInstructions.Enable : ConstSpeedInstructions.Disable; Win32Elapse(ref win32Handles.Brake, ref win32State.Location, ref base.Panel[0], ref this.Sound[0]); data.Handles.Reverser = win32Handles.Reverser; data.Handles.PowerNotch = win32Handles.Power; data.Handles.BrakeNotch = win32Handles.Brake; if (win32Handles.ConstantSpeed == ConstSpeedInstructions.Enable) { data.Handles.ConstSpeed = true; } else if (win32Handles.ConstantSpeed == ConstSpeedInstructions.Disable) { data.Handles.ConstSpeed = false; } else if (win32Handles.ConstantSpeed != ConstSpeedInstructions.Continue) { this.PluginValid = false; } /* * Process the sound instructions * */ for (int i = 0; i < this.Sound.Length; i++) { if (this.Sound[i] != this.LastSound[i]) { if (this.Sound[i] == SoundInstructions.Stop) { if (i < base.Train.Cars[base.Train.DriverCar].Sounds.Plugin.Length) { Sounds.StopSound(base.Train.Cars[base.Train.DriverCar].Sounds.Plugin[i].Source); } } else if (this.Sound[i] > SoundInstructions.Stop & this.Sound[i] <= SoundInstructions.PlayLooping) { if (i < base.Train.Cars[base.Train.DriverCar].Sounds.Plugin.Length) { Sounds.SoundBuffer buffer = base.Train.Cars[base.Train.DriverCar].Sounds.Plugin[i].Buffer; if (buffer != null) { double volume = (double)(this.Sound[i] - SoundInstructions.Stop) / (double)(SoundInstructions.PlayLooping - SoundInstructions.Stop); if (Sounds.IsPlaying(base.Train.Cars[base.Train.DriverCar].Sounds.Plugin[i].Source)) { base.Train.Cars[base.Train.DriverCar].Sounds.Plugin[i].Source.Volume = volume; } else { base.Train.Cars[base.Train.DriverCar].Sounds.Plugin[i].Source = Sounds.PlaySound(buffer, 1.0, volume, base.Train.Cars[base.Train.DriverCar].Sounds.Plugin[i].Position, base.Train, base.Train.DriverCar, true); } } } } else if (this.Sound[i] == SoundInstructions.PlayOnce) { if (i < base.Train.Cars[base.Train.DriverCar].Sounds.Plugin.Length) { Sounds.SoundBuffer buffer = base.Train.Cars[base.Train.DriverCar].Sounds.Plugin[i].Buffer; if (buffer != null) { base.Train.Cars[base.Train.DriverCar].Sounds.Plugin[i].Source = Sounds.PlaySound(buffer, 1.0, 1.0, base.Train.Cars[base.Train.DriverCar].Sounds.Plugin[i].Position, base.Train, base.Train.DriverCar, false); } } this.Sound[i] = SoundInstructions.Continue; } else if (this.Sound[i] != SoundInstructions.Continue) { this.PluginValid = false; } this.LastSound[i] = this.Sound[i]; } else { if ((this.Sound[i] < SoundInstructions.Stop | this.Sound[i] > SoundInstructions.PlayLooping) && this.Sound[i] != SoundInstructions.PlayOnce & this.Sound[i] != SoundInstructions.Continue) { this.PluginValid = false; } } } } catch (Exception ex) { base.LastException = ex; throw; } } internal override void SetReverser(int reverser) { try { Win32SetReverser(reverser); } catch (Exception ex) { base.LastException = ex; throw; } } internal override void SetPower(int powerNotch) { try { Win32SetPower(powerNotch); } catch (Exception ex) { base.LastException = ex; throw; } } internal override void SetBrake(int brakeNotch) { try { Win32SetBrake(brakeNotch); } catch (Exception ex) { base.LastException = ex; throw; } } internal override void KeyDown(VirtualKeys key) { try { Win32KeyDown((int)key); } catch (Exception ex) { base.LastException = ex; throw; } } internal override void KeyUp(VirtualKeys key) { try { Win32KeyUp((int)key); } catch (Exception ex) { base.LastException = ex; throw; } } internal override void HornBlow(HornTypes type) { try { Win32HornBlow((int)type); } catch (Exception ex) { base.LastException = ex; throw; } } internal override void DoorChange(DoorStates oldState, DoorStates newState) { if (oldState == DoorStates.None & newState != DoorStates.None) { try { Win32DoorOpen(); } catch (Exception ex) { base.LastException = ex; throw; } } else if (oldState != DoorStates.None & newState == DoorStates.None) { try { Win32DoorClose(); } catch (Exception ex) { base.LastException = ex; throw; } } } internal override void SetSignal(SignalData[] signal) { if (base.LastAspects.Length == 0 || signal[0].Aspect != base.LastAspects[0]) { try { Win32SetSignal(signal[0].Aspect); } catch (Exception ex) { base.LastException = ex; throw; } } } internal override void SetBeacon(BeaconData beacon) { try { Win32BeaconData win32Beacon; win32Beacon.Type = beacon.Type; win32Beacon.Signal = beacon.Signal.Aspect; win32Beacon.Distance = (float)beacon.Signal.Distance; win32Beacon.Optional = beacon.Optional; Win32SetBeaconData(ref win32Beacon.Type); } catch (Exception ex) { base.LastException = ex; throw; } } internal override void PerformAI(AIData data) { } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/Loading.cs000066400000000000000000000411711171674032100214500ustar00rootroot00000000000000using System; using System.Text; using System.Threading; using System.Windows.Forms; using OpenBveApi.Math; namespace OpenBve { internal static class Loading { // members internal static double RouteProgress; internal static double TrainProgress; internal static bool Cancel; internal static bool Complete; // private static Thread Loader = null; private static string CurrentRouteFile; private static Encoding CurrentRouteEncoding; private static string CurrentTrainFolder; private static Encoding CurrentTrainEncoding; internal static double TrainProgressCurrentSum; internal static double TrainProgressCurrentWeight; // load /// Initializes loading the route and train asynchronously. Set the Loading.Cancel member to cancel loading. Check the Loading.Complete member to see when loading has finished. internal static void LoadSynchronously(string RouteFile, Encoding RouteEncoding, string TrainFolder, Encoding TrainEncoding) { // members RouteProgress = 0.0; TrainProgress = 0.0; TrainProgressCurrentSum = 0.0; TrainProgressCurrentWeight = 1.0; Cancel = false; Complete = false; CurrentRouteFile = RouteFile; CurrentRouteEncoding = RouteEncoding; CurrentTrainFolder = TrainFolder; CurrentTrainEncoding = TrainEncoding; // Loader = new Thread(new ThreadStart(LoadThreaded)); // Loader.IsBackground = true; // Loader.Start(); LoadThreaded(); return; } // get railway folder private static string GetRailwayFolder(string RouteFile) { try { string Folder = System.IO.Path.GetDirectoryName(RouteFile); while (true) { string Subfolder = OpenBveApi.Path.CombineDirectory(Folder, "Railway"); if (System.IO.Directory.Exists(Subfolder)) { return Subfolder; } System.IO.DirectoryInfo Info = System.IO.Directory.GetParent(Folder); if (Info == null) break; Folder = Info.FullName; } } catch { } return Application.StartupPath; // return null; } // load threaded private static void LoadThreaded() { #if DEBUG LoadEverythingThreaded(); #else try { LoadEverythingThreaded(); } catch (Exception ex) { for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i] != null && TrainManager.Trains[i].Plugin != null) { if (TrainManager.Trains[i].Plugin.LastException != null) { Interface.AddMessage(Interface.MessageType.Critical, false, "The train plugin " + TrainManager.Trains[i].Plugin.PluginTitle + " caused a critical error in the route and train loader: " + TrainManager.Trains[i].Plugin.LastException.Message); return; } } } Interface.AddMessage(Interface.MessageType.Critical, false, "The route and train loader encountered the following critical error: " + ex.Message); } #endif Complete = true; } private static void LoadEverythingThreaded() { string RailwayFolder = GetRailwayFolder(CurrentRouteFile); // if (RailwayFolder == null) { // Interface.AddMessage(Interface.MessageType.Critical, false, "The Railway folder could not be found. Please check your folder structure."); // return; // } string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); // reset Game.Reset(true); Game.MinimalisticSimulation = true; // screen World.CameraTrackFollower = new TrackManager.TrackFollower(); World.CameraTrackFollower.Train = null; World.CameraTrackFollower.CarIndex = -1; World.CameraMode = World.CameraViewMode.Interior; // load route bool IsRW = string.Equals(System.IO.Path.GetExtension(CurrentRouteFile), ".rw", StringComparison.OrdinalIgnoreCase); CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false); System.Threading.Thread.Sleep(1); if (Cancel) return; Game.CalculateSeaLevelConstants(); if (Game.BogusPretrainInstructions.Length != 0) { double t = Game.BogusPretrainInstructions[0].Time; double p = Game.BogusPretrainInstructions[0].TrackPosition; for (int i = 1; i < Game.BogusPretrainInstructions.Length; i++) { if (Game.BogusPretrainInstructions[i].Time > t) { t = Game.BogusPretrainInstructions[i].Time; } else { t += 1.0; Game.BogusPretrainInstructions[i].Time = t; } if (Game.BogusPretrainInstructions[i].TrackPosition > p) { p = Game.BogusPretrainInstructions[i].TrackPosition; } else { p += 1.0; Game.BogusPretrainInstructions[i].TrackPosition = p; } } } RouteProgress = 1.0; // initialize trains System.Threading.Thread.Sleep(1); if (Cancel) return; TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Game.BogusPretrainInstructions.Length != 0 ? 1 : 0)]; for (int k = 0; k < TrainManager.Trains.Length; k++) { TrainManager.Trains[k] = new TrainManager.Train(); TrainManager.Trains[k].TrainIndex = k; if (k == TrainManager.Trains.Length - 1 & Game.BogusPretrainInstructions.Length != 0) { TrainManager.Trains[k].State = TrainManager.TrainState.Bogus; } else { TrainManager.Trains[k].State = TrainManager.TrainState.Pending; } } TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length]; // load trains double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length; for (int k = 0; k < TrainManager.Trains.Length; k++) { if (TrainManager.Trains[k].State == TrainManager.TrainState.Bogus) { // bogus train string Folder = Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"); TrainDatParser.ParseTrainData(Folder, System.Text.Encoding.UTF8, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) return; SoundCfgParser.LoadNoSound(TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) return; TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum; TrainProgressCurrentSum += TrainProgressCurrentWeight; } else { // real train TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum; TrainDatParser.ParseTrainData(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) return; TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum; SoundCfgParser.ParseSoundConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) return; // door open/close speed for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8; } } if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = 1.0; TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0; } } for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].RearAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].BeaconReceiver.Train = TrainManager.Trains[k]; } // add panel section if (k == TrainManager.PlayerTrain.TrainIndex) { TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections = new TrainManager.CarSection[1]; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Elements = new ObjectManager.AnimatedObject[] { }; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Overlay = true; TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum; TrainManager.ParsePanelConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) return; } // add exterior section if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { ObjectManager.UnifiedObject[] CarObjects; ExtensionsCfgParser.ParseExtensionsConfig(CurrentTrainFolder, CurrentTrainEncoding, out CarObjects, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) return; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); ObjectManager.StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, ObjectManager.ObjectLoadMode.Normal, false, false, false); if (so == null) { CarObjects[i] = null; } else { double sx = TrainManager.Trains[k].Cars[i].Width; double sy = TrainManager.Trains[k].Cars[i].Height; double sz = TrainManager.Trains[k].Cars[i].Length; CsvB3dObjectParser.ApplyScale(so, sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object int j = TrainManager.Trains[k].Cars[i].CarSections.Length; Array.Resize(ref TrainManager.Trains[k].Cars[i].CarSections, j + 1); if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } } } // place cars { double z = 0.0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].FrontAxlePosition; TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].RearAxlePosition; TrainManager.Trains[k].Cars[i].BeaconReceiver.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].BeaconReceiverPosition; z -= TrainManager.Trains[k].Cars[i].Length; if (i < TrainManager.Trains[k].Cars.Length - 1) { z -= 0.5 * (TrainManager.Trains[k].Couplers[i].MinimumDistanceBetweenCars + TrainManager.Trains[k].Couplers[i].MaximumDistanceBetweenCars); } } } // configure ai / timetable if (TrainManager.Trains[k] == TrainManager.PlayerTrain) { TrainManager.Trains[k].TimetableDelta = 0.0; } else if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { TrainManager.Trains[k].AI = new Game.SimpleHumanDriverAI(TrainManager.Trains[k]); TrainManager.Trains[k].TimetableDelta = Game.PrecedingTrainTimeDeltas[k]; TrainManager.Trains[k].Specs.DoorOpenMode = TrainManager.DoorMode.Manual; TrainManager.Trains[k].Specs.DoorCloseMode = TrainManager.DoorMode.Manual; } } TrainProgress = 1.0; // finished created objects System.Threading.Thread.Sleep(1); if (Cancel) return; ObjectManager.FinishCreatingObjects(); // update sections if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // load plugin for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i].State != TrainManager.TrainState.Bogus) { if (TrainManager.Trains[i] == TrainManager.PlayerTrain) { if (!PluginManager.LoadCustomPlugin(TrainManager.Trains[i], CurrentTrainFolder, CurrentTrainEncoding)) { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } else { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/MainLoop.cs000066400000000000000000002713301171674032100216130ustar00rootroot00000000000000using System; using System.Windows.Forms; using OpenBveApi.Math; using Tao.OpenGl; using Tao.Sdl; namespace OpenBve { internal static class MainLoop { // declarations internal static bool LimitFramerate = false; private static bool Quit = false; private static int TimeFactor = 1; private static ViewPortMode CurrentViewPortMode = ViewPortMode.Scenery; // -------------------------------- internal static void StartLoopEx(formMain.MainDialogResult result) { Renderer.Initialize(); Renderer.InitializeLighting(); Sdl.SDL_GL_SwapBuffers(); MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); MainLoop.InitializeMotionBlur(); ProcessEvents(); Gl.glDisable(Gl.GL_FOG); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glPushMatrix(); Gl.glLoadIdentity(); Gl.glOrtho(0.0, (double)Screen.Width, (double)Screen.Height, 0.0, -1.0, 1.0); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); Gl.glLoadIdentity(); Renderer.DrawLoadingScreen(); Gl.glPopMatrix(); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glPopMatrix(); Gl.glMatrixMode(Gl.GL_MODELVIEW); Sdl.SDL_GL_SwapBuffers(); Loading.LoadSynchronously(result.RouteFile, result.RouteEncoding, result.TrainFolder, result.TrainEncoding); Timetable.CreateTimetable(); for (int i = 0; i < Interface.MessageCount; i++) { if (Interface.Messages[i].Type == Interface.MessageType.Critical) { MessageBox.Show("A critical error has occured:\n\n" + Interface.Messages[i].Text + "\n\nPlease inspect the error log file for further information.", "Load", MessageBoxButtons.OK, MessageBoxIcon.Hand); return; } } Renderer.InitializeLighting(); Game.LogRouteName = System.IO.Path.GetFileName(result.RouteFile); Game.LogTrainName = System.IO.Path.GetFileName(result.TrainFolder); Game.LogDateTime = DateTime.Now; StartLoop(); } // start loop private static void StartLoop() { // load in advance Textures.UnloadAllTextures(); if (Interface.CurrentOptions.LoadInAdvance) { Textures.LoadAllTextures(); } // camera ObjectManager.InitializeVisibility(); TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, 0.0, true, false); TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, -0.1, true, false); TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, 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 = 0.0; for (int i = 0; i < Game.Stations.Length; i++) { if (Game.Stations[i].StopMode == Game.StationStopMode.AllStop | Game.Stations[i].StopMode == Game.StationStopMode.PlayerStop & Game.Stations[i].Stops.Length != 0) { PlayerFirstStationIndex = i; int s = Game.GetStopIndex(i, TrainManager.PlayerTrain.Cars.Length); if (s >= 0) { PlayerFirstStationPosition = Game.Stations[i].Stops[s].TrackPosition; } else { PlayerFirstStationPosition = Game.Stations[i].DefaultTrackPosition; } if (Game.Stations[i].ArrivalTime < 0.0) { if (Game.Stations[i].DepartureTime < 0.0) { Game.SecondsSinceMidnight = 0.0; Game.StartupTime = 0.0; } else { Game.SecondsSinceMidnight = Game.Stations[i].DepartureTime - Game.Stations[i].StopTime; Game.StartupTime = Game.Stations[i].DepartureTime - Game.Stations[i].StopTime; } } else { Game.SecondsSinceMidnight = Game.Stations[i].ArrivalTime; Game.StartupTime = Game.Stations[i].ArrivalTime; } break; } } 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 == Game.StationStopMode.AllStop | Game.Stations[i].StopMode == Game.StationStopMode.PlayerPass & Game.Stations[i].Stops.Length != 0) { OtherFirstStationIndex = i; int s = Game.GetStopIndex(i, 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.InitializeTrain(TrainManager.Trains[i]); int s = i == TrainManager.PlayerTrain.TrainIndex ? 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].Specs.AnticipatedLeftDoorsOpened = true; } } if (Game.Stations[s].OpenRightDoors) { for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Specs.AnticipatedRightDoorsOpened = 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.MoveCar(TrainManager.Trains[i], j, -length, 0.01); TrainManager.MoveCar(TrainManager.Trains[i], j, length, 0.01); } } // 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].StationType != Game.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 (i == TrainManager.PlayerTrain.TrainIndex) { p = PlayerFirstStationPosition; } else if (TrainManager.Trains[i].State == TrainManager.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.MoveCar(TrainManager.Trains[i], j, p, 0.01); } } // timetable if (Timetable.DefaultTimetableDescription.Length == 0) { Timetable.DefaultTimetableDescription = Game.LogTrainName; } // initialize camera if (World.CameraRestriction == World.CameraRestrictionMode.NotAvailable) { World.CameraMode = World.CameraViewMode.InteriorLookAhead; } TrainManager.UpdateCamera(TrainManager.PlayerTrain); TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, -1.0, true, false); ObjectManager.UpdateVisibility(World.CameraTrackFollower.TrackPosition + World.CameraCurrentAlignment.Position.Z); World.CameraSavedInterior = new World.CameraAlignment(); World.CameraSavedExterior = new World.CameraAlignment(new Vector3(-2.5, 1.5, -15.0), 0.3, -0.2, 0.0, PlayerFirstStationPosition, 1.0); World.CameraSavedTrack = new World.CameraAlignment(new Vector3(-3.0, 2.5, 0.0), 0.3, 0.0, 0.0, TrainManager.PlayerTrain.Cars[0].FrontAxle.Follower.TrackPosition - 10.0, 1.0); // timer Timers.Initialize(); // framerate display double TotalTimeElapsedForInfo = 0.0; double TotalTimeElapsedForSectionUpdate = 0.0; int TotalFramesElapsed = 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); // 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) { Timetable.CurrentTimetable = Timetable.TimetableState.Custom; } } // 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.Messages[i].FileNotFound) { filesNotFound++; } else if (Interface.Messages[i].Type == Interface.MessageType.Error) { errors++; } else if (Interface.Messages[i].Type == Interface.MessageType.Warning) { warnings++; } } if (filesNotFound != 0) { Game.AddDebugMessage(filesNotFound.ToString() + " file(s) not found", 10.0); } if (errors != 0 & warnings != 0) { Game.AddDebugMessage(errors.ToString() + " error(s), " + warnings.ToString() + " warning(s)", 10.0); } else if (errors != 0) { Game.AddDebugMessage(errors.ToString() + " error(s)", 10.0); } else { Game.AddDebugMessage(warnings.ToString() + " warning(s)", 10.0); } } // if (TrainManager.PlayerTrain.Plugin != null && TrainManager.PlayerTrain.Plugin is Win32Plugin) { // Game.AddDebugMessage("The train uses a Win32 plugin.", 10.0); // } // loop World.InitializeCameraRestriction(); while (true) { // timer double RealTimeElapsed; double TimeElapsed; if (Game.SecondsSinceMidnight >= Game.StartupTime) { RealTimeElapsed = Timers.GetElapsedTime(); TimeElapsed = RealTimeElapsed * (double)TimeFactor; } else { RealTimeElapsed = 0.0; TimeElapsed = Game.StartupTime - Game.SecondsSinceMidnight; } TotalTimeElapsedForInfo += TimeElapsed; TotalTimeElapsedForSectionUpdate += TimeElapsed; TotalFramesElapsed++; if (TotalTimeElapsedForSectionUpdate >= 1.0) { if (Game.Sections.Length != 0) { Game.UpdateSection(Game.Sections.Length - 1); } TotalTimeElapsedForSectionUpdate = 0.0; } if (TotalTimeElapsedForInfo >= 0.2) { Game.InfoFrameRate = (double)TimeFactor * (double)TotalFramesElapsed / TotalTimeElapsedForInfo; TotalTimeElapsedForInfo = 0.0; TotalFramesElapsed = 0; } // events UpdateControlRepeats(RealTimeElapsed); ProcessEvents(); World.CameraAlignmentDirection = new World.CameraAlignment(); World.UpdateMouseGrab(TimeElapsed); ProcessControls(TimeElapsed); if (Quit) break; // update simulation in chunks { const double chunkTime = 1.0 / 75.0; if (TimeElapsed <= chunkTime) { Game.SecondsSinceMidnight += TimeElapsed; TrainManager.UpdateTrains(TimeElapsed); } else { const int maxChunks = 75; int chunks = Math.Min((int)Math.Round(TimeElapsed / chunkTime), maxChunks); double time = TimeElapsed / (double)chunks; for (int i = 0; i < chunks; i++) { Game.SecondsSinceMidnight += time; TrainManager.UpdateTrains(time); } } } // update in one piece ObjectManager.UpdateAnimatedWorldObjects(TimeElapsed, false); if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead | World.CameraMode == World.CameraViewMode.Exterior) { TrainManager.UpdateCamera(TrainManager.PlayerTrain); } if (World.CameraRestriction == World.CameraRestrictionMode.NotAvailable) { World.UpdateDriverBody(TimeElapsed); } World.UpdateAbsoluteCamera(TimeElapsed); TrainManager.UpdateTrainObjects(TimeElapsed, false); if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead | World.CameraMode == World.CameraViewMode.Exterior) { ObjectManager.UpdateVisibility(World.CameraTrackFollower.TrackPosition + World.CameraCurrentAlignment.Position.Z); int d = TrainManager.PlayerTrain.DriverCar; World.CameraSpeed = TrainManager.PlayerTrain.Cars[d].Specs.CurrentSpeed; } else { World.CameraSpeed = 0.0; } Game.UpdateScore(TimeElapsed); Game.UpdateMessages(); Game.UpdateScoreMessages(TimeElapsed); Sounds.Update(TimeElapsed); Renderer.RenderScene(TimeElapsed); Sdl.SDL_GL_SwapBuffers(); Game.UpdateBlackBox(); // pause/menu while (Game.CurrentInterface != Game.InterfaceType.Normal) { UpdateControlRepeats(RealTimeElapsed); ProcessEvents(); ProcessControls(0.0); if (Quit) break; if (Game.CurrentInterface == Game.InterfaceType.Pause) { System.Threading.Thread.Sleep(10); } Renderer.RenderScene(TimeElapsed); Sdl.SDL_GL_SwapBuffers(); TimeElapsed = Timers.GetElapsedTime(); } // limit framerate if (LimitFramerate) { System.Threading.Thread.Sleep(10); } #if DEBUG CheckForOpenGlError("MainLoop"); #endif } // finish try { Interface.SaveLogs(); } catch { } try { Interface.SaveOptions(); } catch { } } // -------------------------------- // repeats private struct ControlRepeat { internal int ControlIndex; internal double Countdown; internal ControlRepeat(int controlIndex, double countdown) { this.ControlIndex = controlIndex; this.Countdown = countdown; } } private static ControlRepeat[] RepeatControls = new ControlRepeat[16]; private static int RepeatControlsUsed = 0; private static void AddControlRepeat(int controlIndex) { if (RepeatControls.Length == RepeatControlsUsed) { Array.Resize(ref RepeatControls, RepeatControls.Length << 1); } RepeatControls[RepeatControlsUsed] = new ControlRepeat(controlIndex, Interface.CurrentOptions.KeyRepeatDelay); RepeatControlsUsed++; } private static void RemoveControlRepeat(int controlIndex) { for (int i = 0; i < RepeatControlsUsed; i++) { if (RepeatControls[i].ControlIndex == controlIndex) { RepeatControls[i] = RepeatControls[RepeatControlsUsed - 1]; RepeatControlsUsed--; break; } } } private static void UpdateControlRepeats(double timeElapsed) { for (int i = 0; i < RepeatControlsUsed; i++) { RepeatControls[i].Countdown -= timeElapsed; if (RepeatControls[i].Countdown <= 0.0) { int j = RepeatControls[i].ControlIndex; Interface.CurrentControls[j].AnalogState = 1.0; Interface.CurrentControls[j].DigitalState = Interface.DigitalControlState.Pressed; RepeatControls[i].Countdown += Interface.CurrentOptions.KeyRepeatInterval; } } } // process events private static Interface.KeyboardModifier CurrentKeyboardModifier = Interface.KeyboardModifier.None; private static void ProcessEvents() { Sdl.SDL_Event Event; while (Sdl.SDL_PollEvent(out Event) != 0) { switch (Event.type) { // quit case Sdl.SDL_QUIT: Quit = true; return; // resize case Sdl.SDL_VIDEORESIZE: Screen.Width = Event.resize.w; Screen.Height = Event.resize.h; UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); InitializeMotionBlur(); break; // key down case Sdl.SDL_KEYDOWN: if (Event.key.keysym.sym == Sdl.SDLK_LSHIFT | Event.key.keysym.sym == Sdl.SDLK_RSHIFT) CurrentKeyboardModifier |= Interface.KeyboardModifier.Shift; if (Event.key.keysym.sym == Sdl.SDLK_LCTRL | Event.key.keysym.sym == Sdl.SDLK_RCTRL) CurrentKeyboardModifier |= Interface.KeyboardModifier.Ctrl; if (Event.key.keysym.sym == Sdl.SDLK_LALT | Event.key.keysym.sym == Sdl.SDLK_RALT) CurrentKeyboardModifier |= Interface.KeyboardModifier.Alt; for (int i = 0; i < Interface.CurrentControls.Length; i++) { if (Interface.CurrentControls[i].Method == Interface.ControlMethod.Keyboard) { if (Interface.CurrentControls[i].Element == Event.key.keysym.sym & Interface.CurrentControls[i].Modifier == CurrentKeyboardModifier) { Interface.CurrentControls[i].AnalogState = 1.0; Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.Pressed; AddControlRepeat(i); } } } break; // key up case Sdl.SDL_KEYUP: if (Event.key.keysym.sym == Sdl.SDLK_LSHIFT | Event.key.keysym.sym == Sdl.SDLK_RSHIFT) CurrentKeyboardModifier &= ~Interface.KeyboardModifier.Shift; if (Event.key.keysym.sym == Sdl.SDLK_LCTRL | Event.key.keysym.sym == Sdl.SDLK_RCTRL) CurrentKeyboardModifier &= ~Interface.KeyboardModifier.Ctrl; if (Event.key.keysym.sym == Sdl.SDLK_LALT | Event.key.keysym.sym == Sdl.SDLK_RALT) CurrentKeyboardModifier &= ~Interface.KeyboardModifier.Alt; for (int i = 0; i < Interface.CurrentControls.Length; i++) { if (Interface.CurrentControls[i].Method == Interface.ControlMethod.Keyboard) { if (Interface.CurrentControls[i].Element == Event.key.keysym.sym) { Interface.CurrentControls[i].AnalogState = 0.0; Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.Released; RemoveControlRepeat(i); } } } break; // joystick button down case Sdl.SDL_JOYBUTTONDOWN: if (Interface.CurrentOptions.UseJoysticks) { for (int i = 0; i < Interface.CurrentControls.Length; i++) { if (Interface.CurrentControls[i].Method == Interface.ControlMethod.Joystick) { if (Interface.CurrentControls[i].Component == Interface.JoystickComponent.Button) { if (Interface.CurrentControls[i].Device == (int)Event.jbutton.which & Interface.CurrentControls[i].Element == (int)Event.jbutton.button) { Interface.CurrentControls[i].AnalogState = 1.0; Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.Pressed; AddControlRepeat(i); } } } } } break; // joystick button up case Sdl.SDL_JOYBUTTONUP: if (Interface.CurrentOptions.UseJoysticks) { for (int i = 0; i < Interface.CurrentControls.Length; i++) { if (Interface.CurrentControls[i].Method == Interface.ControlMethod.Joystick) { if (Interface.CurrentControls[i].Component == Interface.JoystickComponent.Button) { if (Interface.CurrentControls[i].Device == (int)Event.jbutton.which & Interface.CurrentControls[i].Element == (int)Event.jbutton.button) { Interface.CurrentControls[i].AnalogState = 0.0; Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.Released; RemoveControlRepeat(i); } } } } } break; // joystick hat case Sdl.SDL_JOYHATMOTION: if (Interface.CurrentOptions.UseJoysticks) { for (int i = 0; i < Interface.CurrentControls.Length; i++) { if (Interface.CurrentControls[i].Method == Interface.ControlMethod.Joystick) { if (Interface.CurrentControls[i].Component == Interface.JoystickComponent.Hat) { if (Interface.CurrentControls[i].Device == (int)Event.jhat.which) { if (Interface.CurrentControls[i].Element == (int)Event.jhat.hat) { if (Interface.CurrentControls[i].Direction == (int)Event.jhat.val) { Interface.CurrentControls[i].AnalogState = 1.0; Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.Pressed; } else { Interface.CurrentControls[i].AnalogState = 0.0; Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.Released; } } } } } } } break; // joystick axis case Sdl.SDL_JOYAXISMOTION: if (Interface.CurrentOptions.UseJoysticks) { for (int i = 0; i < Interface.CurrentControls.Length; i++) { if (Interface.CurrentControls[i].Method == Interface.ControlMethod.Joystick) { if (Interface.CurrentControls[i].Component == Interface.JoystickComponent.Axis) { if (Interface.CurrentControls[i].Device == (int)Event.jaxis.which & Interface.CurrentControls[i].Element == (int)Event.jaxis.axis) { double a = (double)Event.jaxis.val / 32768.0; if (Interface.CurrentControls[i].InheritedType == Interface.CommandType.AnalogHalf) { if (Math.Sign(a) == Math.Sign(Interface.CurrentControls[i].Direction)) { a = Math.Abs(a); if (a < Interface.CurrentOptions.JoystickAxisThreshold) { Interface.CurrentControls[i].AnalogState = 0.0; } else if (Interface.CurrentOptions.JoystickAxisThreshold != 1.0) { Interface.CurrentControls[i].AnalogState = (a - Interface.CurrentOptions.JoystickAxisThreshold) / (1.0 - Interface.CurrentOptions.JoystickAxisThreshold); } else { Interface.CurrentControls[i].AnalogState = 1.0; } } } else if (Interface.CurrentControls[i].InheritedType == Interface.CommandType.AnalogFull) { a *= (double)Interface.CurrentControls[i].Direction; if (a > -Interface.CurrentOptions.JoystickAxisThreshold & a < Interface.CurrentOptions.JoystickAxisThreshold) { Interface.CurrentControls[i].AnalogState = 0.0; } else if (Interface.CurrentOptions.JoystickAxisThreshold != 1.0) { if (a < 0.0) { Interface.CurrentControls[i].AnalogState = (a + Interface.CurrentOptions.JoystickAxisThreshold) / (1.0 - Interface.CurrentOptions.JoystickAxisThreshold); } else if (a > 0.0) { Interface.CurrentControls[i].AnalogState = (a - Interface.CurrentOptions.JoystickAxisThreshold) / (1.0 - Interface.CurrentOptions.JoystickAxisThreshold); } else { Interface.CurrentControls[i].AnalogState = 0.0; } } else { Interface.CurrentControls[i].AnalogState = (double)Math.Sign(a); } } else { if (Math.Sign(a) == Math.Sign(Interface.CurrentControls[i].Direction)) { a = Math.Abs(a); if (a < Interface.CurrentOptions.JoystickAxisThreshold) { a = 0.0; } else if (Interface.CurrentOptions.JoystickAxisThreshold != 1.0) { a = (a - Interface.CurrentOptions.JoystickAxisThreshold) / (1.0 - Interface.CurrentOptions.JoystickAxisThreshold); } else { a = 1.0; } if (Interface.CurrentControls[i].DigitalState == Interface.DigitalControlState.Released | Interface.CurrentControls[i].DigitalState == Interface.DigitalControlState.ReleasedAcknowledged) { if (a > 0.67) Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.Pressed; } else { if (a < 0.33) Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.Released; } } } } } } } } break; case Sdl.SDL_MOUSEBUTTONDOWN: // mouse button down if (Event.button.button == Sdl.SDL_BUTTON_RIGHT) { // mouse grab World.MouseGrabEnabled = !World.MouseGrabEnabled; if (World.MouseGrabEnabled) { World.MouseGrabTarget = new World.Vector2D(0.0, 0.0); Sdl.SDL_WM_GrabInput(Sdl.SDL_GRAB_ON); Game.AddMessage(Interface.GetInterfaceString("notification_mousegrab_on"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 5.0); } else { Sdl.SDL_WM_GrabInput(Sdl.SDL_GRAB_OFF); Game.AddMessage(Interface.GetInterfaceString("notification_mousegrab_off"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 5.0); } } break; case Sdl.SDL_MOUSEMOTION: // mouse motion if (World.MouseGrabIgnoreOnce) { World.MouseGrabIgnoreOnce = false; } else if (World.MouseGrabEnabled) { World.MouseGrabTarget = new World.Vector2D((double)Event.motion.xrel, (double)Event.motion.yrel); } break; } } } // process controls private static void ProcessControls(double TimeElapsed) { switch (Game.CurrentInterface) { case Game.InterfaceType.Pause: // pause for (int i = 0; i < Interface.CurrentControls.Length; i++) { if (Interface.CurrentControls[i].InheritedType == Interface.CommandType.Digital) { if (Interface.CurrentControls[i].DigitalState == Interface.DigitalControlState.Pressed) { Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.PressedAcknowledged; switch (Interface.CurrentControls[i].Command) { case Interface.Command.MiscPause: Game.CurrentInterface = Game.InterfaceType.Normal; break; case Interface.Command.MenuActivate: Game.CreateMenu(false); Game.CurrentInterface = Game.InterfaceType.Menu; break; case Interface.Command.MiscQuit: Game.CreateMenu(true); Game.CurrentInterface = Game.InterfaceType.Menu; break; case Interface.Command.MiscFullscreen: Screen.ToggleFullscreen(); break; case Interface.Command.MiscMute: Sounds.GlobalMute = !Sounds.GlobalMute; Sounds.Update(TimeElapsed); break; } } } } break; case Game.InterfaceType.Menu: // menu for (int i = 0; i < Interface.CurrentControls.Length; i++) { if (Interface.CurrentControls[i].InheritedType == Interface.CommandType.Digital) { if (Interface.CurrentControls[i].DigitalState == Interface.DigitalControlState.Pressed) { Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.PressedAcknowledged; switch (Interface.CurrentControls[i].Command) { case Interface.Command.MenuUp: { // up Game.MenuEntry[] a = Game.CurrentMenu; int j = Game.CurrentMenuSelection.Length - 1; int k = 0; while (k < j) { Game.MenuSubmenu b = a[Game.CurrentMenuSelection[k]] as Game.MenuSubmenu; if (b == null) break; a = b.Entries; k++; } if (Game.CurrentMenuSelection[j] > 0 && !(a[Game.CurrentMenuSelection[j] - 1] is Game.MenuCaption)) { Game.CurrentMenuSelection[j]--; } } break; case Interface.Command.MenuDown: { // down Game.MenuEntry[] a = Game.CurrentMenu; int j = Game.CurrentMenuSelection.Length - 1; int k = 0; while (k < j) { Game.MenuSubmenu b = a[Game.CurrentMenuSelection[k]] as Game.MenuSubmenu; if (b == null) break; a = b.Entries; k++; } if (Game.CurrentMenuSelection[j] < a.Length - 1) { Game.CurrentMenuSelection[j]++; } } break; case Interface.Command.MenuEnter: { // enter Game.MenuEntry[] a = Game.CurrentMenu; int j = Game.CurrentMenuSelection.Length - 1; { int k = 0; while (k < j) { Game.MenuSubmenu b = a[Game.CurrentMenuSelection[k]] as Game.MenuSubmenu; if (b == null) break; a = b.Entries; k++; } } if (a[Game.CurrentMenuSelection[j]] is Game.MenuCommand) { // command Game.MenuCommand b = (Game.MenuCommand)a[Game.CurrentMenuSelection[j]]; switch (b.Tag) { case Game.MenuTag.Back: // back if (Game.CurrentMenuSelection.Length <= 1) { Game.CurrentInterface = Game.InterfaceType.Normal; } else { Array.Resize(ref Game.CurrentMenuSelection, Game.CurrentMenuSelection.Length - 1); Array.Resize(ref Game.CurrentMenuOffsets, Game.CurrentMenuOffsets.Length - 1); } break; case Game.MenuTag.JumpToStation: // jump to station TrainManager.JumpTrain(TrainManager.PlayerTrain, b.Data); break; case Game.MenuTag.ExitToMainMenu: Program.RestartArguments = Interface.CurrentOptions.GameMode == Interface.GameMode.Arcade ? "/review" : ""; Quit = true; break; case Game.MenuTag.Quit: // quit Quit = true; break; } } else if (a[Game.CurrentMenuSelection[j]] is Game.MenuSubmenu) { // menu Game.MenuSubmenu b = (Game.MenuSubmenu)a[Game.CurrentMenuSelection[j]]; int n = Game.CurrentMenuSelection.Length; Array.Resize(ref Game.CurrentMenuSelection, n + 1); Array.Resize(ref Game.CurrentMenuOffsets, n + 1); /* Select the first non-caption entry. */ int selection; for (selection = 0; selection < b.Entries.Length; selection++) { if (!(b.Entries[selection] is Game.MenuCaption)) break; } /* Select the next station if this menu has stations in it. */ int station = TrainManager.PlayerTrain.LastStation; if (TrainManager.PlayerTrain.Station == -1 | TrainManager.PlayerTrain.StationState != TrainManager.TrainStopState.Pending) { for (int k = station + 1; k < Game.Stations.Length; k++) { if (Game.StopsAtStation(k, TrainManager.PlayerTrain)) { station = k; break; } } } for (int k = selection + 1; k < b.Entries.Length; k++) { Game.MenuCommand c = b.Entries[k] as Game.MenuCommand; if (c != null && c.Tag == Game.MenuTag.JumpToStation) { if (c.Data <= station) { selection = k; } } } Game.CurrentMenuSelection[n] = selection < b.Entries.Length ? selection : 0; Game.CurrentMenuOffsets[n] = double.NegativeInfinity; a = b.Entries; for (int h = 0; h < a.Length; h++) { a[h].Highlight = h == 0 ? 1.0 : 0.0; a[h].Alpha = 0.0; } } } break; case Interface.Command.MenuBack: // back if (Game.CurrentMenuSelection.Length <= 1) { Game.CurrentInterface = Game.InterfaceType.Normal; } else { Array.Resize(ref Game.CurrentMenuSelection, Game.CurrentMenuSelection.Length - 1); Array.Resize(ref Game.CurrentMenuOffsets, Game.CurrentMenuOffsets.Length - 1); } break; case Interface.Command.MiscFullscreen: // fullscreen Screen.ToggleFullscreen(); break; case Interface.Command.MiscMute: // mute Sounds.GlobalMute = !Sounds.GlobalMute; Sounds.Update(TimeElapsed); break; } } } } break; case Game.InterfaceType.Normal: // normal for (int i = 0; i < Interface.CurrentControls.Length; i++) { if (Interface.CurrentControls[i].InheritedType == Interface.CommandType.AnalogHalf | Interface.CurrentControls[i].InheritedType == Interface.CommandType.AnalogFull) { // analog control if (Interface.CurrentControls[i].AnalogState != 0.0) { switch (Interface.CurrentControls[i].Command) { case Interface.Command.PowerHalfAxis: case Interface.Command.PowerFullAxis: // power half/full-axis if (!TrainManager.PlayerTrain.Specs.SingleHandle) { double a = Interface.CurrentControls[i].AnalogState; if (Interface.CurrentControls[i].Command == Interface.Command.BrakeFullAxis) { a = 0.5 * (a + 1.0); } a *= (double)TrainManager.PlayerTrain.Specs.MaximumPowerNotch; int p = (int)Math.Round(a); TrainManager.ApplyNotch(TrainManager.PlayerTrain, p, false, 0, true); } break; case Interface.Command.BrakeHalfAxis: case Interface.Command.BrakeFullAxis: // brake half/full-axis if (!TrainManager.PlayerTrain.Specs.SingleHandle) { int d = TrainManager.PlayerTrain.DriverCar; if (TrainManager.PlayerTrain.Cars[d].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { double a = Interface.CurrentControls[i].AnalogState; if (Interface.CurrentControls[i].Command == Interface.Command.BrakeFullAxis) { a = 0.5 * (a + 1.0); } int b = (int)Math.Round(3.0 * a); switch (b) { case 0: TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver = false; TrainManager.ApplyAirBrakeHandle(TrainManager.PlayerTrain, TrainManager.AirBrakeHandleState.Release); break; case 1: TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver = false; TrainManager.ApplyAirBrakeHandle(TrainManager.PlayerTrain, TrainManager.AirBrakeHandleState.Lap); break; case 2: TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver = false; TrainManager.ApplyAirBrakeHandle(TrainManager.PlayerTrain, TrainManager.AirBrakeHandleState.Service); break; case 3: TrainManager.ApplyEmergencyBrake(TrainManager.PlayerTrain); break; } } else { if (TrainManager.PlayerTrain.Specs.HasHoldBrake) { double a = Interface.CurrentControls[i].AnalogState; if (Interface.CurrentControls[i].Command == Interface.Command.BrakeFullAxis) { a = 0.5 * (a + 1.0); } a *= (double)TrainManager.PlayerTrain.Specs.MaximumBrakeNotch + 2; int b = (int)Math.Round(a); bool q = b == 1; if (b > 0) b--; if (b <= TrainManager.PlayerTrain.Specs.MaximumBrakeNotch) { TrainManager.UnapplyEmergencyBrake(TrainManager.PlayerTrain); TrainManager.ApplyNotch(TrainManager.PlayerTrain, 0, true, b, false); } else { TrainManager.ApplyEmergencyBrake(TrainManager.PlayerTrain); } TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, q); } else { double a = Interface.CurrentControls[i].AnalogState; if (Interface.CurrentControls[i].Command == Interface.Command.BrakeFullAxis) { a = 0.5 * (a + 1.0); } a *= (double)TrainManager.PlayerTrain.Specs.MaximumBrakeNotch + 1; int b = (int)Math.Round(a); if (b <= TrainManager.PlayerTrain.Specs.MaximumBrakeNotch) { TrainManager.UnapplyEmergencyBrake(TrainManager.PlayerTrain); TrainManager.ApplyNotch(TrainManager.PlayerTrain, 0, true, b, false); } else { TrainManager.ApplyEmergencyBrake(TrainManager.PlayerTrain); } } } } break; case Interface.Command.SingleFullAxis: // single full axis if (TrainManager.PlayerTrain.Specs.SingleHandle) { if (TrainManager.PlayerTrain.Specs.HasHoldBrake) { double a = Interface.CurrentControls[i].AnalogState; int p = (int)Math.Round(a * (double)TrainManager.PlayerTrain.Specs.MaximumPowerNotch); int b = (int)Math.Round(-a * (double)TrainManager.PlayerTrain.Specs.MaximumBrakeNotch + 2); if (p < 0) p = 0; if (b < 0) b = 0; bool q = b == 1; if (b > 0) b--; if (b <= TrainManager.PlayerTrain.Specs.MaximumBrakeNotch) { TrainManager.UnapplyEmergencyBrake(TrainManager.PlayerTrain); TrainManager.ApplyNotch(TrainManager.PlayerTrain, p, false, b, false); } else { TrainManager.ApplyEmergencyBrake(TrainManager.PlayerTrain); } TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, q); } else { double a = Interface.CurrentControls[i].AnalogState; int p = (int)Math.Round(a * (double)TrainManager.PlayerTrain.Specs.MaximumPowerNotch); int b = (int)Math.Round(-a * ((double)TrainManager.PlayerTrain.Specs.MaximumBrakeNotch + 1)); if (p < 0) p = 0; if (b < 0) b = 0; if (b <= TrainManager.PlayerTrain.Specs.MaximumBrakeNotch) { TrainManager.UnapplyEmergencyBrake(TrainManager.PlayerTrain); TrainManager.ApplyNotch(TrainManager.PlayerTrain, p, false, b, false); } else { TrainManager.ApplyEmergencyBrake(TrainManager.PlayerTrain); } } } break; case Interface.Command.ReverserFullAxis: // reverser full axis { double a = Interface.CurrentControls[i].AnalogState; int r = (int)Math.Round(a); TrainManager.ApplyReverser(TrainManager.PlayerTrain, r, false); } break; case Interface.Command.CameraMoveForward: // camera move forward if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead | World.CameraMode == World.CameraViewMode.Exterior) { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopSpeed : World.CameraExteriorTopSpeed; World.CameraAlignmentDirection.Position.Z = s * Interface.CurrentControls[i].AnalogState; } else { World.CameraAlignmentDirection.TrackPosition = World.CameraExteriorTopSpeed * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraMoveBackward: // camera move backward if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead | World.CameraMode == World.CameraViewMode.Exterior) { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopSpeed : World.CameraExteriorTopSpeed; World.CameraAlignmentDirection.Position.Z = -s * Interface.CurrentControls[i].AnalogState; } else { World.CameraAlignmentDirection.TrackPosition = -World.CameraExteriorTopSpeed * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraMoveLeft: // camera move left { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopSpeed : World.CameraExteriorTopSpeed; World.CameraAlignmentDirection.Position.X = -s * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraMoveRight: // camera move right { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopSpeed : World.CameraExteriorTopSpeed; World.CameraAlignmentDirection.Position.X = s * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraMoveUp: // camera move up { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopSpeed : World.CameraExteriorTopSpeed; World.CameraAlignmentDirection.Position.Y = s * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraMoveDown: // camera move down { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopSpeed : World.CameraExteriorTopSpeed; World.CameraAlignmentDirection.Position.Y = -s * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraRotateLeft: // camera rotate left { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopAngularSpeed : World.CameraExteriorTopAngularSpeed; World.CameraAlignmentDirection.Yaw = -s * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraRotateRight: // camera rotate right { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopAngularSpeed : World.CameraExteriorTopAngularSpeed; World.CameraAlignmentDirection.Yaw = s * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraRotateUp: // camera rotate up { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopAngularSpeed : World.CameraExteriorTopAngularSpeed; World.CameraAlignmentDirection.Pitch = s * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraRotateDown: // camera rotate down { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopAngularSpeed : World.CameraExteriorTopAngularSpeed; World.CameraAlignmentDirection.Pitch = -s * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraRotateCCW: // camera rotate ccw if ((World.CameraMode != World.CameraViewMode.Interior & World.CameraMode != World.CameraViewMode.InteriorLookAhead) | World.CameraRestriction != World.CameraRestrictionMode.On) { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopAngularSpeed : World.CameraExteriorTopAngularSpeed; World.CameraAlignmentDirection.Roll = -s * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraRotateCW: // camera rotate cw if ((World.CameraMode != World.CameraViewMode.Interior & World.CameraMode != World.CameraViewMode.InteriorLookAhead) | World.CameraRestriction != World.CameraRestrictionMode.On) { double s = World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead ? World.CameraInteriorTopAngularSpeed : World.CameraExteriorTopAngularSpeed; World.CameraAlignmentDirection.Roll = s * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraZoomIn: // camera zoom in if (TimeElapsed > 0.0) { World.CameraAlignmentDirection.Zoom = -World.CameraZoomTopSpeed * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.CameraZoomOut: // camera zoom out if (TimeElapsed > 0.0) { World.CameraAlignmentDirection.Zoom = World.CameraZoomTopSpeed * Interface.CurrentControls[i].AnalogState; } break; case Interface.Command.TimetableUp: // timetable up if (TimeElapsed > 0.0) { const double scrollSpeed = 250.0; if (Timetable.CurrentTimetable == Timetable.TimetableState.Default) { Timetable.DefaultTimetablePosition += scrollSpeed * Interface.CurrentControls[i].AnalogState * TimeElapsed; if (Timetable.DefaultTimetablePosition > 0.0) Timetable.DefaultTimetablePosition = 0.0; } else if (Timetable.CurrentTimetable == Timetable.TimetableState.Custom) { Timetable.CustomTimetablePosition += scrollSpeed * Interface.CurrentControls[i].AnalogState * TimeElapsed; if (Timetable.CustomTimetablePosition > 0.0) Timetable.CustomTimetablePosition = 0.0; } } break; case Interface.Command.TimetableDown: // timetable down if (TimeElapsed > 0.0) { const double scrollSpeed = 250.0; if (Timetable.CurrentTimetable == Timetable.TimetableState.Default) { Timetable.DefaultTimetablePosition -= scrollSpeed * Interface.CurrentControls[i].AnalogState * TimeElapsed; double max; if (Timetable.DefaultTimetableTexture != null) { Textures.LoadTexture(Timetable.DefaultTimetableTexture, Textures.OpenGlTextureWrapMode.ClampClamp); max = Math.Min(Screen.Height - Timetable.DefaultTimetableTexture.Height, 0.0); } else { max = 0.0; } if (Timetable.DefaultTimetablePosition < max) Timetable.DefaultTimetablePosition = max; } else if (Timetable.CurrentTimetable == Timetable.TimetableState.Custom) { Timetable.CustomTimetablePosition -= scrollSpeed * Interface.CurrentControls[i].AnalogState * TimeElapsed; Textures.Texture texture = Timetable.CurrentCustomTimetableDaytimeTexture; if (texture == null) { texture = Timetable.CurrentCustomTimetableNighttimeTexture; } double max; if (texture != null) { Textures.LoadTexture(texture, Textures.OpenGlTextureWrapMode.ClampClamp); max = Math.Min(Screen.Height - texture.Height, 0.0); } else { max = 0.0; } if (Timetable.CustomTimetablePosition < max) Timetable.CustomTimetablePosition = max; } } break; } } } else if (Interface.CurrentControls[i].InheritedType == Interface.CommandType.Digital) { // digital control if (Interface.CurrentControls[i].DigitalState == Interface.DigitalControlState.Pressed) { // pressed Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.PressedAcknowledged; switch (Interface.CurrentControls[i].Command) { case Interface.Command.MiscQuit: // quit Game.CreateMenu(true); Game.CurrentInterface = Game.InterfaceType.Menu; break; case Interface.Command.CameraInterior: // camera: interior SaveCameraSettings(); bool lookahead = false; if (World.CameraMode != World.CameraViewMode.InteriorLookAhead & World.CameraRestriction == World.CameraRestrictionMode.NotAvailable) { Game.AddMessage(Interface.GetInterfaceString("notification_interior_lookahead"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); lookahead = true; } else { Game.AddMessage(Interface.GetInterfaceString("notification_interior"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); } World.CameraMode = World.CameraViewMode.Interior; RestoreCameraSettings(); for (int j = 0; j <= TrainManager.PlayerTrain.DriverCar; j++) { if (TrainManager.PlayerTrain.Cars[j].CarSections.Length != 0) { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, 0); } else { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, -1); } } for (int j = TrainManager.PlayerTrain.DriverCar + 1; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, -1); } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(TimeElapsed); World.UpdateViewingDistances(); if (World.CameraRestriction != World.CameraRestrictionMode.NotAvailable) { if (!World.PerformCameraRestrictionTest()) { World.InitializeCameraRestriction(); } } if (lookahead) { World.CameraMode = World.CameraViewMode.InteriorLookAhead; } break; case Interface.Command.CameraExterior: // camera: exterior Game.AddMessage(Interface.GetInterfaceString("notification_exterior"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); SaveCameraSettings(); World.CameraMode = World.CameraViewMode.Exterior; RestoreCameraSettings(); if (TrainManager.PlayerTrain.Cars.Length >= 1 && TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].CarSections.Length >= 2) { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, 1); } else { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, -1); } for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { if (j != TrainManager.PlayerTrain.DriverCar) { if (TrainManager.PlayerTrain.Cars[j].CarSections.Length >= 1) { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, 0); } else { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, -1); } } } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(TimeElapsed); World.UpdateViewingDistances(); break; case Interface.Command.CameraTrack: case Interface.Command.CameraFlyBy: // camera: track / fly-by { SaveCameraSettings(); if (Interface.CurrentControls[i].Command == Interface.Command.CameraTrack) { World.CameraMode = World.CameraViewMode.Track; Game.AddMessage(Interface.GetInterfaceString("notification_track"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); } else { if (World.CameraMode == World.CameraViewMode.FlyBy) { World.CameraMode = World.CameraViewMode.FlyByZooming; Game.AddMessage(Interface.GetInterfaceString("notification_flybyzooming"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); } else { World.CameraMode = World.CameraViewMode.FlyBy; Game.AddMessage(Interface.GetInterfaceString("notification_flybynormal"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); } } RestoreCameraSettings(); if (TrainManager.PlayerTrain.Cars.Length >= 1 && TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].CarSections.Length >= 2) { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, 1); } else { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, -1); } for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { if (j != TrainManager.PlayerTrain.DriverCar) { if (TrainManager.PlayerTrain.Cars[j].CarSections.Length >= 1) { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, 0); } else { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, -1); } } } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); UpdateViewport(ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(TimeElapsed); World.UpdateViewingDistances(); } break; case Interface.Command.CameraPreviousPOI: // camera: previous poi if (Game.ApplyPointOfInterest(-1, true)) { if (World.CameraMode != World.CameraViewMode.Track & World.CameraMode != World.CameraViewMode.FlyBy & World.CameraMode != World.CameraViewMode.FlyByZooming) { World.CameraMode = World.CameraViewMode.Track; Game.AddMessage(Interface.GetInterfaceString("notification_track"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); } double z = World.CameraCurrentAlignment.Position.Z; World.CameraCurrentAlignment.Position = new Vector3(World.CameraCurrentAlignment.Position.X, World.CameraCurrentAlignment.Position.Y, 0.0); World.CameraCurrentAlignment.Zoom = 0.0; World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); if (TrainManager.PlayerTrain.Cars.Length >= 1 && TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].CarSections.Length >= 2) { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, 1); } else { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, -1); } for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { if (j != TrainManager.PlayerTrain.DriverCar) { if (TrainManager.PlayerTrain.Cars[j].CarSections.Length >= 1) { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, 0); } else { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, -1); } } } TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, World.CameraTrackFollower.TrackPosition + z, true, false); World.CameraCurrentAlignment.TrackPosition = World.CameraTrackFollower.TrackPosition; World.VerticalViewingAngle = World.OriginalVerticalViewingAngle; UpdateViewport(ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(TimeElapsed); World.UpdateViewingDistances(); } break; case Interface.Command.CameraNextPOI: // camera: next poi if (Game.ApplyPointOfInterest(1, true)) { if (World.CameraMode != World.CameraViewMode.Track & World.CameraMode != World.CameraViewMode.FlyBy & World.CameraMode != World.CameraViewMode.FlyByZooming) { World.CameraMode = World.CameraViewMode.Track; Game.AddMessage(Interface.GetInterfaceString("notification_track"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); } double z = World.CameraCurrentAlignment.Position.Z; World.CameraCurrentAlignment.Position = new Vector3(World.CameraCurrentAlignment.Position.X, World.CameraCurrentAlignment.Position.Y, 0.0); World.CameraCurrentAlignment.Zoom = 0.0; World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); if (TrainManager.PlayerTrain.Cars.Length >= 1 && TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].CarSections.Length >= 2) { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, 1); } else { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, -1); } for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { if (j != TrainManager.PlayerTrain.DriverCar) { if (TrainManager.PlayerTrain.Cars[j].CarSections.Length >= 1) { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, 0); } else { TrainManager.ChangeCarSection(TrainManager.PlayerTrain, j, -1); } } } TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, World.CameraTrackFollower.TrackPosition + z, true, false); World.CameraCurrentAlignment.TrackPosition = World.CameraTrackFollower.TrackPosition; World.VerticalViewingAngle = World.OriginalVerticalViewingAngle; UpdateViewport(ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(TimeElapsed); World.UpdateViewingDistances(); } break; case Interface.Command.CameraReset: // camera: reset if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) { World.CameraCurrentAlignment.Position = new Vector3(0.0, 0.0, 0.0); } World.CameraCurrentAlignment.Yaw = 0.0; World.CameraCurrentAlignment.Pitch = 0.0; World.CameraCurrentAlignment.Roll = 0.0; if (World.CameraMode == World.CameraViewMode.Track) { TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, TrainManager.PlayerTrain.Cars[0].FrontAxle.Follower.TrackPosition, true, false); } else if (World.CameraMode == World.CameraViewMode.FlyBy | World.CameraMode == World.CameraViewMode.FlyByZooming) { if (TrainManager.PlayerTrain.Specs.CurrentAverageSpeed >= 0.0) { double d = 30.0 + 4.0 * TrainManager.PlayerTrain.Specs.CurrentAverageSpeed; TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, TrainManager.PlayerTrain.Cars[0].FrontAxle.Follower.TrackPosition + d, true, false); } else { double d = 30.0 - 4.0 * TrainManager.PlayerTrain.Specs.CurrentAverageSpeed; TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.Cars.Length - 1].RearAxle.Follower.TrackPosition - d, true, false); } } World.CameraCurrentAlignment.TrackPosition = World.CameraTrackFollower.TrackPosition; World.CameraCurrentAlignment.Zoom = 0.0; World.VerticalViewingAngle = World.OriginalVerticalViewingAngle; World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); UpdateViewport(ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(TimeElapsed); World.UpdateViewingDistances(); if ((World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) & World.CameraRestriction == World.CameraRestrictionMode.On) { if (!World.PerformCameraRestrictionTest()) { World.InitializeCameraRestriction(); } } break; case Interface.Command.CameraRestriction: // camera: restriction if (World.CameraRestriction != World.CameraRestrictionMode.NotAvailable) { if (World.CameraRestriction == World.CameraRestrictionMode.Off) { World.CameraRestriction = World.CameraRestrictionMode.On; } else { World.CameraRestriction = World.CameraRestrictionMode.Off; } World.InitializeCameraRestriction(); if (World.CameraRestriction == World.CameraRestrictionMode.Off) { Game.AddMessage(Interface.GetInterfaceString("notification_camerarestriction_off"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); } else { Game.AddMessage(Interface.GetInterfaceString("notification_camerarestriction_on"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); } } break; case Interface.Command.SinglePower: // single power if (TrainManager.PlayerTrain.Specs.SingleHandle) { int b = TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver; if (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver) { TrainManager.UnapplyEmergencyBrake(TrainManager.PlayerTrain); } else if (b == 1 & TrainManager.PlayerTrain.Specs.HasHoldBrake) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, 0, true, 0, false); TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, true); } else if (TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, false); } else if (b > 0) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, 0, true, -1, true); } else { int p = TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver; if (p < TrainManager.PlayerTrain.Specs.MaximumPowerNotch) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, 1, true, 0, true); } } } break; case Interface.Command.SingleNeutral: // single neutral if (TrainManager.PlayerTrain.Specs.SingleHandle) { int p = TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver; if (p > 0) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, -1, true, 0, true); } else { int b = TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver; if (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver) { TrainManager.UnapplyEmergencyBrake(TrainManager.PlayerTrain); } else if (b == 1 & TrainManager.PlayerTrain.Specs.HasHoldBrake) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, 0, true, 0, false); TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, true); } else if (TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, false); } else if (b > 0) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, 0, true, -1, true); } } } break; case Interface.Command.SingleBrake: // single brake if (TrainManager.PlayerTrain.Specs.SingleHandle) { int p = TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver; if (p > 0) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, -1, true, 0, true); } else { int b = TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver; if (TrainManager.PlayerTrain.Specs.HasHoldBrake & b == 0 & !TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, true); } else if (b < TrainManager.PlayerTrain.Specs.MaximumBrakeNotch) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, 0, true, 1, true); TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, false); } } } break; case Interface.Command.SingleEmergency: // single emergency if (TrainManager.PlayerTrain.Specs.SingleHandle) { TrainManager.ApplyEmergencyBrake(TrainManager.PlayerTrain); } break; case Interface.Command.PowerIncrease: // power increase if (!TrainManager.PlayerTrain.Specs.SingleHandle) { int p = TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver; if (p < TrainManager.PlayerTrain.Specs.MaximumPowerNotch) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, 1, true, 0, true); } } break; case Interface.Command.PowerDecrease: // power decrease if (!TrainManager.PlayerTrain.Specs.SingleHandle) { int p = TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver; if (p > 0) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, -1, true, 0, true); } } break; case Interface.Command.BrakeIncrease: // brake increase if (!TrainManager.PlayerTrain.Specs.SingleHandle) { int d = TrainManager.PlayerTrain.DriverCar; if (TrainManager.PlayerTrain.Cars[d].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { if (TrainManager.PlayerTrain.Specs.HasHoldBrake & TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Release & !TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, true); } else if (TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { TrainManager.ApplyAirBrakeHandle(TrainManager.PlayerTrain, TrainManager.AirBrakeHandleState.Lap); TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, false); } else if (TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap) { TrainManager.ApplyAirBrakeHandle(TrainManager.PlayerTrain, TrainManager.AirBrakeHandleState.Service); } else if (TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Release) { TrainManager.ApplyAirBrakeHandle(TrainManager.PlayerTrain, TrainManager.AirBrakeHandleState.Lap); } } else { int b = TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver; if (TrainManager.PlayerTrain.Specs.HasHoldBrake & b == 0 & !TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, true); } else if (b < TrainManager.PlayerTrain.Specs.MaximumBrakeNotch) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, 0, true, 1, true); TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, false); } } } break; case Interface.Command.BrakeDecrease: // brake decrease if (!TrainManager.PlayerTrain.Specs.SingleHandle) { int d = TrainManager.PlayerTrain.DriverCar; if (TrainManager.PlayerTrain.Cars[d].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { if (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver) { TrainManager.UnapplyEmergencyBrake(TrainManager.PlayerTrain); } else if (TrainManager.PlayerTrain.Specs.HasHoldBrake & TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap & !TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, true); } else if (TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { TrainManager.ApplyAirBrakeHandle(TrainManager.PlayerTrain, TrainManager.AirBrakeHandleState.Release); TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, false); } else if (TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap) { TrainManager.ApplyAirBrakeHandle(TrainManager.PlayerTrain, TrainManager.AirBrakeHandleState.Release); } else if (TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service) { TrainManager.ApplyAirBrakeHandle(TrainManager.PlayerTrain, TrainManager.AirBrakeHandleState.Lap); } } else { int b = TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver; if (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver) { TrainManager.UnapplyEmergencyBrake(TrainManager.PlayerTrain); } else if (b == 1 & TrainManager.PlayerTrain.Specs.HasHoldBrake) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, 0, true, 0, false); TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, true); } else if (TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { TrainManager.ApplyHoldBrake(TrainManager.PlayerTrain, false); } else if (b > 0) { TrainManager.ApplyNotch(TrainManager.PlayerTrain, 0, true, -1, true); } } } break; case Interface.Command.BrakeEmergency: // brake emergency if (!TrainManager.PlayerTrain.Specs.SingleHandle) { TrainManager.ApplyEmergencyBrake(TrainManager.PlayerTrain); } break; case Interface.Command.DeviceConstSpeed: // const speed if (TrainManager.PlayerTrain.Specs.HasConstSpeed) { TrainManager.PlayerTrain.Specs.CurrentConstSpeed = !TrainManager.PlayerTrain.Specs.CurrentConstSpeed; } break; case Interface.Command.ReverserForward: // reverser forward if (TrainManager.PlayerTrain.Specs.CurrentReverser.Driver < 1) { TrainManager.ApplyReverser(TrainManager.PlayerTrain, 1, true); } break; case Interface.Command.ReverserBackward: // reverser backward if (TrainManager.PlayerTrain.Specs.CurrentReverser.Driver > -1) { TrainManager.ApplyReverser(TrainManager.PlayerTrain, -1, true); } break; case Interface.Command.HornPrimary: case Interface.Command.HornSecondary: case Interface.Command.HornMusic: // horn { int j = Interface.CurrentControls[i].Command == Interface.Command.HornPrimary ? 0 : Interface.CurrentControls[i].Command == Interface.Command.HornSecondary ? 1 : 2; int d = TrainManager.PlayerTrain.DriverCar; if (TrainManager.PlayerTrain.Cars[d].Sounds.Horns.Length > j) { Sounds.SoundBuffer buffer = TrainManager.PlayerTrain.Cars[d].Sounds.Horns[j].Sound.Buffer; if (buffer != null) { Vector3 pos = TrainManager.PlayerTrain.Cars[d].Sounds.Horns[j].Sound.Position; if (TrainManager.PlayerTrain.Cars[d].Sounds.Horns[j].Loop) { if (Sounds.IsPlaying(TrainManager.PlayerTrain.Cars[d].Sounds.Horns[j].Sound.Source)) { Sounds.StopSound(TrainManager.PlayerTrain.Cars[d].Sounds.Horns[j].Sound.Source); } else { TrainManager.PlayerTrain.Cars[d].Sounds.Horns[j].Sound.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, true); } } else { TrainManager.PlayerTrain.Cars[d].Sounds.Horns[j].Sound.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, false); } if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.HornBlow(j == 0 ? OpenBveApi.Runtime.HornTypes.Primary : j == 1 ? OpenBveApi.Runtime.HornTypes.Secondary : OpenBveApi.Runtime.HornTypes.Music); } } } } break; case Interface.Command.DoorsLeft: // doors: left if ((TrainManager.GetDoorsState(TrainManager.PlayerTrain, true, false) & TrainManager.TrainDoorState.Opened) == 0) { if (TrainManager.PlayerTrain.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic) { TrainManager.OpenTrainDoors(TrainManager.PlayerTrain, true, false); } } else { if (TrainManager.PlayerTrain.Specs.DoorCloseMode != TrainManager.DoorMode.Automatic) { TrainManager.CloseTrainDoors(TrainManager.PlayerTrain, true, false); } } break; case Interface.Command.DoorsRight: // doors: right if ((TrainManager.GetDoorsState(TrainManager.PlayerTrain, false, true) & TrainManager.TrainDoorState.Opened) == 0) { if (TrainManager.PlayerTrain.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic) { TrainManager.OpenTrainDoors(TrainManager.PlayerTrain, false, true); } } else { if (TrainManager.PlayerTrain.Specs.DoorCloseMode != TrainManager.DoorMode.Automatic) { TrainManager.CloseTrainDoors(TrainManager.PlayerTrain, false, true); } } break; case Interface.Command.SecurityS: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.S); } break; case Interface.Command.SecurityA1: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.A1); } break; case Interface.Command.SecurityA2: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.A2); } break; case Interface.Command.SecurityB1: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.B1); } break; case Interface.Command.SecurityB2: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.B2); } break; case Interface.Command.SecurityC1: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.C1); } break; case Interface.Command.SecurityC2: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.C2); } break; case Interface.Command.SecurityD: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.D); } break; case Interface.Command.SecurityE: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.E); } break; case Interface.Command.SecurityF: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.F); } break; case Interface.Command.SecurityG: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.G); } break; case Interface.Command.SecurityH: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.H); } break; case Interface.Command.SecurityI: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.I); } break; case Interface.Command.SecurityJ: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.J); } break; case Interface.Command.SecurityK: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.K); } break; case Interface.Command.SecurityL: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyDown(OpenBveApi.Runtime.VirtualKeys.L); } break; case Interface.Command.TimetableToggle: // option: timetable if (Timetable.CustomTimetableAvailable) { switch (Timetable.CurrentTimetable) { case Timetable.TimetableState.Custom: Timetable.CurrentTimetable = Timetable.TimetableState.Default; break; case Timetable.TimetableState.Default: Timetable.CurrentTimetable = Timetable.TimetableState.None; break; default: Timetable.CurrentTimetable = Timetable.TimetableState.Custom; break; } } else { switch (Timetable.CurrentTimetable) { case Timetable.TimetableState.Default: Timetable.CurrentTimetable = Timetable.TimetableState.None; break; default: Timetable.CurrentTimetable = Timetable.TimetableState.Default; break; } } break; case Interface.Command.DebugWireframe: // option: wireframe Renderer.OptionWireframe = !Renderer.OptionWireframe; if (Renderer.OptionWireframe) { Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_LINE); } else { Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_FILL); } Renderer.StaticOpaqueForceUpdate = true; break; case Interface.Command.DebugNormals: // option: normals Renderer.OptionNormals = !Renderer.OptionNormals; Renderer.StaticOpaqueForceUpdate = true; break; case Interface.Command.MiscAI: // option: AI if (Interface.CurrentOptions.GameMode == Interface.GameMode.Expert) { Game.AddMessage(Interface.GetInterfaceString("notification_notavailableexpert"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 5.0); } else { if (TrainManager.PlayerTrain.AI == null) { TrainManager.PlayerTrain.AI = new Game.SimpleHumanDriverAI(TrainManager.PlayerTrain); if (TrainManager.PlayerTrain.Plugin != null && !TrainManager.PlayerTrain.Plugin.SupportsAI) { Game.AddMessage(Interface.GetInterfaceString("notification_aiunable"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 10.0); } } else { TrainManager.PlayerTrain.AI = null; } } break; case Interface.Command.MiscInterfaceMode: // option: debug switch (Renderer.CurrentOutputMode) { case Renderer.OutputMode.Default: Renderer.CurrentOutputMode = Interface.CurrentOptions.GameMode == Interface.GameMode.Expert ? Renderer.OutputMode.None : Renderer.OutputMode.Debug; break; case Renderer.OutputMode.Debug: Renderer.CurrentOutputMode = Renderer.OutputMode.None; break; default: Renderer.CurrentOutputMode = Renderer.OutputMode.Default; break; } break; case Interface.Command.MiscBackfaceCulling: // option: backface culling Renderer.OptionBackfaceCulling = !Renderer.OptionBackfaceCulling; Renderer.StaticOpaqueForceUpdate = true; Game.AddMessage(Interface.GetInterfaceString(Renderer.OptionBackfaceCulling ? "notification_backfaceculling_on" : "notification_backfaceculling_off"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); break; case Interface.Command.MiscCPUMode: // option: limit frame rate LimitFramerate = !LimitFramerate; Game.AddMessage(Interface.GetInterfaceString(LimitFramerate ? "notification_cpu_low" : "notification_cpu_normal"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 2.0); break; case Interface.Command.DebugBrakeSystems: // option: brake systems if (Interface.CurrentOptions.GameMode == Interface.GameMode.Expert) { Game.AddMessage(Interface.GetInterfaceString("notification_notavailableexpert"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 5.0); } else { Renderer.OptionBrakeSystems = !Renderer.OptionBrakeSystems; } break; case Interface.Command.MenuActivate: // menu Game.CreateMenu(false); Game.CurrentInterface = Game.InterfaceType.Menu; break; case Interface.Command.MiscPause: // pause Game.CurrentInterface = Game.InterfaceType.Pause; break; case Interface.Command.MiscClock: // clock Renderer.OptionClock = !Renderer.OptionClock; break; case Interface.Command.MiscTimeFactor: // time factor if (Interface.CurrentOptions.GameMode == Interface.GameMode.Expert) { Game.AddMessage(Interface.GetInterfaceString("notification_notavailableexpert"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 5.0); } else { TimeFactor = TimeFactor == 1 ? 5 : 1; Game.AddMessage(TimeFactor.ToString(System.Globalization.CultureInfo.InvariantCulture) + "x", Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 5.0 * (double)TimeFactor); } break; case Interface.Command.MiscSpeed: // speed if (Interface.CurrentOptions.GameMode == Interface.GameMode.Expert) { Game.AddMessage(Interface.GetInterfaceString("notification_notavailableexpert"), Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 5.0); } else { Renderer.OptionSpeed++; if ((int)Renderer.OptionSpeed >= 3) Renderer.OptionSpeed = 0; } break; case Interface.Command.MiscFps: // fps Renderer.OptionFrameRates = !Renderer.OptionFrameRates; break; case Interface.Command.MiscFullscreen: // toggle fullscreen Screen.ToggleFullscreen(); break; case Interface.Command.MiscMute: // mute Sounds.GlobalMute = !Sounds.GlobalMute; Sounds.Update(TimeElapsed); break; } } else if (Interface.CurrentControls[i].DigitalState == Interface.DigitalControlState.Released) { // released Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.ReleasedAcknowledged; switch (Interface.CurrentControls[i].Command) { case Interface.Command.SecurityS: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.S); } break; case Interface.Command.SecurityA1: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.A1); } break; case Interface.Command.SecurityA2: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.A2); } break; case Interface.Command.SecurityB1: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.B1); } break; case Interface.Command.SecurityB2: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.B2); } break; case Interface.Command.SecurityC1: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.C1); } break; case Interface.Command.SecurityC2: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.C2); } break; case Interface.Command.SecurityD: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.D); } break; case Interface.Command.SecurityE: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.E); } break; case Interface.Command.SecurityF: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.F); } break; case Interface.Command.SecurityG: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.G); } break; case Interface.Command.SecurityH: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.H); } break; case Interface.Command.SecurityI: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.I); } break; case Interface.Command.SecurityJ: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.J); } break; case Interface.Command.SecurityK: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.K); } break; case Interface.Command.SecurityL: if (TrainManager.PlayerTrain.Plugin != null) { TrainManager.PlayerTrain.Plugin.KeyUp(OpenBveApi.Runtime.VirtualKeys.L); } break; } } } } break; } } // -------------------------------- // save camera setting private static void SaveCameraSettings() { switch (World.CameraMode) { case World.CameraViewMode.Interior: case World.CameraViewMode.InteriorLookAhead: World.CameraSavedInterior = World.CameraCurrentAlignment; break; case World.CameraViewMode.Exterior: World.CameraSavedExterior = World.CameraCurrentAlignment; break; case World.CameraViewMode.Track: case World.CameraViewMode.FlyBy: case World.CameraViewMode.FlyByZooming: World.CameraSavedTrack = World.CameraCurrentAlignment; break; } } // restore camera setting private static void RestoreCameraSettings() { switch (World.CameraMode) { case World.CameraViewMode.Interior: case World.CameraViewMode.InteriorLookAhead: World.CameraCurrentAlignment = World.CameraSavedInterior; break; case World.CameraViewMode.Exterior: World.CameraCurrentAlignment = World.CameraSavedExterior; break; case World.CameraViewMode.Track: case World.CameraViewMode.FlyBy: case World.CameraViewMode.FlyByZooming: World.CameraCurrentAlignment = World.CameraSavedTrack; TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, World.CameraSavedTrack.TrackPosition, true, false); World.CameraCurrentAlignment.TrackPosition = World.CameraTrackFollower.TrackPosition; break; } World.CameraCurrentAlignment.Zoom = 0.0; World.VerticalViewingAngle = World.OriginalVerticalViewingAngle; } // -------------------------------- // update viewport internal enum ViewPortMode { Scenery = 0, Cab = 1 } internal enum ViewPortChangeMode { ChangeToScenery = 0, ChangeToCab = 1, NoChange = 2 } internal static void UpdateViewport(ViewPortChangeMode Mode) { if (Mode == ViewPortChangeMode.ChangeToCab) { CurrentViewPortMode = ViewPortMode.Cab; } else { CurrentViewPortMode = ViewPortMode.Scenery; } Gl.glViewport(0, 0, Screen.Width, Screen.Height); World.AspectRatio = (double)Screen.Width / (double)Screen.Height; World.HorizontalViewingAngle = 2.0 * Math.Atan(Math.Tan(0.5 * World.VerticalViewingAngle) * World.AspectRatio); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glLoadIdentity(); const double invdeg = 57.295779513082320877; if (CurrentViewPortMode == ViewPortMode.Cab) { Glu.gluPerspective(World.VerticalViewingAngle * invdeg, -World.AspectRatio, 0.025, 50.0); } else { Glu.gluPerspective(World.VerticalViewingAngle * invdeg, -World.AspectRatio, 0.5, World.BackgroundImageDistance); } Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glLoadIdentity(); } // initialize motion blur internal static void InitializeMotionBlur() { if (Interface.CurrentOptions.MotionBlur != Interface.MotionBlurMode.None) { if (Renderer.PixelBufferOpenGlTextureIndex != 0) { Gl.glDeleteTextures(1, new int[] { Renderer.PixelBufferOpenGlTextureIndex }); Renderer.PixelBufferOpenGlTextureIndex = 0; } int w = Interface.CurrentOptions.NoTextureResize ? Screen.Width : Textures.RoundUpToPowerOfTwo(Screen.Width); int h = Interface.CurrentOptions.NoTextureResize ? Screen.Height : Textures.RoundUpToPowerOfTwo(Screen.Height); Renderer.PixelBuffer = new byte[4 * w * h]; int[] a = new int[1]; Gl.glGenTextures(1, a); Gl.glBindTexture(Gl.GL_TEXTURE_2D, a[0]); Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR); Gl.glTexParameterf(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR); Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, w, h, 0, Gl.GL_RGB, Gl.GL_UNSIGNED_BYTE, Renderer.PixelBuffer); Renderer.PixelBufferOpenGlTextureIndex = a[0]; Gl.glCopyTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, 0, 0, w, h, 0); } } #if DEBUG // check error private static void CheckForOpenGlError(string Location) { int error = Gl.glGetError(); if (error != Gl.GL_NO_ERROR) { string message = Location + ": "; switch (error) { case Gl.GL_INVALID_ENUM: message += "GL_INVALID_ENUM"; break; case Gl.GL_INVALID_VALUE: message += "GL_INVALID_VALUE"; break; case Gl.GL_INVALID_OPERATION: message += "GL_INVALID_OPERATION"; break; case Gl.GL_STACK_OVERFLOW: message += "GL_STACK_OVERFLOW"; break; case Gl.GL_STACK_UNDERFLOW: message += "GL_STACK_UNDERFLOW"; break; case Gl.GL_OUT_OF_MEMORY: message += "GL_OUT_OF_MEMORY"; break; case Gl.GL_TABLE_TOO_LARGE: message += "GL_TABLE_TOO_LARGE"; break; default: message += error.ToString(); break; } throw new InvalidOperationException(message); } } #endif } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/NetPlugin.cs000066400000000000000000000171551171674032100220050ustar00rootroot00000000000000using System; using OpenBveApi.Runtime; namespace OpenBve { /// Represents a .NET assembly plugin. internal class NetPlugin : PluginManager.Plugin { // sound handle internal class SoundHandleEx : SoundHandle { internal Sounds.SoundSource Source; internal SoundHandleEx(double volume, double pitch, Sounds.SoundSource source) { base.MyVolume = volume; base.MyPitch = pitch; base.MyValid = true; this.Source = source; } } // --- members --- private string PluginFolder; private string TrainFolder; private IRuntime Api; private SoundHandleEx[] SoundHandles; private int SoundHandlesCount; // --- constructors --- internal NetPlugin(string pluginFile, string trainFolder, IRuntime api, TrainManager.Train train) { base.PluginTitle = System.IO.Path.GetFileName(pluginFile); base.PluginValid = true; base.PluginMessage = null; base.Train = train; base.Panel = null; base.SupportsAI = false; base.LastTime = 0.0; base.LastReverser = -2; base.LastPowerNotch = -1; base.LastBrakeNotch = -1; base.LastAspects = new int[] { }; base.LastSection = -1; base.LastException = null; this.PluginFolder = System.IO.Path.GetDirectoryName(pluginFile); this.TrainFolder = trainFolder; this.Api = api; this.SoundHandles = new SoundHandleEx[16]; this.SoundHandlesCount = 0; } // --- functions --- internal override bool Load(VehicleSpecs specs, InitializationModes mode) { LoadProperties properties = new LoadProperties(this.PluginFolder, this.TrainFolder, this.PlaySound); bool success; #if !DEBUG try { #endif success = this.Api.Load(properties); base.SupportsAI = properties.AISupport == AISupport.Basic; #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif if (success) { base.Panel = properties.Panel ?? new int[] { }; #if !DEBUG try { #endif Api.SetVehicleSpecs(specs); Api.Initialize(mode); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif UpdatePower(); UpdateBrake(); UpdateReverser(); return true; } else if (properties.FailureReason != null) { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + base.PluginTitle + " failed to load for the following reason: " + properties.FailureReason); return false; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + base.PluginTitle + " failed to load for an unspecified reason."); return false; } } internal override void Unload() { #if !DEBUG try { #endif this.Api.Unload(); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void BeginJump(InitializationModes mode) { #if !DEBUG try { #endif this.Api.Initialize(mode); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void EndJump() { } internal override void Elapse(ElapseData data) { #if !DEBUG try { #endif this.Api.Elapse(data); for (int i = 0; i < this.SoundHandlesCount; i++) { if (this.SoundHandles[i].Stopped | this.SoundHandles[i].Source.State == Sounds.SoundSourceState.Stopped) { this.SoundHandles[i].Stop(); this.SoundHandles[i].Source.Stop(); this.SoundHandles[i] = this.SoundHandles[this.SoundHandlesCount - 1]; this.SoundHandlesCount--; i--; } else { this.SoundHandles[i].Source.Pitch = Math.Max(0.01, this.SoundHandles[i].Pitch); this.SoundHandles[i].Source.Volume = Math.Max(0.0, this.SoundHandles[i].Volume); } } #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void SetReverser(int reverser) { #if !DEBUG try { #endif this.Api.SetReverser(reverser); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void SetPower(int powerNotch) { #if !DEBUG try { #endif this.Api.SetPower(powerNotch); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void SetBrake(int brakeNotch) { #if !DEBUG try { #endif this.Api.SetBrake(brakeNotch); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void KeyDown(VirtualKeys key) { #if !DEBUG try { #endif this.Api.KeyDown(key); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void KeyUp(VirtualKeys key) { #if !DEBUG try { #endif this.Api.KeyUp(key); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void HornBlow(HornTypes type) { #if !DEBUG try { #endif this.Api.HornBlow(type); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void DoorChange(DoorStates oldState, DoorStates newState) { #if !DEBUG try { #endif this.Api.DoorChange(oldState, newState); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void SetSignal(SignalData[] signal) { #if !DEBUG try { #endif // if (this.Train == TrainManager.PlayerTrain) { // for (int i = 0; i < signal.Length; i++) { // Game.AddDebugMessage(i.ToString() + " - " + signal[i].Aspect.ToString(), 3.0); // } // } this.Api.SetSignal(signal); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void SetBeacon(BeaconData beacon) { // if (this.Train == TrainManager.PlayerTrain) { // Game.AddDebugMessage("Beacon, type=" + beacon.Type.ToString() + ", aspect=" + beacon.Signal.Aspect.ToString() + ", data=" + beacon.Optional.ToString(), 3.0); // } #if !DEBUG try { #endif this.Api.SetBeacon(beacon); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal override void PerformAI(AIData data) { #if !DEBUG try { #endif this.Api.PerformAI(data); #if !DEBUG } catch (Exception ex) { base.LastException = ex; throw; } #endif } internal SoundHandleEx PlaySound(int index, double volume, double pitch, bool looped) { if (index >= 0 && index < this.Train.Cars[this.Train.DriverCar].Sounds.Plugin.Length && this.Train.Cars[this.Train.DriverCar].Sounds.Plugin[index].Buffer != null) { Sounds.SoundBuffer buffer = this.Train.Cars[this.Train.DriverCar].Sounds.Plugin[index].Buffer; OpenBveApi.Math.Vector3 position = this.Train.Cars[this.Train.DriverCar].Sounds.Plugin[index].Position; Sounds.SoundSource source = Sounds.PlaySound(buffer, pitch, volume, position, this.Train, this.Train.DriverCar, looped); if (this.SoundHandlesCount == this.SoundHandles.Length) { Array.Resize(ref this.SoundHandles, this.SoundHandles.Length << 1); } this.SoundHandles[this.SoundHandlesCount] = new SoundHandleEx(volume, pitch, source); this.SoundHandlesCount++; return this.SoundHandles[this.SoundHandlesCount - 1]; } else { return null; } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/ObjectManager.cs000066400000000000000000002437101171674032100225770ustar00rootroot00000000000000using System; using OpenBveApi.Math; namespace OpenBve { internal static class ObjectManager { // unified objects internal abstract class UnifiedObject { } // static objects internal class StaticObject : UnifiedObject { internal World.Mesh Mesh; /// The index to the Renderer.Object array, plus 1. The value of zero represents that the object is not currently shown by the renderer. internal int RendererIndex; /// The starting track position, for static objects only. internal float StartingDistance; /// The ending track position, for static objects only. internal float EndingDistance; /// The block mod group, for static objects only. internal short GroupIndex; /// Whether the object is dynamic, i.e. not static. internal bool Dynamic; } internal static StaticObject[] Objects = new StaticObject[16]; internal static int ObjectsUsed; internal static int[] ObjectsSortedByStart = new int[] { }; internal static int[] ObjectsSortedByEnd = new int[] { }; internal static int ObjectsSortedByStartPointer = 0; internal static int ObjectsSortedByEndPointer = 0; internal static double LastUpdatedTrackPosition = 0.0; // animated objects internal class Damping { internal double NaturalFrequency; internal double NaturalTime; internal double DampingRatio; internal double NaturalDampingFrequency; internal double OriginalAngle; internal double OriginalDerivative; internal double TargetAngle; internal double CurrentAngle; internal double CurrentValue; internal double CurrentTimeDelta; internal Damping(double NaturalFrequency, double DampingRatio) { if (NaturalFrequency < 0.0) { throw new ArgumentException("NaturalFrequency must be non-negative in the constructor of the Damping class."); } else if (DampingRatio < 0.0) { throw new ArgumentException("DampingRatio must be non-negative in the constructor of the Damping class."); } else { this.NaturalFrequency = NaturalFrequency; this.NaturalTime = NaturalFrequency != 0.0 ? 1.0 / NaturalFrequency : 0.0; this.DampingRatio = DampingRatio; if (DampingRatio < 1.0) { this.NaturalDampingFrequency = NaturalFrequency * Math.Sqrt(1.0 - DampingRatio * DampingRatio); } else if (DampingRatio == 1.0) { this.NaturalDampingFrequency = NaturalFrequency; } else { this.NaturalDampingFrequency = NaturalFrequency * Math.Sqrt(DampingRatio * DampingRatio - 1.0); } this.OriginalAngle = 0.0; this.OriginalDerivative = 0.0; this.TargetAngle = 0.0; this.CurrentAngle = 0.0; this.CurrentValue = 1.0; this.CurrentTimeDelta = 0.0; } } internal Damping Clone() { return (Damping)this.MemberwiseClone(); } } internal struct AnimatedObjectState { internal Vector3 Position; internal ObjectManager.StaticObject Object; } internal class AnimatedObject { // states internal AnimatedObjectState[] States; internal FunctionScripts.FunctionScript StateFunction; internal int CurrentState; internal Vector3 TranslateXDirection; internal Vector3 TranslateYDirection; internal Vector3 TranslateZDirection; internal FunctionScripts.FunctionScript TranslateXFunction; internal FunctionScripts.FunctionScript TranslateYFunction; internal FunctionScripts.FunctionScript TranslateZFunction; internal Vector3 RotateXDirection; internal Vector3 RotateYDirection; internal Vector3 RotateZDirection; internal FunctionScripts.FunctionScript RotateXFunction; internal FunctionScripts.FunctionScript RotateYFunction; internal FunctionScripts.FunctionScript RotateZFunction; internal Damping RotateXDamping; internal Damping RotateYDamping; internal Damping RotateZDamping; internal World.Vector2D TextureShiftXDirection; internal World.Vector2D TextureShiftYDirection; internal FunctionScripts.FunctionScript TextureShiftXFunction; internal FunctionScripts.FunctionScript TextureShiftYFunction; internal bool LEDClockwiseWinding; internal double LEDInitialAngle; internal double LEDLastAngle; /// If LEDFunction is used, an array of five vectors representing the bottom-left, up-left, up-right, bottom-right and center coordinates of the LED square, or a null reference otherwise. internal Vector3[] LEDVectors; internal FunctionScripts.FunctionScript LEDFunction; internal double RefreshRate; internal double CurrentTrackZOffset; internal double SecondsSinceLastUpdate; internal int ObjectIndex; // methods internal bool IsFreeOfFunctions() { if (this.StateFunction != null) return false; if (this.TranslateXFunction != null | this.TranslateYFunction != null | this.TranslateZFunction != null) return false; if (this.RotateXFunction != null | this.RotateYFunction != null | this.RotateZFunction != null) return false; if (this.TextureShiftXFunction != null | this.TextureShiftYFunction != null) return false; if (this.LEDFunction != null) return false; return true; } internal AnimatedObject Clone() { AnimatedObject Result = new AnimatedObject(); Result.States = new AnimatedObjectState[this.States.Length]; for (int i = 0; i < this.States.Length; i++) { Result.States[i].Position = this.States[i].Position; Result.States[i].Object = CloneObject(this.States[i].Object); } 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.CurrentTrackZOffset = 0.0; Result.SecondsSinceLastUpdate = 0.0; Result.ObjectIndex = -1; return Result; } } internal class AnimatedObjectCollection : UnifiedObject { internal AnimatedObject[] Objects; } internal static void InitializeAnimatedObject(ref AnimatedObject Object, int StateIndex, bool Overlay, bool Show) { int i = Object.ObjectIndex; Renderer.HideObject(i); int t = StateIndex; if (t >= 0 && Object.States[t].Object != null) { int m = Object.States[t].Object.Mesh.Vertices.Length; ObjectManager.Objects[i].Mesh.Vertices = new World.Vertex[m]; for (int k = 0; k < m; k++) { ObjectManager.Objects[i].Mesh.Vertices[k] = Object.States[t].Object.Mesh.Vertices[k]; } m = Object.States[t].Object.Mesh.Faces.Length; ObjectManager.Objects[i].Mesh.Faces = new World.MeshFace[m]; for (int k = 0; k < m; k++) { ObjectManager.Objects[i].Mesh.Faces[k].Flags = Object.States[t].Object.Mesh.Faces[k].Flags; ObjectManager.Objects[i].Mesh.Faces[k].Material = Object.States[t].Object.Mesh.Faces[k].Material; int o = Object.States[t].Object.Mesh.Faces[k].Vertices.Length; ObjectManager.Objects[i].Mesh.Faces[k].Vertices = new World.MeshFaceVertex[o]; for (int h = 0; h < o; h++) { ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h] = Object.States[t].Object.Mesh.Faces[k].Vertices[h]; } } ObjectManager.Objects[i].Mesh.Materials = Object.States[t].Object.Mesh.Materials; } else { ObjectManager.Objects[i] = null; ObjectManager.Objects[i] = new StaticObject(); ObjectManager.Objects[i].Mesh.Faces = new World.MeshFace[] { }; ObjectManager.Objects[i].Mesh.Materials = new World.MeshMaterial[] { }; ObjectManager.Objects[i].Mesh.Vertices = new World.Vertex[] { }; } Object.CurrentState = StateIndex; if (Show) { if (Overlay) { Renderer.ShowObject(i, Renderer.ObjectType.Overlay); } else { Renderer.ShowObject(i, Renderer.ObjectType.Dynamic); } } } internal static void UpdateAnimatedObject(ref AnimatedObject Object, bool IsPartOfTrain, TrainManager.Train Train, int CarIndex, int SectionIndex, double TrackPosition, Vector3 Position, Vector3 Direction, Vector3 Up, Vector3 Side, bool Overlay, bool UpdateFunctions, bool Show, double TimeElapsed) { int s = Object.CurrentState; int i = Object.ObjectIndex; // state change if (Object.StateFunction != null & UpdateFunctions) { double sd = Object.StateFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); int si = (int)Math.Round(sd); int sn = Object.States.Length; if (si < 0 | si >= sn) si = -1; if (s != si) { InitializeAnimatedObject(ref Object, si, Overlay, Show); s = si; } } if (s == -1) return; // translation if (Object.TranslateXFunction != null) { double x; if (UpdateFunctions) { x = Object.TranslateXFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { x = Object.TranslateXFunction.LastResult; } double rx = Object.TranslateXDirection.X, ry = Object.TranslateXDirection.Y, rz = Object.TranslateXDirection.Z; World.Rotate(ref rx, ref ry, ref rz, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); Position.X += x * rx; Position.Y += x * ry; Position.Z += x * rz; } if (Object.TranslateYFunction != null) { double y; if (UpdateFunctions) { y = Object.TranslateYFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { y = Object.TranslateYFunction.LastResult; } double rx = Object.TranslateYDirection.X, ry = Object.TranslateYDirection.Y, rz = Object.TranslateYDirection.Z; World.Rotate(ref rx, ref ry, ref rz, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); Position.X += y * rx; Position.Y += y * ry; Position.Z += y * rz; } if (Object.TranslateZFunction != null) { double z; if (UpdateFunctions) { z = Object.TranslateZFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { z = Object.TranslateZFunction.LastResult; } double rx = Object.TranslateZDirection.X, ry = Object.TranslateZDirection.Y, rz = Object.TranslateZDirection.Z; World.Rotate(ref rx, ref ry, ref rz, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); Position.X += z * rx; Position.Y += z * ry; Position.Z += z * rz; } // rotation bool rotateX = Object.RotateXFunction != null; bool rotateY = Object.RotateYFunction != null; bool rotateZ = Object.RotateZFunction != null; double cosX, sinX; if (rotateX) { double a; if (UpdateFunctions) { a = Object.RotateXFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { a = Object.RotateXFunction.LastResult; } ObjectManager.UpdateDamping(ref Object.RotateXDamping, TimeElapsed, ref a); cosX = Math.Cos(a); sinX = Math.Sin(a); } else { cosX = 0.0; sinX = 0.0; } double cosY, sinY; if (rotateY) { double a; if (UpdateFunctions) { a = Object.RotateYFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { a = Object.RotateYFunction.LastResult; } ObjectManager.UpdateDamping(ref Object.RotateYDamping, TimeElapsed, ref a); cosY = Math.Cos(a); sinY = Math.Sin(a); } else { cosY = 0.0; sinY = 0.0; } double cosZ, sinZ; if (rotateZ) { double a; if (UpdateFunctions) { a = Object.RotateZFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { a = Object.RotateZFunction.LastResult; } ObjectManager.UpdateDamping(ref Object.RotateZDamping, TimeElapsed, ref a); cosZ = Math.Cos(a); sinZ = Math.Sin(a); } else { cosZ = 0.0; sinZ = 0.0; } // texture shift bool shiftx = Object.TextureShiftXFunction != null; bool shifty = Object.TextureShiftYFunction != null; if ((shiftx | shifty) & UpdateFunctions) { for (int k = 0; k < ObjectManager.Objects[i].Mesh.Vertices.Length; k++) { ObjectManager.Objects[i].Mesh.Vertices[k].TextureCoordinates = Object.States[s].Object.Mesh.Vertices[k].TextureCoordinates; } if (shiftx) { double x = Object.TextureShiftXFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); x -= Math.Floor(x); for (int k = 0; k < ObjectManager.Objects[i].Mesh.Vertices.Length; k++) { ObjectManager.Objects[i].Mesh.Vertices[k].TextureCoordinates.X += (float)(x * Object.TextureShiftXDirection.X); ObjectManager.Objects[i].Mesh.Vertices[k].TextureCoordinates.Y += (float)(x * Object.TextureShiftXDirection.Y); } } if (shifty) { double y = Object.TextureShiftYFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); y -= Math.Floor(y); for (int k = 0; k < ObjectManager.Objects[i].Mesh.Vertices.Length; k++) { ObjectManager.Objects[i].Mesh.Vertices[k].TextureCoordinates.X += (float)(y * Object.TextureShiftYDirection.X); ObjectManager.Objects[i].Mesh.Vertices[k].TextureCoordinates.Y += (float)(y * Object.TextureShiftYDirection.Y); } } } // led bool led = Object.LEDFunction != null; double ledangle; if (led) { if (UpdateFunctions) { double lastangle = Object.LEDFunction.LastResult; ledangle = Object.LEDFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { ledangle = Object.LEDFunction.LastResult; } } else { ledangle = 0.0; } // null object if (Object.States[s].Object == null) { return; } // initialize vertices for (int k = 0; k < Object.States[s].Object.Mesh.Vertices.Length; k++) { ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates = Object.States[s].Object.Mesh.Vertices[k].Coordinates; } // led if (led) { /* * Edges: Vertices: * 0 - bottom 0 - bottom-left * 1 - left 1 - top-left * 2 - top 2 - top-right * 3 - right 3 - bottom-right * 4 - center * */ int v = 1; if (Object.LEDClockwiseWinding) { /* winding is clockwise*/ if (ledangle < Object.LEDInitialAngle) { ledangle = Object.LEDInitialAngle; } if (ledangle < Object.LEDLastAngle) { double currentEdgeFloat = Math.Floor(0.636619772367582 * (ledangle + 0.785398163397449)); int currentEdge = ((int)currentEdgeFloat % 4 + 4) % 4; double lastEdgeFloat = Math.Floor(0.636619772367582 * (Object.LEDLastAngle + 0.785398163397449)); int lastEdge = ((int)lastEdgeFloat % 4 + 4) % 4; if (lastEdge < currentEdge | lastEdge == currentEdge & Math.Abs(currentEdgeFloat - lastEdgeFloat) > 2.0) { lastEdge += 4; } if (currentEdge == lastEdge) { /* current angle to last angle */ { double t = 0.5 + (0.636619772367582 * ledangle) - currentEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double cx = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].X + t * Object.LEDVectors[currentEdge].X; double cy = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Y + t * Object.LEDVectors[currentEdge].Y; double cz = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Z + t * Object.LEDVectors[currentEdge].Z; Object.States[s].Object.Mesh.Vertices[v].Coordinates = new Vector3(cx, cy, cz); v++; } { double t = 0.5 + (0.636619772367582 * Object.LEDLastAngle) - lastEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double lx = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].X + t * Object.LEDVectors[lastEdge].X; double ly = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Y + t * Object.LEDVectors[lastEdge].Y; double lz = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Z + t * Object.LEDVectors[lastEdge].Z; Object.States[s].Object.Mesh.Vertices[v].Coordinates = new Vector3(lx, ly, lz); v++; } } else { { /* current angle to square vertex */ double t = 0.5 + (0.636619772367582 * ledangle) - currentEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double cx = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].X + t * Object.LEDVectors[currentEdge].X; double cy = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Y + t * Object.LEDVectors[currentEdge].Y; double cz = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Z + t * Object.LEDVectors[currentEdge].Z; Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = new Vector3(cx, cy, cz); Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = Object.LEDVectors[currentEdge]; v += 2; } for (int j = currentEdge + 1; j < lastEdge; j++) { /* square-vertex to square-vertex */ Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = Object.LEDVectors[(j + 3) % 4]; Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = Object.LEDVectors[j % 4]; v += 2; } { /* square vertex to last angle */ double t = 0.5 + (0.636619772367582 * Object.LEDLastAngle) - lastEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double lx = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].X + t * Object.LEDVectors[lastEdge % 4].X; double ly = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Y + t * Object.LEDVectors[lastEdge % 4].Y; double lz = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Z + t * Object.LEDVectors[lastEdge % 4].Z; Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = Object.LEDVectors[(lastEdge + 3) % 4]; Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = new Vector3(lx, ly, lz); v += 2; } } } } else { /* winding is counter-clockwise*/ if (ledangle > Object.LEDInitialAngle) { ledangle = Object.LEDInitialAngle; } if (ledangle > Object.LEDLastAngle) { double currentEdgeFloat = Math.Floor(0.636619772367582 * (ledangle + 0.785398163397449)); int currentEdge = ((int)currentEdgeFloat % 4 + 4) % 4; double lastEdgeFloat = Math.Floor(0.636619772367582 * (Object.LEDLastAngle + 0.785398163397449)); int lastEdge = ((int)lastEdgeFloat % 4 + 4) % 4; if (currentEdge < lastEdge | lastEdge == currentEdge & Math.Abs(currentEdgeFloat - lastEdgeFloat) > 2.0) { currentEdge += 4; } if (currentEdge == lastEdge) { /* current angle to last angle */ { double t = 0.5 + (0.636619772367582 * Object.LEDLastAngle) - lastEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double lx = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].X + t * Object.LEDVectors[lastEdge].X; double ly = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Y + t * Object.LEDVectors[lastEdge].Y; double lz = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Z + t * Object.LEDVectors[lastEdge].Z; Object.States[s].Object.Mesh.Vertices[v].Coordinates = new Vector3(lx, ly, lz); v++; } { double t = 0.5 + (0.636619772367582 * ledangle) - currentEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = t - Math.Floor(t); t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double cx = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].X + t * Object.LEDVectors[currentEdge].X; double cy = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Y + t * Object.LEDVectors[currentEdge].Y; double cz = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Z + t * Object.LEDVectors[currentEdge].Z; Object.States[s].Object.Mesh.Vertices[v].Coordinates = new Vector3(cx, cy, cz); v++; } } else { { /* current angle to square vertex */ double t = 0.5 + (0.636619772367582 * ledangle) - currentEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double cx = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].X + t * Object.LEDVectors[currentEdge % 4].X; double cy = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Y + t * Object.LEDVectors[currentEdge % 4].Y; double cz = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Z + t * Object.LEDVectors[currentEdge % 4].Z; Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = Object.LEDVectors[(currentEdge + 3) % 4]; Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = new Vector3(cx, cy, cz); v += 2; } for (int j = currentEdge - 1; j > lastEdge; j--) { /* square-vertex to square-vertex */ Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = Object.LEDVectors[(j + 3) % 4]; Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = Object.LEDVectors[j % 4]; v += 2; } { /* square vertex to last angle */ double t = 0.5 + (0.636619772367582 * Object.LEDLastAngle) - lastEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double lx = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].X + t * Object.LEDVectors[lastEdge].X; double ly = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Y + t * Object.LEDVectors[lastEdge].Y; double lz = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Z + t * Object.LEDVectors[lastEdge].Z; Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = new Vector3(lx, ly, lz); Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = Object.LEDVectors[lastEdge % 4]; v += 2; } } } } for (int j = v; v < 11; v++) { Object.States[s].Object.Mesh.Vertices[j].Coordinates = Object.LEDVectors[4]; } } // update vertices for (int k = 0; k < Object.States[s].Object.Mesh.Vertices.Length; k++) { // rotate if (rotateX) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z, Object.RotateXDirection.X, Object.RotateXDirection.Y, Object.RotateXDirection.Z, cosX, sinX); } if (rotateY) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z, Object.RotateYDirection.X, Object.RotateYDirection.Y, Object.RotateYDirection.Z, cosY, sinY); } if (rotateZ) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z, Object.RotateZDirection.X, Object.RotateZDirection.Y, Object.RotateZDirection.Z, cosZ, sinZ); } // translate if (Overlay & World.CameraRestriction != World.CameraRestrictionMode.NotAvailable) { ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X += Object.States[s].Position.X - Position.X; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y += Object.States[s].Position.Y - Position.Y; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z += Object.States[s].Position.Z - Position.Z; World.Rotate(ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z, World.AbsoluteCameraDirection.X, World.AbsoluteCameraDirection.Y, World.AbsoluteCameraDirection.Z, World.AbsoluteCameraUp.X, World.AbsoluteCameraUp.Y, World.AbsoluteCameraUp.Z, World.AbsoluteCameraSide.X, World.AbsoluteCameraSide.Y, World.AbsoluteCameraSide.Z); double dx = -Math.Tan(World.CameraCurrentAlignment.Yaw) - World.CameraCurrentAlignment.Position.X; double dy = -Math.Tan(World.CameraCurrentAlignment.Pitch) - World.CameraCurrentAlignment.Position.Y; double dz = -World.CameraCurrentAlignment.Position.Z; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X += World.AbsoluteCameraPosition.X + dx * World.AbsoluteCameraSide.X + dy * World.AbsoluteCameraUp.X + dz * World.AbsoluteCameraDirection.X; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y += World.AbsoluteCameraPosition.Y + dx * World.AbsoluteCameraSide.Y + dy * World.AbsoluteCameraUp.Y + dz * World.AbsoluteCameraDirection.Y; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z += World.AbsoluteCameraPosition.Z + dx * World.AbsoluteCameraSide.Z + dy * World.AbsoluteCameraUp.Z + dz * World.AbsoluteCameraDirection.Z; } else { ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X += Object.States[s].Position.X; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y += Object.States[s].Position.Y; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z += Object.States[s].Position.Z; World.Rotate(ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X += Position.X; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y += Position.Y; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z += Position.Z; } } // update normals for (int k = 0; k < Object.States[s].Object.Mesh.Faces.Length; k++) { for (int h = 0; h < Object.States[s].Object.Mesh.Faces[k].Vertices.Length; h++) { ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal = Object.States[s].Object.Mesh.Faces[k].Vertices[h].Normal; } for (int h = 0; h < Object.States[s].Object.Mesh.Faces[k].Vertices.Length; h++) { if (!Object.States[s].Object.Mesh.Faces[k].Vertices[h].Normal.IsZero()) { if (rotateX) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.X, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Y, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Z, Object.RotateXDirection.X, Object.RotateXDirection.Y, Object.RotateXDirection.Z, cosX, sinX); } if (rotateY) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.X, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Y, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Z, Object.RotateYDirection.X, Object.RotateYDirection.Y, Object.RotateYDirection.Z, cosY, sinY); } if (rotateZ) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.X, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Y, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Z, Object.RotateZDirection.X, Object.RotateZDirection.Y, Object.RotateZDirection.Z, cosZ, sinZ); } World.Rotate(ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.X, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Y, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Z, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); } } // visibility changed if (Show) { if (Overlay) { Renderer.ShowObject(i, Renderer.ObjectType.Overlay); } else { Renderer.ShowObject(i, Renderer.ObjectType.Dynamic); } } else { Renderer.HideObject(i); } } } // update damping internal static void UpdateDamping(ref Damping Damping, double TimeElapsed, ref double Angle) { if (TimeElapsed < 0.0) { TimeElapsed = 0.0; } else if (TimeElapsed > 1.0) { TimeElapsed = 1.0; } if (Damping != null) { if (Damping.CurrentTimeDelta > Damping.NaturalTime) { // update double newDerivative; if (Damping.NaturalFrequency == 0.0) { newDerivative = 0.0; } else if (Damping.DampingRatio == 0.0) { newDerivative = Damping.OriginalDerivative * Math.Cos(Damping.NaturalFrequency * Damping.CurrentTimeDelta) - Damping.NaturalFrequency * Math.Sin(Damping.NaturalFrequency * Damping.CurrentTimeDelta); } else if (Damping.DampingRatio < 1.0) { newDerivative = Math.Exp(-Damping.DampingRatio * Damping.NaturalFrequency * Damping.CurrentTimeDelta) * (Damping.NaturalDampingFrequency * Damping.OriginalDerivative * Math.Cos(Damping.NaturalDampingFrequency * Damping.CurrentTimeDelta) - (Damping.NaturalDampingFrequency * Damping.NaturalDampingFrequency + Damping.DampingRatio * Damping.NaturalFrequency * (Damping.DampingRatio * Damping.NaturalFrequency + Damping.OriginalDerivative)) * Math.Sin(Damping.NaturalDampingFrequency * Damping.CurrentTimeDelta)) / Damping.NaturalDampingFrequency; } else if (Damping.DampingRatio == 1.0) { newDerivative = Math.Exp(-Damping.NaturalFrequency * Damping.CurrentTimeDelta) * (Damping.OriginalDerivative - Damping.NaturalFrequency * (Damping.NaturalFrequency + Damping.OriginalDerivative) * Damping.CurrentTimeDelta); } else { newDerivative = Math.Exp(-Damping.DampingRatio * Damping.NaturalFrequency * Damping.CurrentTimeDelta) * (Damping.NaturalDampingFrequency * Damping.OriginalDerivative * Math.Cosh(Damping.NaturalDampingFrequency * Damping.CurrentTimeDelta) + (Damping.NaturalDampingFrequency * Damping.NaturalDampingFrequency - Damping.DampingRatio * Damping.NaturalFrequency * (Damping.DampingRatio * Damping.NaturalFrequency + Damping.OriginalDerivative)) * Math.Sinh(Damping.NaturalDampingFrequency * Damping.CurrentTimeDelta)) / Damping.NaturalDampingFrequency; } double a = Damping.TargetAngle - Damping.OriginalAngle; Damping.OriginalAngle = Damping.CurrentAngle; Damping.TargetAngle = Angle; double b = Damping.TargetAngle - Damping.OriginalAngle; double r = b == 0.0 ? 1.0 : a / b; Damping.OriginalDerivative = newDerivative * r; if (Damping.NaturalTime > 0.0) { Damping.CurrentTimeDelta = Damping.CurrentTimeDelta % Damping.NaturalTime; } } { // perform double newValue; if (Damping.NaturalFrequency == 0.0) { newValue = 1.0; } else if (Damping.DampingRatio == 0.0) { newValue = Math.Cos(Damping.NaturalFrequency * Damping.CurrentTimeDelta) + Damping.OriginalDerivative * Math.Sin(Damping.NaturalFrequency * Damping.CurrentTimeDelta) / Damping.NaturalFrequency; } else if (Damping.DampingRatio < 1.0) { double n = (Damping.OriginalDerivative + Damping.NaturalFrequency * Damping.DampingRatio) / Damping.NaturalDampingFrequency; newValue = Math.Exp(-Damping.DampingRatio * Damping.NaturalFrequency * Damping.CurrentTimeDelta) * (Math.Cos(Damping.NaturalDampingFrequency * Damping.CurrentTimeDelta) + n * Math.Sin(Damping.NaturalDampingFrequency * Damping.CurrentTimeDelta)); } else if (Damping.DampingRatio == 1.0) { newValue = Math.Exp(-Damping.NaturalFrequency * Damping.CurrentTimeDelta) * (1.0 + (Damping.OriginalDerivative + Damping.NaturalFrequency) * Damping.CurrentTimeDelta); } else { double n = (Damping.OriginalDerivative + Damping.NaturalFrequency * Damping.DampingRatio) / Damping.NaturalDampingFrequency; newValue = Math.Exp(-Damping.DampingRatio * Damping.NaturalFrequency * Damping.CurrentTimeDelta) * (Math.Cosh(Damping.NaturalDampingFrequency * Damping.CurrentTimeDelta) + n * Math.Sinh(Damping.NaturalDampingFrequency * Damping.CurrentTimeDelta)); } Damping.CurrentValue = newValue; Damping.CurrentAngle = Damping.TargetAngle * (1.0 - newValue) + Damping.OriginalAngle * newValue; Damping.CurrentTimeDelta += TimeElapsed; Angle = Damping.CurrentAngle; } } } // animated world object internal class AnimatedWorldObject { internal Vector3 Position; internal double TrackPosition; internal Vector3 Direction; internal Vector3 Up; internal Vector3 Side; internal AnimatedObject Object; internal int SectionIndex; internal double Radius; internal bool Visible; } internal static AnimatedWorldObject[] AnimatedWorldObjects = new AnimatedWorldObject[4]; internal static int AnimatedWorldObjectsUsed = 0; internal static void CreateAnimatedWorldObjects(AnimatedObject[] Prototypes, Vector3 Position, World.Transformation BaseTransformation, World.Transformation AuxTransformation, int SectionIndex, bool AccurateObjectDisposal, double StartingDistance, double EndingDistance, double BlockLength, double TrackPosition, double Brightness, bool DuplicateMaterials) { bool[] free = new bool[Prototypes.Length]; bool anyfree = false; for (int i = 0; i < Prototypes.Length; i++) { free[i] = Prototypes[i].IsFreeOfFunctions(); if (free[i]) anyfree = true; } if (anyfree) { for (int i = 0; i < Prototypes.Length; i++) { if (Prototypes[i].States.Length != 0) { if (free[i]) { Vector3 p = Position; World.Transformation t = new OpenBve.World.Transformation(BaseTransformation, AuxTransformation); Vector3 s = t.X; Vector3 u = t.Y; Vector3 d = t.Z; p.X += Prototypes[i].States[0].Position.X * s.X + Prototypes[i].States[0].Position.Y * u.X + Prototypes[i].States[0].Position.Z * d.X; p.Y += Prototypes[i].States[0].Position.X * s.Y + Prototypes[i].States[0].Position.Y * u.Y + Prototypes[i].States[0].Position.Z * d.Y; p.Z += Prototypes[i].States[0].Position.X * s.Z + Prototypes[i].States[0].Position.Y * u.Z + Prototypes[i].States[0].Position.Z * d.Z; double zOffset = Prototypes[i].States[0].Position.Z; CreateStaticObject(Prototypes[i].States[0].Object, p, BaseTransformation, AuxTransformation, AccurateObjectDisposal, zOffset, StartingDistance, EndingDistance, BlockLength, TrackPosition, Brightness, DuplicateMaterials); } else { CreateAnimatedWorldObject(Prototypes[i], Position, BaseTransformation, AuxTransformation, SectionIndex, TrackPosition, Brightness); } } } } else { for (int i = 0; i < Prototypes.Length; i++) { if (Prototypes[i].States.Length != 0) { CreateAnimatedWorldObject(Prototypes[i], Position, BaseTransformation, AuxTransformation, SectionIndex, TrackPosition, Brightness); } } } } internal static int CreateAnimatedWorldObject(AnimatedObject Prototype, Vector3 Position, World.Transformation BaseTransformation, World.Transformation AuxTransformation, int SectionIndex, double TrackPosition, double Brightness) { int a = AnimatedWorldObjectsUsed; if (a >= AnimatedWorldObjects.Length) { Array.Resize(ref AnimatedWorldObjects, AnimatedWorldObjects.Length << 1); } World.Transformation FinalTransformation = new World.Transformation(BaseTransformation, AuxTransformation); AnimatedWorldObjects[a] = new AnimatedWorldObject(); AnimatedWorldObjects[a].Position = Position; AnimatedWorldObjects[a].Direction = FinalTransformation.Z; AnimatedWorldObjects[a].Up = FinalTransformation.Y; AnimatedWorldObjects[a].Side = FinalTransformation.X; AnimatedWorldObjects[a].Object = Prototype.Clone(); AnimatedWorldObjects[a].Object.ObjectIndex = CreateDynamicObject(); AnimatedWorldObjects[a].SectionIndex = SectionIndex; AnimatedWorldObjects[a].TrackPosition = TrackPosition; for (int i = 0; i < AnimatedWorldObjects[a].Object.States.Length; i++) { if (AnimatedWorldObjects[a].Object.States[i].Object == null) { AnimatedWorldObjects[a].Object.States[i].Object = new StaticObject(); AnimatedWorldObjects[a].Object.States[i].Object.Mesh.Faces = new World.MeshFace[] { }; AnimatedWorldObjects[a].Object.States[i].Object.Mesh.Materials = new World.MeshMaterial[] { }; AnimatedWorldObjects[a].Object.States[i].Object.Mesh.Vertices = new World.Vertex[] { }; AnimatedWorldObjects[a].Object.States[i].Object.RendererIndex = -1; } } double r = 0.0; for (int i = 0; i < AnimatedWorldObjects[a].Object.States.Length; i++) { for (int j = 0; j < AnimatedWorldObjects[a].Object.States[i].Object.Mesh.Materials.Length; j++) { AnimatedWorldObjects[a].Object.States[i].Object.Mesh.Materials[j].Color.R = (byte)Math.Round((double)Prototype.States[i].Object.Mesh.Materials[j].Color.R * Brightness); AnimatedWorldObjects[a].Object.States[i].Object.Mesh.Materials[j].Color.G = (byte)Math.Round((double)Prototype.States[i].Object.Mesh.Materials[j].Color.G * Brightness); AnimatedWorldObjects[a].Object.States[i].Object.Mesh.Materials[j].Color.B = (byte)Math.Round((double)Prototype.States[i].Object.Mesh.Materials[j].Color.B * Brightness); } for (int j = 0; j < AnimatedWorldObjects[a].Object.States[i].Object.Mesh.Vertices.Length; j++) { double x = Prototype.States[i].Object.Mesh.Vertices[j].Coordinates.X; double y = Prototype.States[i].Object.Mesh.Vertices[j].Coordinates.Y; double z = Prototype.States[i].Object.Mesh.Vertices[j].Coordinates.Z; double t = x * x + y * y + z * z; if (t > r) r = t; } } AnimatedWorldObjects[a].Radius = Math.Sqrt(r); AnimatedWorldObjects[a].Visible = false; InitializeAnimatedObject(ref AnimatedWorldObjects[a].Object, 0, false, false); AnimatedWorldObjectsUsed++; return a; } internal static void UpdateAnimatedWorldObjects(double TimeElapsed, bool ForceUpdate) { for (int i = 0; i < AnimatedWorldObjectsUsed; i++) { const double extraRadius = 10.0; double z = AnimatedWorldObjects[i].Object.TranslateZFunction == null ? 0.0 : AnimatedWorldObjects[i].Object.TranslateZFunction.LastResult; double pa = AnimatedWorldObjects[i].TrackPosition + z - AnimatedWorldObjects[i].Radius - extraRadius; double pb = AnimatedWorldObjects[i].TrackPosition + z + AnimatedWorldObjects[i].Radius + extraRadius; double ta = World.CameraTrackFollower.TrackPosition - World.BackgroundImageDistance - World.ExtraViewingDistance; double tb = World.CameraTrackFollower.TrackPosition + World.BackgroundImageDistance + World.ExtraViewingDistance; bool visible = pb >= ta & pa <= tb; if (visible | ForceUpdate) { if (AnimatedWorldObjects[i].Object.SecondsSinceLastUpdate >= AnimatedWorldObjects[i].Object.RefreshRate | ForceUpdate) { double timeDelta = AnimatedWorldObjects[i].Object.SecondsSinceLastUpdate + TimeElapsed; AnimatedWorldObjects[i].Object.SecondsSinceLastUpdate = 0.0; TrainManager.Train train = null; double trainDistance = double.MaxValue; for (int j = 0; j < TrainManager.Trains.Length; j++) { if (TrainManager.Trains[j].State == TrainManager.TrainState.Available) { double distance; if (TrainManager.Trains[j].Cars[0].FrontAxle.Follower.TrackPosition < AnimatedWorldObjects[i].TrackPosition) { distance = AnimatedWorldObjects[i].TrackPosition - TrainManager.Trains[j].Cars[0].FrontAxle.Follower.TrackPosition; } else if (TrainManager.Trains[j].Cars[TrainManager.Trains[j].Cars.Length - 1].RearAxle.Follower.TrackPosition > AnimatedWorldObjects[i].TrackPosition) { distance = TrainManager.Trains[j].Cars[TrainManager.Trains[j].Cars.Length - 1].RearAxle.Follower.TrackPosition - AnimatedWorldObjects[i].TrackPosition; } else { distance = 0; } if (distance < trainDistance) { train = TrainManager.Trains[j]; trainDistance = distance; } } } UpdateAnimatedObject(ref AnimatedWorldObjects[i].Object, false, train, train == null ? 0 : train.DriverCar, AnimatedWorldObjects[i].SectionIndex, AnimatedWorldObjects[i].TrackPosition, AnimatedWorldObjects[i].Position, AnimatedWorldObjects[i].Direction, AnimatedWorldObjects[i].Up, AnimatedWorldObjects[i].Side, false, true, true, timeDelta); } else { AnimatedWorldObjects[i].Object.SecondsSinceLastUpdate += TimeElapsed; } if (!AnimatedWorldObjects[i].Visible) { Renderer.ShowObject(AnimatedWorldObjects[i].Object.ObjectIndex, Renderer.ObjectType.Dynamic); AnimatedWorldObjects[i].Visible = true; } } else { AnimatedWorldObjects[i].Object.SecondsSinceLastUpdate += TimeElapsed; if (AnimatedWorldObjects[i].Visible) { Renderer.HideObject(AnimatedWorldObjects[i].Object.ObjectIndex); AnimatedWorldObjects[i].Visible = false; } } } } // load object internal enum ObjectLoadMode { Normal, DontAllowUnloadOfTextures } internal static UnifiedObject LoadObject(string FileName, System.Text.Encoding Encoding, ObjectLoadMode LoadMode, bool PreserveVertices, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { #if !DEBUG try { #endif if (!System.IO.Path.HasExtension(FileName)) { while (true) { string f; f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".x"); if (System.IO.File.Exists(f)) { FileName = f; break; } f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".csv"); if (System.IO.File.Exists(f)) { FileName = f; break; } f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".b3d"); if (System.IO.File.Exists(f)) { FileName = f; break; } break; } } UnifiedObject Result; switch (System.IO.Path.GetExtension(FileName).ToLowerInvariant()) { case ".csv": case ".b3d": Result = CsvB3dObjectParser.ReadObject(FileName, Encoding, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); break; case ".x": Result = XObjectParser.ReadObject(FileName, Encoding, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); break; case ".animated": Result = AnimatedObjectParser.ReadObject(FileName, Encoding, LoadMode); break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The file extension is not supported: " + FileName); return null; } OptimizeObject(Result, PreserveVertices); return Result; #if !DEBUG } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, true, "An unexpected error occured (" + ex.Message + ") while attempting to load the file " + FileName); return null; } #endif } internal static StaticObject LoadStaticObject(string FileName, System.Text.Encoding Encoding, ObjectLoadMode LoadMode, bool PreserveVertices, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { #if !DEBUG try { #endif if (!System.IO.Path.HasExtension(FileName)) { while (true) { string f; f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".x"); if (System.IO.File.Exists(f)) { FileName = f; break; } f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".csv"); if (System.IO.File.Exists(f)) { FileName = f; break; } f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), System.IO.Path.GetFileName(FileName) + ".b3d"); if (System.IO.File.Exists(f)) { FileName = f; break; } break; } } StaticObject Result; switch (System.IO.Path.GetExtension(FileName).ToLowerInvariant()) { case ".csv": case ".b3d": Result = CsvB3dObjectParser.ReadObject(FileName, Encoding, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); break; case ".x": Result = XObjectParser.ReadObject(FileName, Encoding, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); break; case ".animated": Interface.AddMessage(Interface.MessageType.Error, false, "Tried to load an animated object even though only static objects are allowed: " + FileName); return null; default: Interface.AddMessage(Interface.MessageType.Error, false, "The file extension is not supported: " + FileName); return null; } OptimizeObject(Result, PreserveVertices); return Result; #if !DEBUG } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, true, "An unexpected error occured (" + ex.Message + ") while attempting to load the file " + FileName); return null; } #endif } // optimize object internal static void OptimizeObject(UnifiedObject Prototype, bool PreserveVertices) { if (Prototype is StaticObject) { StaticObject s = (StaticObject)Prototype; OptimizeObject(s, PreserveVertices); } else if (Prototype is AnimatedObjectCollection) { AnimatedObjectCollection a = (AnimatedObjectCollection)Prototype; for (int i = 0; i < a.Objects.Length; i++) { for (int j = 0; j < a.Objects[i].States.Length; j++) { OptimizeObject(a.Objects[i].States[j].Object, PreserveVertices); } } } } internal static void OptimizeObject(StaticObject Prototype, bool PreserveVertices) { if (Prototype == null) return; int v = Prototype.Mesh.Vertices.Length; int m = Prototype.Mesh.Materials.Length; int f = Prototype.Mesh.Faces.Length; if (f >= Interface.CurrentOptions.ObjectOptimizationBasicThreshold) return; // eliminate invalid faces and reduce incomplete faces for (int i = 0; i < f; i++) { int type = Prototype.Mesh.Faces[i].Flags & World.MeshFace.FaceTypeMask; bool keep; if (type == World.MeshFace.FaceTypeTriangles) { keep = Prototype.Mesh.Faces[i].Vertices.Length >= 3; if (keep) { int n = (Prototype.Mesh.Faces[i].Vertices.Length / 3) * 3; if (Prototype.Mesh.Faces[i].Vertices.Length != n) { Array.Resize(ref Prototype.Mesh.Faces[i].Vertices, n); } } } else if (type == World.MeshFace.FaceTypeQuads) { keep = Prototype.Mesh.Faces[i].Vertices.Length >= 4; if (keep) { int n = Prototype.Mesh.Faces[i].Vertices.Length & ~3; if (Prototype.Mesh.Faces[i].Vertices.Length != n) { Array.Resize(ref Prototype.Mesh.Faces[i].Vertices, n); } } } else if (type == World.MeshFace.FaceTypeQuadStrip) { keep = Prototype.Mesh.Faces[i].Vertices.Length >= 4; if (keep) { int n = Prototype.Mesh.Faces[i].Vertices.Length & ~1; if (Prototype.Mesh.Faces[i].Vertices.Length != n) { Array.Resize(ref Prototype.Mesh.Faces[i].Vertices, n); } } } else { keep = Prototype.Mesh.Faces[i].Vertices.Length >= 3; } if (!keep) { for (int j = i; j < f - 1; j++) { Prototype.Mesh.Faces[j] = Prototype.Mesh.Faces[j + 1]; } f--; i--; } } // eliminate unused vertices if (!PreserveVertices) { for (int i = 0; i < v; i++) { bool keep = false; for (int j = 0; j < f; j++) { for (int k = 0; k < Prototype.Mesh.Faces[j].Vertices.Length; k++) { if (Prototype.Mesh.Faces[j].Vertices[k].Index == i) { keep = true; break; } } if (keep) { break; } } if (!keep) { for (int j = 0; j < f; j++) { for (int k = 0; k < Prototype.Mesh.Faces[j].Vertices.Length; k++) { if (Prototype.Mesh.Faces[j].Vertices[k].Index > i) { Prototype.Mesh.Faces[j].Vertices[k].Index--; } } } for (int j = i; j < v - 1; j++) { Prototype.Mesh.Vertices[j] = Prototype.Mesh.Vertices[j + 1]; } v--; i--; } } } // eliminate duplicate vertices if (!PreserveVertices) { for (int i = 0; i < v - 1; i++) { for (int j = i + 1; j < v; j++) { if (Prototype.Mesh.Vertices[i] == Prototype.Mesh.Vertices[j]) { for (int k = 0; k < f; k++) { for (int h = 0; h < Prototype.Mesh.Faces[k].Vertices.Length; h++) { if (Prototype.Mesh.Faces[k].Vertices[h].Index == j) { Prototype.Mesh.Faces[k].Vertices[h].Index = (ushort)i; } else if (Prototype.Mesh.Faces[k].Vertices[h].Index > j) { Prototype.Mesh.Faces[k].Vertices[h].Index--; } } } for (int k = j; k < v - 1; k++) { Prototype.Mesh.Vertices[k] = Prototype.Mesh.Vertices[k + 1]; } v--; j--; } } } } // eliminate unused materials bool[] materialUsed = new bool[m]; for (int i = 0; i < f; i++) { materialUsed[Prototype.Mesh.Faces[i].Material] = true; } for (int i = 0; i < m; i++) { if (!materialUsed[i]) { for (int j = 0; j < f; j++) { if (Prototype.Mesh.Faces[j].Material > i) { Prototype.Mesh.Faces[j].Material--; } } for (int j = i; j < m - 1; j++) { Prototype.Mesh.Materials[j] = Prototype.Mesh.Materials[j + 1]; materialUsed[j] = materialUsed[j + 1]; } m--; i--; } } // eliminate duplicate materials for (int i = 0; i < m - 1; i++) { for (int j = i + 1; j < m; j++) { if (Prototype.Mesh.Materials[i] == Prototype.Mesh.Materials[j]) { for (int k = 0; k < f; k++) { if (Prototype.Mesh.Faces[k].Material == j) { Prototype.Mesh.Faces[k].Material = (ushort)i; } else if (Prototype.Mesh.Faces[k].Material > j) { Prototype.Mesh.Faces[k].Material--; } } for (int k = j; k < m - 1; k++) { Prototype.Mesh.Materials[k] = Prototype.Mesh.Materials[k + 1]; } m--; j--; } } } // structure optimization if (!PreserveVertices & f < Interface.CurrentOptions.ObjectOptimizationFullThreshold) { // create TRIANGLES and QUADS from POLYGON for (int i = 0; i < f; i++) { int type = Prototype.Mesh.Faces[i].Flags & World.MeshFace.FaceTypeMask; if (type == World.MeshFace.FaceTypePolygon) { if (Prototype.Mesh.Faces[i].Vertices.Length == 3) { unchecked { Prototype.Mesh.Faces[i].Flags &= (byte)~World.MeshFace.FaceTypeMask; Prototype.Mesh.Faces[i].Flags |= World.MeshFace.FaceTypeTriangles; } } else if (Prototype.Mesh.Faces[i].Vertices.Length == 4) { unchecked { Prototype.Mesh.Faces[i].Flags &= (byte)~World.MeshFace.FaceTypeMask; Prototype.Mesh.Faces[i].Flags |= World.MeshFace.FaceTypeQuads; } } } } // decomposit TRIANGLES and QUADS for (int i = 0; i < f; i++) { int type = Prototype.Mesh.Faces[i].Flags & World.MeshFace.FaceTypeMask; if (type == World.MeshFace.FaceTypeTriangles) { if (Prototype.Mesh.Faces[i].Vertices.Length > 3) { int n = (Prototype.Mesh.Faces[i].Vertices.Length - 3) / 3; while (f + n > Prototype.Mesh.Faces.Length) { Array.Resize(ref Prototype.Mesh.Faces, Prototype.Mesh.Faces.Length << 1); } for (int j = 0; j < n; j++) { Prototype.Mesh.Faces[f + j].Vertices = new World.MeshFaceVertex[3]; for (int k = 0; k < 3; k++) { Prototype.Mesh.Faces[f + j].Vertices[k] = Prototype.Mesh.Faces[i].Vertices[3 + 3 * j + k]; } Prototype.Mesh.Faces[f + j].Material = Prototype.Mesh.Faces[i].Material; Prototype.Mesh.Faces[f + j].Flags = Prototype.Mesh.Faces[i].Flags; unchecked { Prototype.Mesh.Faces[i].Flags &= (byte)~World.MeshFace.FaceTypeMask; Prototype.Mesh.Faces[i].Flags |= World.MeshFace.FaceTypeTriangles; } } Array.Resize(ref Prototype.Mesh.Faces[i].Vertices, 3); f += n; } } else if (type == World.MeshFace.FaceTypeQuads) { if (Prototype.Mesh.Faces[i].Vertices.Length > 4) { int n = (Prototype.Mesh.Faces[i].Vertices.Length - 4) >> 2; while (f + n > Prototype.Mesh.Faces.Length) { Array.Resize(ref Prototype.Mesh.Faces, Prototype.Mesh.Faces.Length << 1); } for (int j = 0; j < n; j++) { Prototype.Mesh.Faces[f + j].Vertices = new World.MeshFaceVertex[4]; for (int k = 0; k < 4; k++) { Prototype.Mesh.Faces[f + j].Vertices[k] = Prototype.Mesh.Faces[i].Vertices[4 + 4 * j + k]; } Prototype.Mesh.Faces[f + j].Material = Prototype.Mesh.Faces[i].Material; Prototype.Mesh.Faces[f + j].Flags = Prototype.Mesh.Faces[i].Flags; unchecked { Prototype.Mesh.Faces[i].Flags &= (byte)~World.MeshFace.FaceTypeMask; Prototype.Mesh.Faces[i].Flags |= World.MeshFace.FaceTypeQuads; } } Array.Resize(ref Prototype.Mesh.Faces[i].Vertices, 4); f += n; } } } // optimize for TRIANGLE_STRIP int index = -1; while (true) { // add TRIANGLES to TRIANGLE_STRIP for (int i = 0; i < f; i++) { if (index == i | index == -1) { int type = Prototype.Mesh.Faces[i].Flags & World.MeshFace.FaceTypeMask; if (type == World.MeshFace.FaceTypeTriangleStrip) { int face = Prototype.Mesh.Faces[i].Flags & World.MeshFace.Face2Mask; for (int j = 0; j < f; j++) { int type2 = Prototype.Mesh.Faces[j].Flags & World.MeshFace.FaceTypeMask; int face2 = Prototype.Mesh.Faces[j].Flags & World.MeshFace.Face2Mask; if (type2 == World.MeshFace.FaceTypeTriangles & face == face2) { if (Prototype.Mesh.Faces[i].Material == Prototype.Mesh.Faces[j].Material) { bool keep = true; for (int k = 0; k < 3; k++) { int l = (k + 1) % 3; int n = Prototype.Mesh.Faces[i].Vertices.Length; if (Prototype.Mesh.Faces[i].Vertices[0] == Prototype.Mesh.Faces[j].Vertices[k] & Prototype.Mesh.Faces[i].Vertices[1] == Prototype.Mesh.Faces[j].Vertices[l]) { Array.Resize(ref Prototype.Mesh.Faces[i].Vertices, n + 1); for (int h = n; h >= 1; h--) { Prototype.Mesh.Faces[i].Vertices[h] = Prototype.Mesh.Faces[i].Vertices[h - 1]; } Prototype.Mesh.Faces[i].Vertices[0] = Prototype.Mesh.Faces[j].Vertices[(k + 2) % 3]; keep = false; } else if (Prototype.Mesh.Faces[i].Vertices[n - 1] == Prototype.Mesh.Faces[j].Vertices[l] & Prototype.Mesh.Faces[i].Vertices[n - 2] == Prototype.Mesh.Faces[j].Vertices[k]) { Array.Resize(ref Prototype.Mesh.Faces[i].Vertices, n + 1); Prototype.Mesh.Faces[i].Vertices[n] = Prototype.Mesh.Faces[j].Vertices[(k + 2) % 3]; keep = false; } if (!keep) { break; } } if (!keep) { for (int k = j; k < f - 1; k++) { Prototype.Mesh.Faces[k] = Prototype.Mesh.Faces[k + 1]; } if (j < i) { i--; } f--; j--; } } } } } } } // join TRIANGLES into new TRIANGLE_STRIP index = -1; for (int i = 0; i < f - 1; i++) { int type = Prototype.Mesh.Faces[i].Flags & World.MeshFace.FaceTypeMask; if (type == World.MeshFace.FaceTypeTriangles) { int face = Prototype.Mesh.Faces[i].Flags & World.MeshFace.Face2Mask; for (int j = i + 1; j < f; j++) { int type2 = Prototype.Mesh.Faces[j].Flags & World.MeshFace.FaceTypeMask; int face2 = Prototype.Mesh.Faces[j].Flags & World.MeshFace.Face2Mask; if (type2 == World.MeshFace.FaceTypeTriangles & face == face2) { if (Prototype.Mesh.Faces[i].Material == Prototype.Mesh.Faces[j].Material) { for (int ik = 0; ik < 3; ik++) { int il = (ik + 1) % 3; for (int jk = 0; jk < 3; jk++) { int jl = (jk + 1) % 3; if (Prototype.Mesh.Faces[i].Vertices[ik] == Prototype.Mesh.Faces[j].Vertices[jl] & Prototype.Mesh.Faces[i].Vertices[il] == Prototype.Mesh.Faces[j].Vertices[jk]) { unchecked { Prototype.Mesh.Faces[i].Flags &= (byte)~World.MeshFace.FaceTypeMask; Prototype.Mesh.Faces[i].Flags |= World.MeshFace.FaceTypeTriangleStrip; } Prototype.Mesh.Faces[i].Vertices = new World.MeshFaceVertex[] { Prototype.Mesh.Faces[i].Vertices[(ik + 2) % 3], Prototype.Mesh.Faces[i].Vertices[ik], Prototype.Mesh.Faces[i].Vertices[il], Prototype.Mesh.Faces[j].Vertices[(jk + 2) % 3] }; for (int k = j; k < f - 1; k++) { Prototype.Mesh.Faces[k] = Prototype.Mesh.Faces[k + 1]; } f--; index = i; break; } } if (index >= 0) break; } } } if (index >= 0) break; } } } if (index == -1) break; } // optimize for QUAD_STRIP index = -1; while (true) { // add QUADS to QUAD_STRIP for (int i = 0; i < f; i++) { if (index == i | index == -1) { int type = Prototype.Mesh.Faces[i].Flags & World.MeshFace.FaceTypeMask; if (type == World.MeshFace.FaceTypeQuadStrip) { int face = Prototype.Mesh.Faces[i].Flags & World.MeshFace.Face2Mask; for (int j = 0; j < f; j++) { int type2 = Prototype.Mesh.Faces[j].Flags & World.MeshFace.FaceTypeMask; int face2 = Prototype.Mesh.Faces[j].Flags & World.MeshFace.Face2Mask; if (type2 == World.MeshFace.FaceTypeQuads & face == face2) { if (Prototype.Mesh.Faces[i].Material == Prototype.Mesh.Faces[j].Material) { bool keep = true; for (int k = 0; k < 4; k++) { int l = (k + 1) & 3; int n = Prototype.Mesh.Faces[i].Vertices.Length; if (Prototype.Mesh.Faces[i].Vertices[0] == Prototype.Mesh.Faces[j].Vertices[l] & Prototype.Mesh.Faces[i].Vertices[1] == Prototype.Mesh.Faces[j].Vertices[k]) { Array.Resize(ref Prototype.Mesh.Faces[i].Vertices, n + 2); for (int h = n + 1; h >= 2; h--) { Prototype.Mesh.Faces[i].Vertices[h] = Prototype.Mesh.Faces[i].Vertices[h - 2]; } Prototype.Mesh.Faces[i].Vertices[0] = Prototype.Mesh.Faces[j].Vertices[(k + 2) & 3]; Prototype.Mesh.Faces[i].Vertices[1] = Prototype.Mesh.Faces[j].Vertices[(k + 3) & 3]; keep = false; } else if (Prototype.Mesh.Faces[i].Vertices[n - 1] == Prototype.Mesh.Faces[j].Vertices[l] & Prototype.Mesh.Faces[i].Vertices[n - 2] == Prototype.Mesh.Faces[j].Vertices[k]) { Array.Resize(ref Prototype.Mesh.Faces[i].Vertices, n + 2); Prototype.Mesh.Faces[i].Vertices[n] = Prototype.Mesh.Faces[j].Vertices[(k + 3) & 3]; Prototype.Mesh.Faces[i].Vertices[n + 1] = Prototype.Mesh.Faces[j].Vertices[(k + 2) & 3]; keep = false; } if (!keep) { break; } } if (!keep) { for (int k = j; k < f - 1; k++) { Prototype.Mesh.Faces[k] = Prototype.Mesh.Faces[k + 1]; } if (j < i) { i--; } f--; j--; } } } } } } } // join QUADS into new QUAD_STRIP index = -1; for (int i = 0; i < f - 1; i++) { int type = Prototype.Mesh.Faces[i].Flags & World.MeshFace.FaceTypeMask; if (type == World.MeshFace.FaceTypeQuads) { int face = Prototype.Mesh.Faces[i].Flags & World.MeshFace.Face2Mask; for (int j = i + 1; j < f; j++) { int type2 = Prototype.Mesh.Faces[j].Flags & World.MeshFace.FaceTypeMask; int face2 = Prototype.Mesh.Faces[j].Flags & World.MeshFace.Face2Mask; if (type2 == World.MeshFace.FaceTypeQuads & face == face2) { if (Prototype.Mesh.Faces[i].Material == Prototype.Mesh.Faces[j].Material) { for (int ik = 0; ik < 4; ik++) { int il = (ik + 1) & 3; for (int jk = 0; jk < 4; jk++) { int jl = (jk + 1) & 3; if (Prototype.Mesh.Faces[i].Vertices[ik] == Prototype.Mesh.Faces[j].Vertices[jl] & Prototype.Mesh.Faces[i].Vertices[il] == Prototype.Mesh.Faces[j].Vertices[jk]) { unchecked { Prototype.Mesh.Faces[i].Flags &= (byte)~World.MeshFace.FaceTypeMask; Prototype.Mesh.Faces[i].Flags |= World.MeshFace.FaceTypeQuadStrip; } Prototype.Mesh.Faces[i].Vertices = new World.MeshFaceVertex[] { Prototype.Mesh.Faces[i].Vertices[(ik + 2) & 3], Prototype.Mesh.Faces[i].Vertices[(ik + 3) & 3], Prototype.Mesh.Faces[i].Vertices[il], Prototype.Mesh.Faces[i].Vertices[ik], Prototype.Mesh.Faces[j].Vertices[(jk + 3) & 3], Prototype.Mesh.Faces[j].Vertices[(jk + 2) & 3] }; for (int k = j; k < f - 1; k++) { Prototype.Mesh.Faces[k] = Prototype.Mesh.Faces[k + 1]; } f--; index = i; break; } } if (index >= 0) break; } } } if (index >= 0) break; } } } if (index == -1) break; } // join TRIANGLES, join QUADS for (int i = 0; i < f - 1; i++) { int type = Prototype.Mesh.Faces[i].Flags & World.MeshFace.FaceTypeMask; if (type == World.MeshFace.FaceTypeTriangles | type == World.MeshFace.FaceTypeQuads) { int face = Prototype.Mesh.Faces[i].Flags & World.MeshFace.Face2Mask; for (int j = i + 1; j < f; j++) { int type2 = Prototype.Mesh.Faces[j].Flags & World.MeshFace.FaceTypeMask; int face2 = Prototype.Mesh.Faces[j].Flags & World.MeshFace.Face2Mask; if (type == type2 & face == face2) { if (Prototype.Mesh.Faces[i].Material == Prototype.Mesh.Faces[j].Material) { int n = Prototype.Mesh.Faces[i].Vertices.Length; Array.Resize(ref Prototype.Mesh.Faces[i].Vertices, n + Prototype.Mesh.Faces[j].Vertices.Length); for (int k = 0; k < Prototype.Mesh.Faces[j].Vertices.Length; k++) { Prototype.Mesh.Faces[i].Vertices[n + k] = Prototype.Mesh.Faces[j].Vertices[k]; } for (int k = j; k < f - 1; k++) { Prototype.Mesh.Faces[k] = Prototype.Mesh.Faces[k + 1]; } f--; j--; } } } } } } // finalize arrays if (v != Prototype.Mesh.Vertices.Length) { Array.Resize(ref Prototype.Mesh.Vertices, v); } if (m != Prototype.Mesh.Materials.Length) { Array.Resize(ref Prototype.Mesh.Materials, m); } if (f != Prototype.Mesh.Faces.Length) { Array.Resize(ref Prototype.Mesh.Faces, f); } } // join objects internal static void JoinObjects(ref StaticObject Base, StaticObject Add) { if (Base == null & Add == null) { return; } else if (Base == null) { Base = CloneObject(Add); } else if (Add != null) { int mf = Base.Mesh.Faces.Length; int mm = Base.Mesh.Materials.Length; int mv = Base.Mesh.Vertices.Length; Array.Resize(ref Base.Mesh.Faces, mf + Add.Mesh.Faces.Length); Array.Resize(ref Base.Mesh.Materials, mm + Add.Mesh.Materials.Length); Array.Resize(ref Base.Mesh.Vertices, mv + Add.Mesh.Vertices.Length); for (int i = 0; i < Add.Mesh.Faces.Length; i++) { Base.Mesh.Faces[mf + i] = Add.Mesh.Faces[i]; for (int j = 0; j < Base.Mesh.Faces[mf + i].Vertices.Length; j++) { Base.Mesh.Faces[mf + i].Vertices[j].Index += (ushort)mv; } Base.Mesh.Faces[mf + i].Material += (ushort)mm; } for (int i = 0; i < Add.Mesh.Materials.Length; i++) { Base.Mesh.Materials[mm + i] = Add.Mesh.Materials[i]; } for (int i = 0; i < Add.Mesh.Vertices.Length; i++) { Base.Mesh.Vertices[mv + i] = Add.Mesh.Vertices[i]; } } } // create object internal static void CreateObject(UnifiedObject Prototype, Vector3 Position, World.Transformation BaseTransformation, World.Transformation AuxTransformation, bool AccurateObjectDisposal, double StartingDistance, double EndingDistance, double BlockLength, double TrackPosition) { CreateObject(Prototype, Position, BaseTransformation, AuxTransformation, -1, AccurateObjectDisposal, StartingDistance, EndingDistance, BlockLength, TrackPosition, 1.0, false); } internal static void CreateObject(UnifiedObject Prototype, Vector3 Position, World.Transformation BaseTransformation, World.Transformation AuxTransformation, int SectionIndex, bool AccurateObjectDisposal, double StartingDistance, double EndingDistance, double BlockLength, double TrackPosition, double Brightness, bool DuplicateMaterials) { if (Prototype is StaticObject) { StaticObject s = (StaticObject)Prototype; CreateStaticObject(s, Position, BaseTransformation, AuxTransformation, AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, BlockLength, TrackPosition, Brightness, DuplicateMaterials); } else if (Prototype is AnimatedObjectCollection) { AnimatedObjectCollection a = (AnimatedObjectCollection)Prototype; CreateAnimatedWorldObjects(a.Objects, Position, BaseTransformation, AuxTransformation, SectionIndex, AccurateObjectDisposal, StartingDistance, EndingDistance, BlockLength, TrackPosition, Brightness, DuplicateMaterials); } } // create static object internal static int CreateStaticObject(StaticObject Prototype, Vector3 Position, World.Transformation BaseTransformation, World.Transformation AuxTransformation, bool AccurateObjectDisposal, double StartingDistance, double EndingDistance, double BlockLength, double TrackPosition) { return CreateStaticObject(Prototype, Position, BaseTransformation, AuxTransformation, AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, BlockLength, TrackPosition, 1.0, false); } internal static int CreateStaticObject(StaticObject Prototype, Vector3 Position, World.Transformation BaseTransformation, World.Transformation AuxTransformation, bool AccurateObjectDisposal, double AccurateObjectDisposalZOffset, double StartingDistance, double EndingDistance, double BlockLength, double TrackPosition, double Brightness, bool DuplicateMaterials) { int a = ObjectsUsed; if (a >= Objects.Length) { Array.Resize(ref Objects, Objects.Length << 1); } ApplyStaticObjectData(ref Objects[a], Prototype, Position, BaseTransformation, AuxTransformation, AccurateObjectDisposal, AccurateObjectDisposalZOffset, StartingDistance, EndingDistance, BlockLength, TrackPosition, Brightness, DuplicateMaterials); for (int i = 0; i < Prototype.Mesh.Faces.Length; i++) { switch (Prototype.Mesh.Faces[i].Flags & World.MeshFace.FaceTypeMask) { case World.MeshFace.FaceTypeTriangles: Game.InfoTotalTriangles++; break; case World.MeshFace.FaceTypeTriangleStrip: Game.InfoTotalTriangleStrip++; break; case World.MeshFace.FaceTypeQuads: Game.InfoTotalQuads++; break; case World.MeshFace.FaceTypeQuadStrip: Game.InfoTotalQuadStrip++; break; case World.MeshFace.FaceTypePolygon: Game.InfoTotalPolygon++; break; } } ObjectsUsed++; return a; } internal static void ApplyStaticObjectData(ref StaticObject Object, StaticObject Prototype, Vector3 Position, World.Transformation BaseTransformation, World.Transformation AuxTransformation, bool AccurateObjectDisposal, double AccurateObjectDisposalZOffset, double StartingDistance, double EndingDistance, double BlockLength, double TrackPosition, double Brightness, bool DuplicateMaterials) { Object = new StaticObject(); Object.StartingDistance = float.MaxValue; Object.EndingDistance = float.MinValue; bool brightnesschange = Brightness != 1.0; // vertices Object.Mesh.Vertices = new World.Vertex[Prototype.Mesh.Vertices.Length]; for (int j = 0; j < Prototype.Mesh.Vertices.Length; j++) { Object.Mesh.Vertices[j] = Prototype.Mesh.Vertices[j]; if (AccurateObjectDisposal) { World.Rotate(ref Object.Mesh.Vertices[j].Coordinates.X, ref Object.Mesh.Vertices[j].Coordinates.Y, ref Object.Mesh.Vertices[j].Coordinates.Z, AuxTransformation); if (Object.Mesh.Vertices[j].Coordinates.Z < Object.StartingDistance) { Object.StartingDistance = (float)Object.Mesh.Vertices[j].Coordinates.Z; } if (Object.Mesh.Vertices[j].Coordinates.Z > Object.EndingDistance) { Object.EndingDistance = (float)Object.Mesh.Vertices[j].Coordinates.Z; } Object.Mesh.Vertices[j].Coordinates = Prototype.Mesh.Vertices[j].Coordinates; } World.Rotate(ref Object.Mesh.Vertices[j].Coordinates.X, ref Object.Mesh.Vertices[j].Coordinates.Y, ref Object.Mesh.Vertices[j].Coordinates.Z, AuxTransformation); World.Rotate(ref Object.Mesh.Vertices[j].Coordinates.X, ref Object.Mesh.Vertices[j].Coordinates.Y, ref Object.Mesh.Vertices[j].Coordinates.Z, BaseTransformation); Object.Mesh.Vertices[j].Coordinates.X += Position.X; Object.Mesh.Vertices[j].Coordinates.Y += Position.Y; Object.Mesh.Vertices[j].Coordinates.Z += Position.Z; } if (AccurateObjectDisposal) { Object.StartingDistance += (float)AccurateObjectDisposalZOffset; Object.EndingDistance += (float)AccurateObjectDisposalZOffset; } // faces Object.Mesh.Faces = new World.MeshFace[Prototype.Mesh.Faces.Length]; for (int j = 0; j < Prototype.Mesh.Faces.Length; j++) { Object.Mesh.Faces[j].Flags = Prototype.Mesh.Faces[j].Flags; Object.Mesh.Faces[j].Material = Prototype.Mesh.Faces[j].Material; Object.Mesh.Faces[j].Vertices = new World.MeshFaceVertex[Prototype.Mesh.Faces[j].Vertices.Length]; for (int k = 0; k < Prototype.Mesh.Faces[j].Vertices.Length; k++) { Object.Mesh.Faces[j].Vertices[k] = Prototype.Mesh.Faces[j].Vertices[k]; double nx = Object.Mesh.Faces[j].Vertices[k].Normal.X; double ny = Object.Mesh.Faces[j].Vertices[k].Normal.Y; double nz = Object.Mesh.Faces[j].Vertices[k].Normal.Z; if (nx * nx + ny * ny + nz * nz != 0.0) { World.Rotate(ref Object.Mesh.Faces[j].Vertices[k].Normal.X, ref Object.Mesh.Faces[j].Vertices[k].Normal.Y, ref Object.Mesh.Faces[j].Vertices[k].Normal.Z, AuxTransformation); World.Rotate(ref Object.Mesh.Faces[j].Vertices[k].Normal.X, ref Object.Mesh.Faces[j].Vertices[k].Normal.Y, ref Object.Mesh.Faces[j].Vertices[k].Normal.Z, BaseTransformation); } } } // materials Object.Mesh.Materials = new World.MeshMaterial[Prototype.Mesh.Materials.Length]; for (int j = 0; j < Prototype.Mesh.Materials.Length; j++) { Object.Mesh.Materials[j] = Prototype.Mesh.Materials[j]; Object.Mesh.Materials[j].Color.R = (byte)Math.Round((double)Prototype.Mesh.Materials[j].Color.R * Brightness); Object.Mesh.Materials[j].Color.G = (byte)Math.Round((double)Prototype.Mesh.Materials[j].Color.G * Brightness); Object.Mesh.Materials[j].Color.B = (byte)Math.Round((double)Prototype.Mesh.Materials[j].Color.B * Brightness); } const double minBlockLength = 20.0; if (BlockLength < minBlockLength) { BlockLength = BlockLength * Math.Ceiling(minBlockLength / BlockLength); } if (AccurateObjectDisposal) { Object.StartingDistance += (float)TrackPosition; Object.EndingDistance += (float)TrackPosition; double z = BlockLength * Math.Floor(TrackPosition / BlockLength); StartingDistance = Math.Min(z - BlockLength, (double)Object.StartingDistance); EndingDistance = Math.Max(z + 2.0 * BlockLength, (double)Object.EndingDistance); Object.StartingDistance = (float)(BlockLength * Math.Floor(StartingDistance / BlockLength)); Object.EndingDistance = (float)(BlockLength * Math.Ceiling(EndingDistance / BlockLength)); } else { Object.StartingDistance = (float)StartingDistance; Object.EndingDistance = (float)EndingDistance; } if (BlockLength != 0.0) { checked { Object.GroupIndex = (short)Mod(Math.Floor(Object.StartingDistance / BlockLength), Math.Ceiling(Interface.CurrentOptions.ViewingDistance / BlockLength)); } } } private static double Mod(double a, double b) { return a - b * Math.Floor(a / b); } // create dynamic object internal static int CreateDynamicObject() { int a = ObjectsUsed; if (a >= Objects.Length) { Array.Resize(ref Objects, Objects.Length << 1); } Objects[a] = new StaticObject(); Objects[a].Mesh.Faces = new World.MeshFace[] { }; Objects[a].Mesh.Materials = new World.MeshMaterial[] { }; Objects[a].Mesh.Vertices = new World.Vertex[] { }; Objects[a].Dynamic = true; ObjectsUsed++; return a; } // clone object internal static StaticObject CloneObject(StaticObject Prototype) { if (Prototype == null) return null; return CloneObject(Prototype, null, null); } /// Creates a clone of the specified object. /// The prototype. /// The replacement daytime texture, or a null reference to keep the texture of the prototype. /// The replacement nighttime texture, or a null reference to keep the texture of the prototype. /// internal static StaticObject CloneObject(StaticObject Prototype, Textures.Texture DaytimeTexture, Textures.Texture NighttimeTexture) { if (Prototype == null) return null; StaticObject Result = new StaticObject(); Result.StartingDistance = Prototype.StartingDistance; Result.EndingDistance = Prototype.EndingDistance; Result.Dynamic = Prototype.Dynamic; // vertices Result.Mesh.Vertices = new World.Vertex[Prototype.Mesh.Vertices.Length]; for (int j = 0; j < Prototype.Mesh.Vertices.Length; j++) { Result.Mesh.Vertices[j] = Prototype.Mesh.Vertices[j]; } // faces Result.Mesh.Faces = new World.MeshFace[Prototype.Mesh.Faces.Length]; for (int j = 0; j < Prototype.Mesh.Faces.Length; j++) { Result.Mesh.Faces[j].Flags = Prototype.Mesh.Faces[j].Flags; Result.Mesh.Faces[j].Material = Prototype.Mesh.Faces[j].Material; Result.Mesh.Faces[j].Vertices = new World.MeshFaceVertex[Prototype.Mesh.Faces[j].Vertices.Length]; for (int k = 0; k < Prototype.Mesh.Faces[j].Vertices.Length; k++) { Result.Mesh.Faces[j].Vertices[k] = Prototype.Mesh.Faces[j].Vertices[k]; } } // materials Result.Mesh.Materials = new World.MeshMaterial[Prototype.Mesh.Materials.Length]; for (int j = 0; j < Prototype.Mesh.Materials.Length; j++) { Result.Mesh.Materials[j] = Prototype.Mesh.Materials[j]; if (DaytimeTexture != null) { Result.Mesh.Materials[j].DaytimeTexture = DaytimeTexture; } else { Result.Mesh.Materials[j].DaytimeTexture = Prototype.Mesh.Materials[j].DaytimeTexture; } if (DaytimeTexture != null) { Result.Mesh.Materials[j].NighttimeTexture = NighttimeTexture; } else { Result.Mesh.Materials[j].NighttimeTexture = Prototype.Mesh.Materials[j].NighttimeTexture; } } return Result; } // finish creating objects internal static void FinishCreatingObjects() { Array.Resize(ref Objects, ObjectsUsed); Array.Resize(ref AnimatedWorldObjects, AnimatedWorldObjectsUsed); } // initialize visibility internal static void InitializeVisibility() { // sort objects ObjectsSortedByStart = new int[ObjectsUsed]; ObjectsSortedByEnd = new int[ObjectsUsed]; double[] a = new double[ObjectsUsed]; double[] b = new double[ObjectsUsed]; int n = 0; for (int i = 0; i < ObjectsUsed; i++) { if (!Objects[i].Dynamic) { ObjectsSortedByStart[n] = i; ObjectsSortedByEnd[n] = i; a[n] = Objects[i].StartingDistance; b[n] = Objects[i].EndingDistance; n++; } } Array.Resize(ref ObjectsSortedByStart, n); Array.Resize(ref ObjectsSortedByEnd, n); Array.Resize(ref a, n); Array.Resize(ref b, n); Array.Sort(a, ObjectsSortedByStart); Array.Sort(b, ObjectsSortedByEnd); ObjectsSortedByStartPointer = 0; ObjectsSortedByEndPointer = 0; // initial visiblity double p = World.CameraTrackFollower.TrackPosition + World.CameraCurrentAlignment.Position.Z; for (int i = 0; i < ObjectsUsed; i++) { if (!Objects[i].Dynamic) { if (Objects[i].StartingDistance <= p + World.ForwardViewingDistance & Objects[i].EndingDistance >= p - World.BackwardViewingDistance) { Renderer.ShowObject(i, Renderer.ObjectType.Static); } } } } // update visibility internal static void UpdateVisibility(double TrackPosition, bool ViewingDistanceChanged) { if (ViewingDistanceChanged) { UpdateVisibility(TrackPosition); UpdateVisibility(TrackPosition - 0.001); UpdateVisibility(TrackPosition + 0.001); UpdateVisibility(TrackPosition); } else { UpdateVisibility(TrackPosition); } } internal static void UpdateVisibility(double TrackPosition) { double d = TrackPosition - LastUpdatedTrackPosition; int n = ObjectsSortedByStart.Length; int m = ObjectsSortedByEnd.Length; double p = World.CameraTrackFollower.TrackPosition + World.CameraCurrentAlignment.Position.Z; if (d < 0.0) { if (ObjectsSortedByStartPointer >= n) ObjectsSortedByStartPointer = n - 1; if (ObjectsSortedByEndPointer >= n) ObjectsSortedByEndPointer = n - 1; // dispose while (ObjectsSortedByStartPointer >= 0) { int o = ObjectsSortedByStart[ObjectsSortedByStartPointer]; if (Objects[o].StartingDistance > p + World.ForwardViewingDistance) { Renderer.HideObject(o); ObjectsSortedByStartPointer--; } else { break; } } // introduce while (ObjectsSortedByEndPointer >= 0) { int o = ObjectsSortedByEnd[ObjectsSortedByEndPointer]; if (Objects[o].EndingDistance >= p - World.BackwardViewingDistance) { if (Objects[o].StartingDistance <= p + World.ForwardViewingDistance) { Renderer.ShowObject(o, Renderer.ObjectType.Static); } ObjectsSortedByEndPointer--; } else { break; } } } else if (d > 0.0) { if (ObjectsSortedByStartPointer < 0) ObjectsSortedByStartPointer = 0; if (ObjectsSortedByEndPointer < 0) ObjectsSortedByEndPointer = 0; // dispose while (ObjectsSortedByEndPointer < n) { int o = ObjectsSortedByEnd[ObjectsSortedByEndPointer]; if (Objects[o].EndingDistance < p - World.BackwardViewingDistance) { Renderer.HideObject(o); ObjectsSortedByEndPointer++; } else { break; } } // introduce while (ObjectsSortedByStartPointer < n) { int o = ObjectsSortedByStart[ObjectsSortedByStartPointer]; if (Objects[o].StartingDistance <= p + World.ForwardViewingDistance) { if (Objects[o].EndingDistance >= p - World.BackwardViewingDistance) { Renderer.ShowObject(o, Renderer.ObjectType.Static); } ObjectsSortedByStartPointer++; } else { break; } } } LastUpdatedTrackPosition = TrackPosition; } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/PluginManager.cs000066400000000000000000000660741171674032100226350ustar00rootroot00000000000000using System; using System.Reflection; using OpenBveApi.Runtime; namespace OpenBve { internal static partial class PluginManager { /// Represents an abstract plugin. internal abstract class Plugin { // --- members --- /// The file title of the plugin, including the file extension. internal string PluginTitle; /// Whether the plugin is the default ATS/ATC plugin. internal bool IsDefault; /// Whether the plugin returned valid information in the last Elapse call. internal bool PluginValid; /// The debug message the plugin returned in the last Elapse call. internal string PluginMessage; /// The train the plugin is attached to. internal TrainManager.Train Train; /// The array of panel variables. internal int[] Panel; /// Whether the plugin supports the AI. internal bool SupportsAI; /// The last in-game time reported to the plugin. internal double LastTime; /// The last reverser reported to the plugin. internal int LastReverser; /// The last power notch reported to the plugin. internal int LastPowerNotch; /// The last brake notch reported to the plugin. internal int LastBrakeNotch; /// The last aspects per relative section reported to the plugin. Section 0 is the current section, section 1 the upcoming section, and so on. internal int[] LastAspects; /// The absolute section the train was last. internal int LastSection; /// The last exception the plugin raised. internal Exception LastException; // --- functions --- /// Called to load and initialize the plugin. /// The train specifications. /// The initialization mode of the train. /// Whether loading the plugin was successful. internal abstract bool Load(VehicleSpecs specs, InitializationModes mode); /// Called to unload the plugin. internal abstract void Unload(); /// Called before the train jumps to a different location. /// The initialization mode of the train. internal abstract void BeginJump(InitializationModes mode); /// Called when the train has finished jumping to a different location. internal abstract void EndJump(); /// Called every frame to update the plugin. internal void UpdatePlugin() { /* * Prepare the vehicle state. * */ double location = this.Train.Cars[0].FrontAxle.Follower.TrackPosition - this.Train.Cars[0].FrontAxlePosition + 0.5 * this.Train.Cars[0].Length; double speed = this.Train.Cars[this.Train.DriverCar].Specs.CurrentPerceivedSpeed; double bcPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.BrakeCylinderCurrentPressure; double mrPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.MainReservoirCurrentPressure; double erPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.EqualizingReservoirCurrentPressure; double bpPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.BrakePipeCurrentPressure; double sapPressure = this.Train.Cars[this.Train.DriverCar].Specs.AirBrake.StraightAirPipeCurrentPressure; VehicleState vehicle = new VehicleState(location, new Speed(speed), bcPressure, mrPressure, erPressure, bpPressure, sapPressure); /* * Prepare the preceding vehicle state. * */ double bestLocation = double.MaxValue; double bestSpeed = 0.0; for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i] != this.Train & TrainManager.Trains[i].State == TrainManager.TrainState.Available) { int c = TrainManager.Trains[i].Cars.Length - 1; double z = TrainManager.Trains[i].Cars[c].RearAxle.Follower.TrackPosition - TrainManager.Trains[i].Cars[c].RearAxlePosition - 0.5 * TrainManager.Trains[i].Cars[c].Length; if (z >= location & z < bestLocation) { bestLocation = z; bestSpeed = TrainManager.Trains[i].Specs.CurrentAverageSpeed; } } } PrecedingVehicleState precedingVehicle; if (bestLocation != double.MaxValue) { precedingVehicle = new PrecedingVehicleState(bestLocation, bestLocation - location, new Speed(bestSpeed)); } else { precedingVehicle = null; } /* * Get the driver handles. * */ Handles handles = GetHandles(); /* * Update the plugin. * */ double totalTime = Game.SecondsSinceMidnight; double elapsedTime = Game.SecondsSinceMidnight - LastTime; ElapseData data = new ElapseData(vehicle, precedingVehicle, handles, new Time(totalTime), new Time(elapsedTime)); LastTime = Game.SecondsSinceMidnight; Elapse(data); this.PluginMessage = data.DebugMessage; /* * Set the virtual handles. * */ this.PluginValid = true; SetHandles(data.Handles, true); } /// Gets the driver handles. /// The driver handles. private Handles GetHandles() { int reverser = this.Train.Specs.CurrentReverser.Driver; int powerNotch = this.Train.Specs.CurrentPowerNotch.Driver; int brakeNotch; if (this.Train.Cars[this.Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? 3 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service ? 2 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap ? 1 : 0; } else { if (this.Train.Specs.HasHoldBrake) { brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 2 : this.Train.Specs.CurrentBrakeNotch.Driver > 0 ? this.Train.Specs.CurrentBrakeNotch.Driver + 1 : this.Train.Specs.CurrentHoldBrake.Driver ? 1 : 0; } else { brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 1 : this.Train.Specs.CurrentBrakeNotch.Driver; } } bool constSpeed = this.Train.Specs.CurrentConstSpeed; return new Handles(reverser, powerNotch, brakeNotch, constSpeed); } /// Sets the driver handles or the virtual handles. /// The handles. /// Whether to set the virtual handles. private void SetHandles(Handles handles, bool virtualHandles) { /* * Process the handles. */ if (this.Train.Specs.SingleHandle & handles.BrakeNotch != 0) { handles.PowerNotch = 0; } /* * Process the reverser. */ if (handles.Reverser >= -1 & handles.Reverser <= 1) { if (virtualHandles) { this.Train.Specs.CurrentReverser.Actual = handles.Reverser; } else { TrainManager.ApplyReverser(this.Train, handles.Reverser, false); } } else { if (virtualHandles) { this.Train.Specs.CurrentReverser.Actual = this.Train.Specs.CurrentReverser.Driver; } this.PluginValid = false; } /* * Process the power. * */ if (handles.PowerNotch >= 0 & handles.PowerNotch <= this.Train.Specs.MaximumPowerNotch) { if (virtualHandles) { this.Train.Specs.CurrentPowerNotch.Safety = handles.PowerNotch; } else { TrainManager.ApplyNotch(this.Train, handles.PowerNotch, false, 0, true); } } else { if (virtualHandles) { this.Train.Specs.CurrentPowerNotch.Safety = this.Train.Specs.CurrentPowerNotch.Driver; } this.PluginValid = false; } // if (handles.BrakeNotch != 0) { // if (virtualHandles) { // this.Train.Specs.CurrentPowerNotch.Safety = 0; // } // } /* * Process the brakes. * */ if (virtualHandles) { this.Train.Specs.CurrentEmergencyBrake.Safety = false; this.Train.Specs.CurrentHoldBrake.Actual = false; } if (this.Train.Cars[this.Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { if (handles.BrakeNotch == 0) { if (virtualHandles) { this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Release; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Release); } } else if (handles.BrakeNotch == 1) { if (virtualHandles) { this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Lap; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Lap); } } else if (handles.BrakeNotch == 2) { if (virtualHandles) { this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Release); } } else if (handles.BrakeNotch == 3) { if (virtualHandles) { this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service; this.Train.Specs.CurrentEmergencyBrake.Safety = true; } else { TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Service); TrainManager.ApplyEmergencyBrake(this.Train); } } else { this.PluginValid = false; } } else { if (this.Train.Specs.HasHoldBrake) { if (handles.BrakeNotch == this.Train.Specs.MaximumBrakeNotch + 2) { if (virtualHandles) { this.Train.Specs.CurrentEmergencyBrake.Safety = true; this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.MaximumBrakeNotch; } else { TrainManager.ApplyHoldBrake(this.Train, false); TrainManager.ApplyNotch(this.Train, 0, true, this.Train.Specs.MaximumBrakeNotch, false); TrainManager.ApplyEmergencyBrake(this.Train); } } else if (handles.BrakeNotch >= 2 & handles.BrakeNotch <= this.Train.Specs.MaximumBrakeNotch + 1) { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = handles.BrakeNotch - 1; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyHoldBrake(this.Train, false); TrainManager.ApplyNotch(this.Train, 0, true, handles.BrakeNotch - 1, false); } } else if (handles.BrakeNotch == 1) { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = 0; this.Train.Specs.CurrentHoldBrake.Actual = true; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyNotch(this.Train, 0, true, 0, false); TrainManager.ApplyHoldBrake(this.Train, true); } } else if (handles.BrakeNotch == 0) { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = 0; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyNotch(this.Train, 0, true, 0, false); TrainManager.ApplyHoldBrake(this.Train, false); } } else { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.CurrentBrakeNotch.Driver; } this.PluginValid = false; } } else { if (handles.BrakeNotch == this.Train.Specs.MaximumBrakeNotch + 1) { if (virtualHandles) { this.Train.Specs.CurrentEmergencyBrake.Safety = true; this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.MaximumBrakeNotch; } else { TrainManager.ApplyHoldBrake(this.Train, false); TrainManager.ApplyEmergencyBrake(this.Train); } } else if (handles.BrakeNotch >= 0 & handles.BrakeNotch <= this.Train.Specs.MaximumBrakeNotch | this.Train.Specs.CurrentBrakeNotch.DelayedChanges.Length == 0) { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = handles.BrakeNotch; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyNotch(this.Train, 0, true, handles.BrakeNotch, false); } } else { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.CurrentBrakeNotch.Driver; } this.PluginValid = false; } } } /* * Process the const speed system. * */ this.Train.Specs.CurrentConstSpeed = handles.ConstSpeed & this.Train.Specs.HasConstSpeed; } /// Called every frame to update the plugin. /// The data passed to the plugin on Elapse. /// This function should not be called directly. Call UpdatePlugin instead. internal abstract void Elapse(ElapseData data); /// Called to update the reverser. This invokes a call to SetReverser only if a change actually occured. internal void UpdateReverser() { int reverser = this.Train.Specs.CurrentReverser.Driver; if (reverser != this.LastReverser) { this.LastReverser = reverser; SetReverser(reverser); } } /// Called to indicate a change of the reverser. /// The reverser. /// This function should not be called directly. Call UpdateReverser instead. internal abstract void SetReverser(int reverser); /// Called to update the power notch. This invokes a call to SetPower only if a change actually occured. internal void UpdatePower() { int powerNotch = this.Train.Specs.CurrentPowerNotch.Driver; if (powerNotch != this.LastPowerNotch) { this.LastPowerNotch = powerNotch; SetPower(powerNotch); } } /// Called to indicate a change of the power notch. /// The power notch. /// This function should not be called directly. Call UpdatePower instead. internal abstract void SetPower(int powerNotch); /// Called to update the brake notch. This invokes a call to SetBrake only if a change actually occured. internal void UpdateBrake() { int brakeNotch; if (this.Train.Cars[this.Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { if (this.Train.Specs.HasHoldBrake) { brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? 4 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service ? 3 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap ? 2 : this.Train.Specs.CurrentHoldBrake.Driver ? 1 : 0; } else { brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? 3 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service ? 2 : this.Train.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap ? 1 : 0; } } else { if (this.Train.Specs.HasHoldBrake) { brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 2 : this.Train.Specs.CurrentBrakeNotch.Driver > 0 ? this.Train.Specs.CurrentBrakeNotch.Driver + 1 : this.Train.Specs.CurrentHoldBrake.Driver ? 1 : 0; } else { brakeNotch = this.Train.Specs.CurrentEmergencyBrake.Driver ? this.Train.Specs.MaximumBrakeNotch + 1 : this.Train.Specs.CurrentBrakeNotch.Driver; } } if (brakeNotch != this.LastBrakeNotch) { this.LastBrakeNotch = brakeNotch; SetBrake(brakeNotch); } } /// Called to indicate a change of the brake notch. /// The brake notch. /// This function should not be called directly. Call UpdateBrake instead. internal abstract void SetBrake(int brakeNotch); /// Called when a virtual key is pressed. internal abstract void KeyDown(VirtualKeys key); /// Called when a virtual key is released. internal abstract void KeyUp(VirtualKeys key); /// Called when a horn is played or stopped. internal abstract void HornBlow(HornTypes type); /// Called when the state of the doors changes. internal abstract void DoorChange(DoorStates oldState, DoorStates newState); /// Called to update the aspects of the section. This invokes a call to SetSignal only if a change in aspect occured or when changing section boundaries. /// The sections to submit to the plugin. internal void UpdateSignals(SignalData[] data) { if (data.Length != 0) { bool update; if (this.Train.CurrentSectionIndex != this.LastSection) { update = true; } else if (data.Length != this.LastAspects.Length) { update = true; } else { update = false; for (int i = 0; i < data.Length; i++) { if (data[i].Aspect != this.LastAspects[i]) { update = true; break; } } } if (update) { SetSignal(data); this.LastAspects = new int[data.Length]; for (int i = 0; i < data.Length; i++) { this.LastAspects[i] = data[i].Aspect; } } } } /// Is called when the aspect in the current or any of the upcoming sections changes. /// Signal information per section. In the array, index 0 is the current section, index 1 the upcoming section, and so on. /// This function should not be called directly. Call UpdateSignal instead. internal abstract void SetSignal(SignalData[] signal); /// Called when the train passes a beacon. /// The beacon type. /// The section the beacon is attached to, or -1 for the next red signal. /// Optional data attached to the beacon. internal void UpdateBeacon(int type, int sectionIndex, int optional) { if (sectionIndex == -1) { sectionIndex = this.Train.CurrentSectionIndex + 1; SignalData signal = null; while (sectionIndex < Game.Sections.Length) { signal = Game.GetPluginSignal(this.Train, sectionIndex); if (signal.Aspect == 0) break; sectionIndex++; } if (sectionIndex < Game.Sections.Length) { SetBeacon(new BeaconData(type, optional, signal)); } else { SetBeacon(new BeaconData(type, optional, new SignalData(-1, double.MaxValue))); } } if (sectionIndex >= 0) { SignalData signal; if (sectionIndex < Game.Sections.Length) { signal = Game.GetPluginSignal(this.Train, sectionIndex); } else { signal = new SignalData(0, double.MaxValue); } SetBeacon(new BeaconData(type, optional, signal)); } else { SetBeacon(new BeaconData(type, optional, new SignalData(-1, double.MaxValue))); } } /// Called when the train passes a beacon. /// The beacon data. /// This function should not be called directly. Call UpdateBeacon instead. internal abstract void SetBeacon(BeaconData beacon); /// Updates the AI. /// The AI response. internal AIResponse UpdateAI() { if (this.SupportsAI) { AIData data = new AIData(GetHandles()); this.PerformAI(data); if (data.Response != AIResponse.None) { SetHandles(data.Handles, false); } return data.Response; } else { return AIResponse.None; } } /// Called when the AI should be performed. /// The AI data. /// This function should not be called directly. Call UpdateAI instead. internal abstract void PerformAI(AIData data); } /// Loads a custom plugin for the specified train. /// The train to attach the plugin to. /// The absolute path to the train folder. /// The encoding to be used. /// Whether the plugin was loaded successfully. internal static bool LoadCustomPlugin(TrainManager.Train train, string trainFolder, System.Text.Encoding encoding) { string config = OpenBveApi.Path.CombineFile(trainFolder, "ats.cfg"); if (!System.IO.File.Exists(config)) { return false; } string[] lines = System.IO.File.ReadAllLines(config, encoding); if (lines.Length == 0) { return false; } string file = OpenBveApi.Path.CombineFile(trainFolder, lines[0]); string title = System.IO.Path.GetFileName(file); if (!System.IO.File.Exists(file)) { Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + title + " could not be found in " + config); return false; } return LoadPlugin(train, file, trainFolder); } /// Loads the default plugin for the specified train. /// The train to attach the plugin to. /// The train folder. /// Whether the plugin was loaded successfully. internal static bool LoadDefaultPlugin(TrainManager.Train train, string trainFolder) { string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Plugins"), "OpenBveAts.dll"); bool success = LoadPlugin(train, file, trainFolder); if (success) { train.Plugin.IsDefault = true; SoundCfgParser.LoadDefaultPluginSounds(train, trainFolder); } return success; } /// Loads the specified plugin for the specified train. /// The train to attach the plugin to. /// The file to the plugin. /// The train folder. /// Whether the plugin was loaded successfully. private static bool LoadPlugin(TrainManager.Train train, string pluginFile, string trainFolder) { string pluginTitle = System.IO.Path.GetFileName(pluginFile); if (!System.IO.File.Exists(pluginFile)) { Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + pluginTitle + " could not be found."); return false; } /* * Unload plugin if already loaded. * */ if (train.Plugin != null) { UnloadPlugin(train); } /* * Prepare initialization data for the plugin. * */ BrakeTypes brakeType = (BrakeTypes)train.Cars[train.DriverCar].Specs.BrakeType; int brakeNotches; int powerNotches; bool hasHoldBrake; if (brakeType == BrakeTypes.AutomaticAirBrake) { brakeNotches = 2; powerNotches = train.Specs.MaximumPowerNotch; hasHoldBrake = false; } else { brakeNotches = train.Specs.MaximumBrakeNotch + (train.Specs.HasHoldBrake ? 1 : 0); powerNotches = train.Specs.MaximumPowerNotch; hasHoldBrake = train.Specs.HasHoldBrake; } int cars = train.Cars.Length; VehicleSpecs specs = new VehicleSpecs(powerNotches, brakeType, brakeNotches, hasHoldBrake, cars); InitializationModes mode = (InitializationModes)Game.TrainStart; /* * Check if the plugin is a .NET plugin. * */ Assembly assembly; try { assembly = Assembly.LoadFile(pluginFile); } catch (BadImageFormatException) { assembly = null; } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " could not be loaded due to the following exception: " + ex.Message); return false; } if (assembly != null) { Type[] types; try { types = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { foreach (Exception e in ex.LoaderExceptions) { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " raised an exception on loading: " + e.Message); } return false; } foreach (Type type in types) { if (type.IsPublic && (type.Attributes & TypeAttributes.Abstract) == 0) { object instance = assembly.CreateInstance(type.FullName); IRuntime api = instance as IRuntime; if (api != null) { train.Plugin = new NetPlugin(pluginFile, trainFolder, api, train); if (train.Plugin.Load(specs, mode)) { return true; } else { train.Plugin = null; return false; } } } } Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE."); return false; } /* * Check if the plugin is a Win32 plugin. * */ try { if (!CheckWin32Header(pluginFile)) { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " is of an unsupported binary format and therefore cannot be used with openBVE."); return false; } } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " could not be read due to the following reason: " + ex.Message); return false; } if (!Program.CurrentlyRunningOnWindows | IntPtr.Size != 4) { Interface.AddMessage(Interface.MessageType.Warning, false, "The train plugin " + pluginTitle + " can only be used on 32-bit Microsoft Windows or compatible."); return false; } train.Plugin = new Win32Plugin(pluginFile, train); if (train.Plugin.Load(specs, mode)) { return true; } else { train.Plugin = null; return false; } } /// Checks whether a specified file is a valid Win32 plugin. /// The file to check. /// Whether the file is a valid Win32 plugin. private static bool CheckWin32Header(string file) { using (System.IO.FileStream stream = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read)) { using (System.IO.BinaryReader reader = new System.IO.BinaryReader(stream)) { if (reader.ReadUInt16() != 0x5A4D) { /* Not MZ signature */ return false; } stream.Position = 0x3C; stream.Position = reader.ReadInt32(); if (reader.ReadUInt32() != 0x00004550) { /* Not PE signature */ return false; } if (reader.ReadUInt16() != 0x014C) { /* Not IMAGE_FILE_MACHINE_I386 */ return false; } } } return true; } /// Unloads the currently loaded plugin, if any. /// Unloads the plugin for the specified train. internal static void UnloadPlugin(TrainManager.Train train) { if (train.Plugin != null) { train.Plugin.Unload(); train.Plugin = null; } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/Program.cs000066400000000000000000000355521171674032100215100ustar00rootroot00000000000000--- DO NOT COMPILE --- using System; using System.Diagnostics; using System.Reflection; using System.Windows.Forms; using Tao.OpenGl; using Tao.Sdl; namespace OpenBve { public static partial class Program { // system internal static string RestartArguments = null; internal enum Platform { Windows, Linux, Mac } internal static Platform CurrentPlatform = Platform.Windows; internal static bool CurrentlyRunOnMono = false; internal static FileSystem FileSystem = null; internal enum ProgramType { OpenBve, RouteViewer, ObjectViewer, Other }; internal const ProgramType CurrentProgramType = ProgramType.OpenBve; private static bool SdlWindowCreated = false; internal static Host CurrentHost = new Host(); // main [STAThread] private static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // platform and mono int p = (int)Environment.OSVersion.Platform; if (p == 4 | p == 128) { // general Unix CurrentPlatform = Platform.Linux; } else if (p == 6) { // Mac CurrentPlatform = Platform.Mac; } else { // non-Unix CurrentPlatform = Platform.Windows; } CurrentlyRunOnMono = Type.GetType("Mono.Runtime") != null; // file system try { FileSystem = FileSystem.FromCommandLineArgs(args); } catch (Exception ex) { MessageBox.Show("The file system configuration could not be accessed or is invalid due to the following reason:\n\n" + ex.Message, "openBVE", MessageBoxButtons.OK, MessageBoxIcon.Hand); return; } FileSystem.CreateFileSystem(); // start #if DEBUG try { #endif Start(args); #if DEBUG } catch (Exception ex) { bool shown = false; for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i] != null && TrainManager.Trains[i].Plugin != null) { if (TrainManager.Trains[i].Plugin.LastException != null) { string text = GetExceptionText(TrainManager.Trains[i].Plugin.LastException, 5); MessageBox.Show("The train plugin " + TrainManager.Trains[i].Plugin.PluginTitle + " caused the following exception:\n\n" + text, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); shown = false; } } } if (!shown) { string text = GetExceptionText(ex, 5); MessageBox.Show(text, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } #endif // deinitialize if(SdlWindowCreated & Interface.CurrentOptions.FullscreenMode) { Sdl.SDL_SetVideoMode(Interface.CurrentOptions.WindowWidth, Interface.CurrentOptions.WindowHeight, 32, Sdl.SDL_OPENGL | Sdl.SDL_DOUBLEBUF); } Renderer.Deinitialize(); Textures.Deinitialize(); Plugins.UnloadPlugins(); for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i] != null && TrainManager.Trains[i].Plugin != null) { PluginManager.UnloadPlugin(TrainManager.Trains[i]); } } SoundManager.Deinitialize(); // close sdl for (int i = 0; i < Interface.CurrentJoysticks.Length; i++) { Sdl.SDL_JoystickClose(Interface.CurrentJoysticks[i].SdlHandle); Interface.CurrentJoysticks[i].SdlHandle = IntPtr.Zero; } Sdl.SDL_Quit(); // restart if (RestartArguments != null) { string arguments; if (FileSystem.RestartArguments.Length != 0 & RestartArguments.Length != 0) { arguments = FileSystem.RestartArguments + " " + RestartArguments; } else { arguments = FileSystem.RestartArguments + RestartArguments; } try { System.Diagnostics.Process.Start(FileSystem.RestartProcess, arguments); } catch (Exception ex) { MessageBox.Show(ex.Message + "\n\nProcess = " + FileSystem.RestartProcess + "\nArguments = " + arguments, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } } // get exception text /// Returns a textual representation of an exception and its inner exceptions. /// The exception to serialize. /// The amount of inner exceptions to include. private static string GetExceptionText(Exception ex, int Levels) { if (Levels > 0 & ex.InnerException != null) { return ex.Message + "\n\n" + GetExceptionText(ex.InnerException, Levels - 1); } else { return ex.Message; } } // start private static void Start(string[] Args) { // initialize sdl video if (Sdl.SDL_Init(Sdl.SDL_INIT_VIDEO) != 0) { MessageBox.Show("SDL failed to initialize the video subsystem.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); return; } if (Sdl.SDL_Init(Sdl.SDL_INIT_JOYSTICK) != 0) { MessageBox.Show("SDL failed to initialize the joystick subsystem.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); return; } // initialize sdl joysticks { int n = Sdl.SDL_NumJoysticks(); Interface.CurrentJoysticks = new Interface.Joystick[n]; for (int i = 0; i < n; i++) { Interface.CurrentJoysticks[i].SdlHandle = Sdl.SDL_JoystickOpen(i); if (CurrentPlatform == Platform.Windows) { string s = Sdl.SDL_JoystickName(i); /* string returned is ascii packed in utf-16 (2 chars per codepoint) */ System.Text.StringBuilder t = new System.Text.StringBuilder(s.Length << 1); for (int k = 0; k < s.Length; k++) { int a = (int)s[k]; t.Append(char.ConvertFromUtf32(a & 0xFF) + char.ConvertFromUtf32(a >> 8)); } Interface.CurrentJoysticks[i].Name = t.ToString(); } else { Interface.CurrentJoysticks[i].Name = Sdl.SDL_JoystickName(i); } } } // load options and controls Interface.LoadOptions(); Interface.LoadControls(null, out Interface.CurrentControls); { string f = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Controls"), "Default keyboard assignment.controls"); Interface.Control[] c; Interface.LoadControls(f, out c); Interface.AddControls(ref Interface.CurrentControls, c); } // command line arguments formMain.MainDialogResult Result = new formMain.MainDialogResult(); for (int i = 0; i < Args.Length; i++) { if (Args[i].StartsWith("/route=", StringComparison.OrdinalIgnoreCase)) { Result.RouteFile = Args[i].Substring(7); Result.RouteEncoding = System.Text.Encoding.UTF8; for (int j = 0; j < Interface.CurrentOptions.RouteEncodings.Length; j++) { if (string.Compare(Interface.CurrentOptions.RouteEncodings[j].Value, Result.RouteFile, StringComparison.InvariantCultureIgnoreCase) == 0) { Result.RouteEncoding = System.Text.Encoding.GetEncoding(Interface.CurrentOptions.RouteEncodings[j].Codepage); break; } } } else if (Args[i].StartsWith("/train=", StringComparison.OrdinalIgnoreCase)) { Result.TrainFolder = Args[i].Substring(7); Result.TrainEncoding = System.Text.Encoding.UTF8; for (int j = 0; j < Interface.CurrentOptions.TrainEncodings.Length; j++) { if (string.Compare(Interface.CurrentOptions.TrainEncodings[j].Value, Result.TrainFolder, StringComparison.InvariantCultureIgnoreCase) == 0) { Result.TrainEncoding = System.Text.Encoding.GetEncoding(Interface.CurrentOptions.TrainEncodings[j].Codepage); break; } } } } // train provided if (Result.TrainFolder != null) { if (System.IO.Directory.Exists(Result.TrainFolder)) { string File = OpenBveApi.Path.CombineFile(Result.TrainFolder, "train.dat"); if (System.IO.File.Exists(File)) { Result.TrainEncoding = System.Text.Encoding.UTF8; for (int j = 0; j < Interface.CurrentOptions.TrainEncodings.Length; j++) { if (string.Compare(Interface.CurrentOptions.TrainEncodings[j].Value, Result.TrainFolder, StringComparison.InvariantCultureIgnoreCase) == 0) { Result.TrainEncoding = System.Text.Encoding.GetEncoding(Interface.CurrentOptions.TrainEncodings[j].Codepage); } } } else { Result.TrainFolder = null; } } else { Result.TrainFolder = null; } } // route provided if (Result.RouteFile != null) { if (!System.IO.File.Exists(Result.RouteFile)) { Result.RouteFile = null; } } // route provided but no train if (Result.RouteFile != null & Result.TrainFolder == null) { bool IsRW = string.Equals(System.IO.Path.GetExtension(Result.RouteFile), ".rw", StringComparison.OrdinalIgnoreCase); CsvRwRouteParser.ParseRoute(Result.RouteFile, IsRW, Result.RouteEncoding, null, null, null, true); if (Game.TrainName != null && Game.TrainName.Length != 0) { string Folder = System.IO.Path.GetDirectoryName(Result.RouteFile); while (true) { string TrainFolder = OpenBveApi.Path.CombineDirectory(Folder, "Train"); if (System.IO.Directory.Exists(TrainFolder)) { Folder = OpenBveApi.Path.CombineDirectory(TrainFolder, Game.TrainName); if (System.IO.Directory.Exists(Folder)) { string File = OpenBveApi.Path.CombineFile(Folder, "train.dat"); if (System.IO.File.Exists(File)) { // associated train found Result.TrainFolder = Folder; Result.TrainEncoding = System.Text.Encoding.UTF8; for (int j = 0; j < Interface.CurrentOptions.TrainEncodings.Length; j++) { if (string.Compare(Interface.CurrentOptions.TrainEncodings[j].Value, Result.TrainFolder, StringComparison.InvariantCultureIgnoreCase) == 0) { Result.TrainEncoding = System.Text.Encoding.GetEncoding(Interface.CurrentOptions.TrainEncodings[j].Codepage); break; } } } } break; } else { System.IO.DirectoryInfo Info = System.IO.Directory.GetParent(Folder); if (Info != null) { Folder = Info.FullName; } else { break; } } } } Game.Reset(false); } // show main menu if applicable if (Result.RouteFile == null | Result.TrainFolder == null) { Result = formMain.ShowMainDialog(); if (!Result.Start) { return; } } // screen int Width = Interface.CurrentOptions.FullscreenMode ? Interface.CurrentOptions.FullscreenWidth : Interface.CurrentOptions.WindowWidth; int Height = Interface.CurrentOptions.FullscreenMode ? Interface.CurrentOptions.FullscreenHeight : Interface.CurrentOptions.WindowHeight; if (Width < 16) Width = 16; if (Height < 16) Height = 16; Screen.Width = Width; Screen.Height = Height; World.AspectRatio = (double)Screen.Width / (double)Screen.Height; const double degree = 0.0174532925199433; World.VerticalViewingAngle = 45.0 * degree; World.HorizontalViewingAngle = 2.0 * Math.Atan(Math.Tan(0.5 * World.VerticalViewingAngle) * World.AspectRatio); World.OriginalVerticalViewingAngle = World.VerticalViewingAngle; World.ExtraViewingDistance = 50.0; World.ForwardViewingDistance = (double)Interface.CurrentOptions.ViewingDistance; World.BackwardViewingDistance = 0.0; World.BackgroundImageDistance = (double)Interface.CurrentOptions.ViewingDistance; // load route and train SoundManager.Initialize(); Plugins.LoadPlugins(); if (!Loading.LoadX(Result.RouteFile, Result.RouteEncoding, Result.TrainFolder, Result.TrainEncoding)) { return; } Game.LogRouteName = System.IO.Path.GetFileName(Result.RouteFile); Game.LogTrainName = System.IO.Path.GetFileName(Result.TrainFolder); Game.LogDateTime = DateTime.Now; // initialize sdl window Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_DOUBLEBUFFER, 1); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_DEPTH_SIZE, 24); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_RED_SIZE, 8); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_GREEN_SIZE, 8); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_BLUE_SIZE, 8); //Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_ALPHA_SIZE, 8); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_SWAP_CONTROL, Interface.CurrentOptions.VerticalSynchronization ? 1 : 0); Sdl.SDL_ShowCursor(Sdl.SDL_DISABLE); SdlWindowCreated = true; int Bits = Interface.CurrentOptions.FullscreenMode ? Interface.CurrentOptions.FullscreenBits : 32; // icon { string File = OpenBveApi.Path.CombineFile(Program.FileSystem.DataFolder, "icon.bmp"); if (System.IO.File.Exists(File)) { try { IntPtr Bitmap = Sdl.SDL_LoadBMP(File); if (Bitmap != null) { if (CurrentPlatform == Platform.Windows) { Sdl.SDL_Surface Surface = (Sdl.SDL_Surface)System.Runtime.InteropServices.Marshal.PtrToStructure(Bitmap, typeof(Sdl.SDL_Surface)); int ColorKey = Sdl.SDL_MapRGB(Surface.format, 0, 0, 255); Sdl.SDL_SetColorKey(Bitmap, Sdl.SDL_SRCCOLORKEY, ColorKey); Sdl.SDL_WM_SetIcon(Bitmap, null); } else { Sdl.SDL_WM_SetIcon(Bitmap, null); } } } catch { } } } // create window int fullscreen = Interface.CurrentOptions.FullscreenMode ? Sdl.SDL_FULLSCREEN : 0; IntPtr video = Sdl.SDL_SetVideoMode(Width, Height, Bits, Sdl.SDL_OPENGL | Sdl.SDL_DOUBLEBUF | fullscreen); if (video != IntPtr.Zero) { // create window Sdl.SDL_WM_SetCaption(Application.ProductName, null); // anisotropic filtering string[] Extensions = Gl.glGetString(Gl.GL_EXTENSIONS).Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); Interface.CurrentOptions.AnisotropicFilteringMaximum = 0; for (int i = 0; i < Extensions.Length; i++) { if (string.Compare(Extensions[i], "GL_EXT_texture_filter_anisotropic", StringComparison.OrdinalIgnoreCase) == 0) { float n; Gl.glGetFloatv(Gl.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, out n); Interface.CurrentOptions.AnisotropicFilteringMaximum = (int)Math.Round((double)n); break; } } if (Interface.CurrentOptions.AnisotropicFilteringMaximum <= 0) { Interface.CurrentOptions.AnisotropicFilteringMaximum = 0; Interface.CurrentOptions.AnisotropicFilteringLevel = 0; } else if (Interface.CurrentOptions.AnisotropicFilteringLevel == 0 & Interface.CurrentOptions.AnisotropicFilteringMaximum > 0) { Interface.CurrentOptions.AnisotropicFilteringLevel = Interface.CurrentOptions.AnisotropicFilteringMaximum; } else if (Interface.CurrentOptions.AnisotropicFilteringLevel > Interface.CurrentOptions.AnisotropicFilteringMaximum) { Interface.CurrentOptions.AnisotropicFilteringLevel = Interface.CurrentOptions.AnisotropicFilteringMaximum; } // module initialization Renderer.Initialize(); Renderer.InitializeLighting(); Sdl.SDL_GL_SwapBuffers(); Timetable.CreateTimetable(); // camera MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); MainLoop.InitializeMotionBlur(); // start loop MainLoop.StartLoop(); } else { // failed MessageBox.Show("SDL failed to create the window.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/Renderer.cs000066400000000000000000004616611171674032100216530ustar00rootroot00000000000000using System; using OpenBveApi.Colors; using OpenBveApi.Math; using Tao.OpenGl; namespace OpenBve { internal static partial class Renderer { private static void RenderOverlayTexture(Textures.Texture texture, double left, double top, double right, double bottom) { DrawRectangle(texture, new System.Drawing.Point((int)left, (int)top), new System.Drawing.Size((int)(right - left), (int)(bottom - top)), null); } private static void RenderOverlaySolid(double left, double top, double right, double bottom) { DrawRectangle(null, new System.Drawing.Point((int)left, (int)top), new System.Drawing.Size((int)(right - left), (int)(bottom - top)), null); } // first frame behavior internal enum LoadTextureImmediatelyMode { NotYet, Yes, NoLonger } internal static LoadTextureImmediatelyMode LoadTexturesImmediately = LoadTextureImmediatelyMode.NotYet; // transparency internal enum TransparencyMode { /// Textures using color-key transparency are considered opaque, producing good performance but crisp outlines. Partially transparent faces are rendered in a single pass with z-buffer writes disabled, producing good performance but more depth-sorting issues. Performance = 0, /// Textures using color-key transparency are considered opaque, producing good performance but crisp outlines. Partially transparent faces are rendered in two passes, the first rendering only opaque pixels with z-buffer writes enabled, and the second rendering only partially transparent pixels with z-buffer writes disabled, producing best quality but worse performance. Intermediate = 1, /// Textures using color-key transparency are considered partially transparent. All partially transparent faces are rendered in two passes, the first rendering only opaque pixels with z-buffer writes enabled, and the second rendering only partially transparent pixels with z-buffer writes disabled, producing best quality but worse performance. Quality = 2 } // output mode internal enum OutputMode { Default = 0, Debug = 1, None = 2 } internal static OutputMode CurrentOutputMode = OutputMode.Default; // object list private struct Object { internal int ObjectIndex; internal ObjectListReference[] FaceListReferences; internal ObjectType Type; } private static Object[] Objects = new Object[256]; private static int ObjectCount = 0; private enum ObjectListType : byte { /// The face is fully opaque and originates from an object that is part of the static scenery. StaticOpaque = 1, /// The face is fully opaque and originates from an object that is part of the dynamic scenery or of a train exterior. DynamicOpaque = 2, /// The face is partly transparent and originates from an object that is part of the scenery or of a train exterior. DynamicAlpha = 3, /// The face is fully opaque and originates from an object that is part of the cab. OverlayOpaque = 4, /// The face is partly transparent and originates from an object that is part of the cab. OverlayAlpha = 5 } internal enum ObjectType : byte { /// The object is part of the static scenery. The matching ObjectListType is StaticOpaque for fully opaque faces, and DynamicAlpha for all other faces. Static = 1, /// The object is part of the animated scenery or of a train exterior. The matching ObjectListType is DynamicOpaque for fully opaque faces, and DynamicAlpha for all other faces. Dynamic = 2, /// The object is part of the cab. The matching ObjectListType is OverlayOpaque for fully opaque faces, and OverlayAlpha for all other faces. Overlay = 3 } private struct ObjectListReference { /// The type of list. internal ObjectListType Type; /// The index in the specified list. internal int Index; internal ObjectListReference(ObjectListType type, int index) { this.Type = type; this.Index = index; } } private class ObjectFace { internal int ObjectListIndex; internal int ObjectIndex; internal int FaceIndex; internal double Distance; internal Textures.OpenGlTextureWrapMode Wrap; } private class ObjectList { internal ObjectFace[] Faces; internal int FaceCount; internal ObjectList() { this.Faces = new ObjectFace[256]; this.FaceCount = 0; } } private class ObjectGroup { internal ObjectList List; internal int OpenGlDisplayList; internal bool OpenGlDisplayListAvailable; internal Vector3 WorldPosition; internal bool Update; internal ObjectGroup() { this.List = new ObjectList(); this.OpenGlDisplayList = 0; this.OpenGlDisplayListAvailable = false; this.WorldPosition = new Vector3(0.0, 0.0, 0.0); this.Update = true; } } // the static opaque lists /// The list of static opaque face groups. Each group contains only objects that are associated the respective group index. private static ObjectGroup[] StaticOpaque = new ObjectGroup[] { }; /// Whether to enforce updating all display lists. internal static bool StaticOpaqueForceUpdate = true; // all other lists /// The list of dynamic opaque faces to be rendered. private static ObjectList DynamicOpaque = new ObjectList(); /// The list of dynamic alpha faces to be rendered. private static ObjectList DynamicAlpha = new ObjectList(); /// The list of overlay opaque faces to be rendered. private static ObjectList OverlayOpaque = new ObjectList(); /// The list of overlay alpha faces to be rendered. private static ObjectList OverlayAlpha = new ObjectList(); // current opengl data private static int AlphaFuncComparison = 0; private static float AlphaFuncValue = 0.0f; private static bool AlphaTestEnabled = false; private static bool BlendEnabled = false; private static bool CullEnabled = true; internal static bool LightingEnabled = false; internal static bool FogEnabled = false; private static bool TexturingEnabled = false; private static bool EmissiveEnabled = false; // options internal static bool OptionLighting = true; internal static Color24 OptionAmbientColor = new Color24(160, 160, 160); internal static Color24 OptionDiffuseColor = new Color24(160, 160, 160); internal static World.Vector3Df OptionLightPosition = new World.Vector3Df(0.223606797749979f, 0.86602540378444f, -0.447213595499958f); internal static float OptionLightingResultingAmount = 1.0f; internal static bool OptionNormals = false; internal static bool OptionWireframe = false; internal static bool OptionBackfaceCulling = true; // interface options internal static bool OptionClock = false; internal enum SpeedDisplayMode { None, Kmph, Mph } internal static SpeedDisplayMode OptionSpeed = SpeedDisplayMode.None; internal static bool OptionFrameRates = false; internal static bool OptionBrakeSystems = false; // fade to black private static double FadeToBlackDueToChangeEnds = 0.0; // textures private static Textures.Texture TextureLogo = null; // constants private const float inv255 = 1.0f / 255.0f; // reset internal static void Reset() { LoadTexturesImmediately = LoadTextureImmediatelyMode.NotYet; Objects = new Object[256]; ObjectCount = 0; StaticOpaque = new ObjectGroup[] { }; StaticOpaqueForceUpdate = true; DynamicOpaque = new ObjectList(); DynamicAlpha = new ObjectList(); OverlayOpaque = new ObjectList(); OverlayAlpha = new ObjectList(); OptionLighting = true; OptionAmbientColor = new Color24(160, 160, 160); OptionDiffuseColor = new Color24(160, 160, 160); OptionLightPosition = new World.Vector3Df(0.223606797749979f, 0.86602540378444f, -0.447213595499958f); OptionLightingResultingAmount = 1.0f; OptionClock = false; OptionBrakeSystems = false; } // initialize internal static void Initialize() { // opengl Gl.glShadeModel(Gl.GL_SMOOTH); Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); Gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); Gl.glEnable(Gl.GL_DEPTH_TEST); Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA); Gl.glDepthFunc(Gl.GL_LEQUAL); Gl.glHint(Gl.GL_FOG_HINT, Gl.GL_FASTEST); Gl.glHint(Gl.GL_LINE_SMOOTH_HINT, Gl.GL_FASTEST); Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_FASTEST); Gl.glHint(Gl.GL_POINT_SMOOTH_HINT, Gl.GL_FASTEST); Gl.glHint(Gl.GL_POLYGON_SMOOTH_HINT, Gl.GL_FASTEST); Gl.glHint(Gl.GL_GENERATE_MIPMAP_HINT, Gl.GL_NICEST); Gl.glDisable(Gl.GL_DITHER); Gl.glCullFace(Gl.GL_FRONT); Gl.glEnable(Gl.GL_CULL_FACE); CullEnabled = true; Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; Gl.glDisable(Gl.GL_TEXTURE_2D); TexturingEnabled = false; // hud Interface.LoadHUD(); string Path = Program.FileSystem.GetDataFolder("In-game"); Textures.RegisterTexture(OpenBveApi.Path.CombineFile(Path, "logo.png"), out TextureLogo); // opengl Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); Gl.glPushMatrix(); Gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); Glu.gluLookAt(0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0); Gl.glPopMatrix(); // prepare rendering logo Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA); Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glPushMatrix(); Gl.glLoadIdentity(); Gl.glOrtho(0.0, (double)Screen.Width, 0.0, (double)Screen.Height, -1.0, 1.0); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); Gl.glLoadIdentity(); // render logo DrawLoadingScreen(); // finalize Gl.glPopMatrix(); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glPopMatrix(); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glDisable(Gl.GL_BLEND); } // deinitialize internal static void Deinitialize() { ClearDisplayLists(); } // clear display lists internal static void ClearDisplayLists() { for (int i = 0; i < StaticOpaque.Length; i++) { if (StaticOpaque[i] != null) { if (StaticOpaque[i].OpenGlDisplayListAvailable) { Gl.glDeleteLists(StaticOpaque[i].OpenGlDisplayList, 1); StaticOpaque[i].OpenGlDisplayListAvailable = false; } } } StaticOpaqueForceUpdate = true; } // initialize lighting internal static void InitializeLighting() { Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, new float[] { inv255 * (float)OptionAmbientColor.R, inv255 * (float)OptionAmbientColor.G, inv255 * (float)OptionAmbientColor.B, 1.0f }); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, new float[] { inv255 * (float)OptionDiffuseColor.R, inv255 * (float)OptionDiffuseColor.G, inv255 * (float)OptionDiffuseColor.B, 1.0f }); Gl.glLightModelfv(Gl.GL_LIGHT_MODEL_AMBIENT, new float[] { 0.0f, 0.0f, 0.0f, 1.0f }); Gl.glCullFace(Gl.GL_FRONT); CullEnabled = true; // possibly undocumented, but required for correct lighting Gl.glEnable(Gl.GL_LIGHT0); Gl.glEnable(Gl.GL_COLOR_MATERIAL); Gl.glColorMaterial(Gl.GL_FRONT_AND_BACK, Gl.GL_AMBIENT_AND_DIFFUSE); Gl.glShadeModel(Gl.GL_SMOOTH); float x = ((float)OptionAmbientColor.R + (float)OptionAmbientColor.G + (float)OptionAmbientColor.B); float y = ((float)OptionDiffuseColor.R + (float)OptionDiffuseColor.G + (float)OptionDiffuseColor.B); if (x < y) x = y; OptionLightingResultingAmount = 0.00208333333333333f * x; if (OptionLightingResultingAmount > 1.0f) OptionLightingResultingAmount = 1.0f; Gl.glEnable(Gl.GL_LIGHTING); LightingEnabled = true; Gl.glDepthFunc(Gl.GL_LEQUAL); } // reset opengl state private static void ResetOpenGlState() { LastBoundTexture = null; Gl.glEnable(Gl.GL_CULL_FACE); CullEnabled = true; Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; Gl.glDisable(Gl.GL_TEXTURE_2D); TexturingEnabled = false; Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA); Gl.glDisable(Gl.GL_BLEND); BlendEnabled = false; Gl.glEnable(Gl.GL_DEPTH_TEST); Gl.glDepthMask(Gl.GL_TRUE); Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION, new float[] { 0.0f, 0.0f, 0.0f, 1.0f }); EmissiveEnabled = false; SetAlphaFunc(Gl.GL_GREATER, 0.9f); } // render scene internal static byte[] PixelBuffer = null; internal static int PixelBufferOpenGlTextureIndex = 0; internal static void RenderScene(double TimeElapsed) { // initialize ResetOpenGlState(); int OpenGlTextureIndex = 0; if (World.CurrentBackground.Texture != null) { //Textures.LoadTexture(World.CurrentBackground.Texture, Textures.OpenGlTextureWrapMode.RepeatClamp); // TODO } if (OptionWireframe | OpenGlTextureIndex == 0) { if (Game.CurrentFog.Start < Game.CurrentFog.End) { const float fogdistance = 600.0f; float n = (fogdistance - Game.CurrentFog.Start) / (Game.CurrentFog.End - Game.CurrentFog.Start); float cr = n * inv255 * (float)Game.CurrentFog.Color.R; float cg = n * inv255 * (float)Game.CurrentFog.Color.G; float cb = n * inv255 * (float)Game.CurrentFog.Color.B; Gl.glClearColor(cr, cg, cb, 1.0f); } else { Gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); } else { Gl.glClear(Gl.GL_DEPTH_BUFFER_BIT); } Gl.glPushMatrix(); MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.ChangeToScenery); if (LoadTexturesImmediately == LoadTextureImmediatelyMode.NotYet) { LoadTexturesImmediately = LoadTextureImmediatelyMode.Yes; } // setup camera double cx = World.AbsoluteCameraPosition.X; double cy = World.AbsoluteCameraPosition.Y; double cz = World.AbsoluteCameraPosition.Z; double dx = World.AbsoluteCameraDirection.X; double dy = World.AbsoluteCameraDirection.Y; double dz = World.AbsoluteCameraDirection.Z; double ux = World.AbsoluteCameraUp.X; double uy = World.AbsoluteCameraUp.Y; double uz = World.AbsoluteCameraUp.Z; Glu.gluLookAt(0.0, 0.0, 0.0, dx, dy, dz, ux, uy, uz); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, new float[] { OptionLightPosition.X, OptionLightPosition.Y, OptionLightPosition.Z, 0.0f }); // fog double fd = Game.NextFog.TrackPosition - Game.PreviousFog.TrackPosition; if (fd != 0.0) { float fr = (float)((World.CameraTrackFollower.TrackPosition - Game.PreviousFog.TrackPosition) / fd); float frc = 1.0f - fr; Game.CurrentFog.Start = Game.PreviousFog.Start * frc + Game.NextFog.Start * fr; Game.CurrentFog.End = Game.PreviousFog.End * frc + Game.NextFog.End * fr; Game.CurrentFog.Color.R = (byte)((float)Game.PreviousFog.Color.R * frc + (float)Game.NextFog.Color.R * fr); Game.CurrentFog.Color.G = (byte)((float)Game.PreviousFog.Color.G * frc + (float)Game.NextFog.Color.G * fr); Game.CurrentFog.Color.B = (byte)((float)Game.PreviousFog.Color.B * frc + (float)Game.NextFog.Color.B * fr); } else { Game.CurrentFog = Game.PreviousFog; } // render background if (FogEnabled) { Gl.glDisable(Gl.GL_FOG); FogEnabled = false; } Gl.glDisable(Gl.GL_DEPTH_TEST); RenderBackground(dx, dy, dz, TimeElapsed); // fog double aa = Game.CurrentFog.Start; double bb = Game.CurrentFog.End; if (Game.CurrentFog.Start < Game.CurrentFog.End & Game.CurrentFog.Start < World.BackgroundImageDistance) { if (!FogEnabled) { Gl.glFogi(Gl.GL_FOG_MODE, Gl.GL_LINEAR); } Gl.glFogf(Gl.GL_FOG_START, Game.CurrentFog.Start); Gl.glFogf(Gl.GL_FOG_END, Game.CurrentFog.End); Gl.glFogfv(Gl.GL_FOG_COLOR, new float[] { inv255 * (float)Game.CurrentFog.Color.R, inv255 * (float)Game.CurrentFog.Color.G, inv255 * (float)Game.CurrentFog.Color.B, 1.0f }); if (!FogEnabled) { Gl.glEnable(Gl.GL_FOG); FogEnabled = true; } } else if (FogEnabled) { Gl.glDisable(Gl.GL_FOG); FogEnabled = false; } // world layer bool optionLighting = OptionLighting; LastBoundTexture = null; if (OptionLighting) { if (!LightingEnabled) { Gl.glEnable(Gl.GL_LIGHTING); LightingEnabled = true; } if (World.CameraRestriction == World.CameraRestrictionMode.NotAvailable) { Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, new float[] { inv255 * (float)OptionAmbientColor.R, inv255 * (float)OptionAmbientColor.G, inv255 * (float)OptionAmbientColor.B, 1.0f }); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, new float[] { inv255 * (float)OptionDiffuseColor.R, inv255 * (float)OptionDiffuseColor.G, inv255 * (float)OptionDiffuseColor.B, 1.0f }); } } else if (LightingEnabled) { Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; } // static opaque if (Interface.CurrentOptions.DisableDisplayLists) { ResetOpenGlState(); for (int i = 0; i < StaticOpaque.Length; i++) { if (StaticOpaque[i] != null) { if (StaticOpaque[i].List != null) { for (int j = 0; j < StaticOpaque[i].List.FaceCount; j++) { if (StaticOpaque[i].List.Faces[j] != null) { RenderFace(ref StaticOpaque[i].List.Faces[j], cx, cy, cz); } } } } } } else { for (int i = 0; i < StaticOpaque.Length; i++) { if (StaticOpaque[i] != null) { if (StaticOpaque[i].Update | StaticOpaqueForceUpdate) { StaticOpaque[i].Update = false; if (StaticOpaque[i].OpenGlDisplayListAvailable) { Gl.glDeleteLists(StaticOpaque[i].OpenGlDisplayList, 1); StaticOpaque[i].OpenGlDisplayListAvailable = false; } if (StaticOpaque[i].List.FaceCount != 0) { StaticOpaque[i].OpenGlDisplayList = Gl.glGenLists(1); StaticOpaque[i].OpenGlDisplayListAvailable = true; ResetOpenGlState(); Gl.glNewList(StaticOpaque[i].OpenGlDisplayList, Gl.GL_COMPILE); for (int j = 0; j < StaticOpaque[i].List.FaceCount; j++) { if (StaticOpaque[i].List.Faces[j] != null) { RenderFace(ref StaticOpaque[i].List.Faces[j], cx, cy, cz); } } Gl.glEndList(); } StaticOpaque[i].WorldPosition = World.AbsoluteCameraPosition; } } } StaticOpaqueForceUpdate = false; for (int i = 0; i < StaticOpaque.Length; i++) { if (StaticOpaque[i] != null) { if (StaticOpaque[i].OpenGlDisplayListAvailable) { ResetOpenGlState(); Gl.glPushMatrix(); Gl.glTranslated(StaticOpaque[i].WorldPosition.X - World.AbsoluteCameraPosition.X, StaticOpaque[i].WorldPosition.Y - World.AbsoluteCameraPosition.Y, StaticOpaque[i].WorldPosition.Z - World.AbsoluteCameraPosition.Z); Gl.glCallList(StaticOpaque[i].OpenGlDisplayList); Gl.glPopMatrix(); } } } } // dynamic opaque ResetOpenGlState(); for (int i = 0; i < DynamicOpaque.FaceCount; i++) { RenderFace(ref DynamicOpaque.Faces[i], cx, cy, cz); } // dynamic alpha ResetOpenGlState(); SortPolygons(DynamicAlpha); if (Interface.CurrentOptions.TransparencyMode == TransparencyMode.Performance) { Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; Gl.glDepthMask(Gl.GL_FALSE); SetAlphaFunc(Gl.GL_GREATER, 0.0f); for (int i = 0; i < DynamicAlpha.FaceCount; i++) { RenderFace(ref DynamicAlpha.Faces[i], cx, cy, cz); } } else { Gl.glDisable(Gl.GL_BLEND); BlendEnabled = false; SetAlphaFunc(Gl.GL_EQUAL, 1.0f); Gl.glDepthMask(Gl.GL_TRUE); for (int i = 0; i < DynamicAlpha.FaceCount; i++) { int r = (int)ObjectManager.Objects[DynamicAlpha.Faces[i].ObjectIndex].Mesh.Faces[DynamicAlpha.Faces[i].FaceIndex].Material; if (ObjectManager.Objects[DynamicAlpha.Faces[i].ObjectIndex].Mesh.Materials[r].BlendMode == World.MeshMaterialBlendMode.Normal & ObjectManager.Objects[DynamicAlpha.Faces[i].ObjectIndex].Mesh.Materials[r].GlowAttenuationData == 0) { if (ObjectManager.Objects[DynamicAlpha.Faces[i].ObjectIndex].Mesh.Materials[r].Color.A == 255) { RenderFace(ref DynamicAlpha.Faces[i], cx, cy, cz); } } } Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; SetAlphaFunc(Gl.GL_LESS, 1.0f); Gl.glDepthMask(Gl.GL_FALSE); bool additive = false; for (int i = 0; i < DynamicAlpha.FaceCount; i++) { int r = (int)ObjectManager.Objects[DynamicAlpha.Faces[i].ObjectIndex].Mesh.Faces[DynamicAlpha.Faces[i].FaceIndex].Material; if (ObjectManager.Objects[DynamicAlpha.Faces[i].ObjectIndex].Mesh.Materials[r].BlendMode == World.MeshMaterialBlendMode.Additive) { if (!additive) { UnsetAlphaFunc(); additive = true; } RenderFace(ref DynamicAlpha.Faces[i], cx, cy, cz); } else { if (additive) { SetAlphaFunc(Gl.GL_LESS, 1.0f); additive = false; } RenderFace(ref DynamicAlpha.Faces[i], cx, cy, cz); } } } // motion blur Gl.glDisable(Gl.GL_DEPTH_TEST); Gl.glDepthMask(Gl.GL_FALSE); SetAlphaFunc(Gl.GL_GREATER, 0.0f); if (Interface.CurrentOptions.MotionBlur != Interface.MotionBlurMode.None) { if (LightingEnabled) { Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; } RenderFullscreenMotionBlur(); } // overlay layer if (FogEnabled) { Gl.glDisable(Gl.GL_FOG); FogEnabled = false; } Gl.glLoadIdentity(); MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.ChangeToCab); Glu.gluLookAt(0.0, 0.0, 0.0, dx, dy, dz, ux, uy, uz); if (World.CameraRestriction == World.CameraRestrictionMode.NotAvailable) { // 3d cab Gl.glDepthMask(Gl.GL_TRUE); Gl.glEnable(Gl.GL_DEPTH_TEST); Gl.glClear(Gl.GL_DEPTH_BUFFER_BIT); if (!LightingEnabled) { Gl.glEnable(Gl.GL_LIGHTING); LightingEnabled = true; } OptionLighting = true; Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, new float[] { 0.6f, 0.6f, 0.6f, 1.0f }); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, new float[] { 0.6f, 0.6f, 0.6f, 1.0f }); // overlay opaque SetAlphaFunc(Gl.GL_GREATER, 0.9f); for (int i = 0; i < OverlayOpaque.FaceCount; i++) { RenderFace(ref OverlayOpaque.Faces[i], cx, cy, cz); } // overlay alpha SortPolygons(OverlayAlpha); if (Interface.CurrentOptions.TransparencyMode == TransparencyMode.Performance) { Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; Gl.glDepthMask(Gl.GL_FALSE); SetAlphaFunc(Gl.GL_GREATER, 0.0f); for (int i = 0; i < OverlayAlpha.FaceCount; i++) { RenderFace(ref OverlayAlpha.Faces[i], cx, cy, cz); } } else { Gl.glDisable(Gl.GL_BLEND); BlendEnabled = false; SetAlphaFunc(Gl.GL_EQUAL, 1.0f); Gl.glDepthMask(Gl.GL_TRUE); for (int i = 0; i < OverlayAlpha.FaceCount; i++) { int r = (int)ObjectManager.Objects[OverlayAlpha.Faces[i].ObjectIndex].Mesh.Faces[OverlayAlpha.Faces[i].FaceIndex].Material; if (ObjectManager.Objects[OverlayAlpha.Faces[i].ObjectIndex].Mesh.Materials[r].BlendMode == World.MeshMaterialBlendMode.Normal & ObjectManager.Objects[OverlayAlpha.Faces[i].ObjectIndex].Mesh.Materials[r].GlowAttenuationData == 0) { if (ObjectManager.Objects[OverlayAlpha.Faces[i].ObjectIndex].Mesh.Materials[r].Color.A == 255) { RenderFace(ref OverlayAlpha.Faces[i], cx, cy, cz); } } } Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; SetAlphaFunc(Gl.GL_LESS, 1.0f); Gl.glDepthMask(Gl.GL_FALSE); bool additive = false; for (int i = 0; i < OverlayAlpha.FaceCount; i++) { int r = (int)ObjectManager.Objects[OverlayAlpha.Faces[i].ObjectIndex].Mesh.Faces[OverlayAlpha.Faces[i].FaceIndex].Material; if (ObjectManager.Objects[OverlayAlpha.Faces[i].ObjectIndex].Mesh.Materials[r].BlendMode == World.MeshMaterialBlendMode.Additive) { if (!additive) { UnsetAlphaFunc(); additive = true; } RenderFace(ref OverlayAlpha.Faces[i], cx, cy, cz); } else { if (additive) { SetAlphaFunc(Gl.GL_LESS, 1.0f); additive = false; } RenderFace(ref OverlayAlpha.Faces[i], cx, cy, cz); } } } } else { // not a 3d cab if (LightingEnabled) { Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = true; } OptionLighting = false; if (!BlendEnabled) { Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; } Gl.glDepthMask(Gl.GL_FALSE); Gl.glDisable(Gl.GL_DEPTH_TEST); UnsetAlphaFunc(); SortPolygons(OverlayAlpha); for (int i = 0; i < OverlayAlpha.FaceCount; i++) { RenderFace(ref OverlayAlpha.Faces[i], cx, cy, cz); } } // render overlays OptionLighting = optionLighting; if (LightingEnabled) { Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; } if (FogEnabled) { Gl.glDisable(Gl.GL_FOG); FogEnabled = false; } if (BlendEnabled) { Gl.glDisable(Gl.GL_BLEND); BlendEnabled = false; } UnsetAlphaFunc(); Gl.glDisable(Gl.GL_DEPTH_TEST); RenderOverlays(TimeElapsed); // finalize rendering Gl.glPopMatrix(); LoadTexturesImmediately = LoadTextureImmediatelyMode.NoLonger; } // set alpha func private static void SetAlphaFunc(int Comparison, float Value) { AlphaTestEnabled = true; AlphaFuncComparison = Comparison; AlphaFuncValue = Value; Gl.glAlphaFunc(Comparison, Value); Gl.glEnable(Gl.GL_ALPHA_TEST); } private static void UnsetAlphaFunc() { AlphaTestEnabled = false; Gl.glDisable(Gl.GL_ALPHA_TEST); } private static void RestoreAlphaFunc() { if (AlphaTestEnabled) { Gl.glAlphaFunc(AlphaFuncComparison, AlphaFuncValue); Gl.glEnable(Gl.GL_ALPHA_TEST); } else { Gl.glDisable(Gl.GL_ALPHA_TEST); } } // render face private static Textures.OpenGlTexture LastBoundTexture = null; private static void RenderFace(ref ObjectFace Face, double CameraX, double CameraY, double CameraZ) { if (CullEnabled) { if (!OptionBackfaceCulling || (ObjectManager.Objects[Face.ObjectIndex].Mesh.Faces[Face.FaceIndex].Flags & World.MeshFace.Face2Mask) != 0) { Gl.glDisable(Gl.GL_CULL_FACE); CullEnabled = false; } } else if (OptionBackfaceCulling) { if ((ObjectManager.Objects[Face.ObjectIndex].Mesh.Faces[Face.FaceIndex].Flags & World.MeshFace.Face2Mask) == 0) { Gl.glEnable(Gl.GL_CULL_FACE); CullEnabled = true; } } int r = (int)ObjectManager.Objects[Face.ObjectIndex].Mesh.Faces[Face.FaceIndex].Material; RenderFace(ref ObjectManager.Objects[Face.ObjectIndex].Mesh.Materials[r], ObjectManager.Objects[Face.ObjectIndex].Mesh.Vertices, Face.Wrap, ref ObjectManager.Objects[Face.ObjectIndex].Mesh.Faces[Face.FaceIndex], CameraX, CameraY, CameraZ); } private static void RenderFace(ref World.MeshMaterial Material, World.Vertex[] Vertices, Textures.OpenGlTextureWrapMode wrap, ref World.MeshFace Face, double CameraX, double CameraY, double CameraZ) { // texture if (Material.DaytimeTexture != null) { if (Textures.LoadTexture(Material.DaytimeTexture, wrap)) { if (!TexturingEnabled) { Gl.glEnable(Gl.GL_TEXTURE_2D); TexturingEnabled = true; } if (Material.DaytimeTexture.OpenGlTextures[(int)wrap] != LastBoundTexture) { Gl.glBindTexture(Gl.GL_TEXTURE_2D, Material.DaytimeTexture.OpenGlTextures[(int)wrap].Name); LastBoundTexture = Material.DaytimeTexture.OpenGlTextures[(int)wrap]; } } else { if (TexturingEnabled) { Gl.glDisable(Gl.GL_TEXTURE_2D); TexturingEnabled = false; LastBoundTexture = null; } } } else { if (TexturingEnabled) { Gl.glDisable(Gl.GL_TEXTURE_2D); TexturingEnabled = false; LastBoundTexture = null; } } // blend mode float factor; if (Material.BlendMode == World.MeshMaterialBlendMode.Additive) { factor = 1.0f; if (!BlendEnabled) Gl.glEnable(Gl.GL_BLEND); Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE); if (FogEnabled) { Gl.glDisable(Gl.GL_FOG); } } else if (Material.NighttimeTexture == null) { float blend = inv255 * (float)Material.DaytimeNighttimeBlend + 1.0f - OptionLightingResultingAmount; if (blend > 1.0f) blend = 1.0f; factor = 1.0f - 0.8f * blend; } else { factor = 1.0f; } if (Material.NighttimeTexture != null) { if (LightingEnabled) { Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; } } else { if (OptionLighting & !LightingEnabled) { Gl.glEnable(Gl.GL_LIGHTING); LightingEnabled = true; } } // render daytime polygon int FaceType = Face.Flags & World.MeshFace.FaceTypeMask; switch (FaceType) { case World.MeshFace.FaceTypeTriangles: Gl.glBegin(Gl.GL_TRIANGLES); break; case World.MeshFace.FaceTypeTriangleStrip: Gl.glBegin(Gl.GL_TRIANGLE_STRIP); break; case World.MeshFace.FaceTypeQuads: Gl.glBegin(Gl.GL_QUADS); break; case World.MeshFace.FaceTypeQuadStrip: Gl.glBegin(Gl.GL_QUAD_STRIP); break; default: Gl.glBegin(Gl.GL_POLYGON); break; } if (Material.GlowAttenuationData != 0) { float alphafactor = (float)GetDistanceFactor(Vertices, ref Face, Material.GlowAttenuationData, CameraX, CameraY, CameraZ); Gl.glColor4f(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); } else { Gl.glColor4f(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A); } if ((Material.Flags & World.MeshMaterial.EmissiveColorMask) != 0) { Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION, new float[] { inv255 * (float)Material.EmissiveColor.R, inv255 * (float)Material.EmissiveColor.G, inv255 * (float)Material.EmissiveColor.B, 1.0f }); EmissiveEnabled = true; } else if (EmissiveEnabled) { Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION, new float[] { 0.0f, 0.0f, 0.0f, 1.0f }); EmissiveEnabled = false; } if (Material.DaytimeTexture != null) { if (LightingEnabled) { for (int j = 0; j < Face.Vertices.Length; j++) { Gl.glNormal3f(Face.Vertices[j].Normal.X, Face.Vertices[j].Normal.Y, Face.Vertices[j].Normal.Z); Gl.glTexCoord2f(Vertices[Face.Vertices[j].Index].TextureCoordinates.X, Vertices[Face.Vertices[j].Index].TextureCoordinates.Y); Gl.glVertex3f((float)(Vertices[Face.Vertices[j].Index].Coordinates.X - CameraX), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Y - CameraY), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Z - CameraZ)); } } else { for (int j = 0; j < Face.Vertices.Length; j++) { Gl.glTexCoord2f(Vertices[Face.Vertices[j].Index].TextureCoordinates.X, Vertices[Face.Vertices[j].Index].TextureCoordinates.Y); Gl.glVertex3f((float)(Vertices[Face.Vertices[j].Index].Coordinates.X - CameraX), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Y - CameraY), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Z - CameraZ)); } } } else { if (LightingEnabled) { for (int j = 0; j < Face.Vertices.Length; j++) { Gl.glNormal3f(Face.Vertices[j].Normal.X, Face.Vertices[j].Normal.Y, Face.Vertices[j].Normal.Z); Gl.glVertex3f((float)(Vertices[Face.Vertices[j].Index].Coordinates.X - CameraX), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Y - CameraY), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Z - CameraZ)); } } else { for (int j = 0; j < Face.Vertices.Length; j++) { Gl.glVertex3f((float)(Vertices[Face.Vertices[j].Index].Coordinates.X - CameraX), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Y - CameraY), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Z - CameraZ)); } } } Gl.glEnd(); // render nighttime polygon if (Material.NighttimeTexture != null && Textures.LoadTexture(Material.NighttimeTexture, wrap)) { if (!TexturingEnabled) { Gl.glEnable(Gl.GL_TEXTURE_2D); TexturingEnabled = true; } if (!BlendEnabled) { Gl.glEnable(Gl.GL_BLEND); } Gl.glBindTexture(Gl.GL_TEXTURE_2D, Material.NighttimeTexture.OpenGlTextures[(int)wrap].Name); LastBoundTexture = null; Gl.glAlphaFunc(Gl.GL_GREATER, 0.0f); Gl.glEnable(Gl.GL_ALPHA_TEST); switch (FaceType) { case World.MeshFace.FaceTypeTriangles: Gl.glBegin(Gl.GL_TRIANGLES); break; case World.MeshFace.FaceTypeTriangleStrip: Gl.glBegin(Gl.GL_TRIANGLE_STRIP); break; case World.MeshFace.FaceTypeQuads: Gl.glBegin(Gl.GL_QUADS); break; case World.MeshFace.FaceTypeQuadStrip: Gl.glBegin(Gl.GL_QUAD_STRIP); break; default: Gl.glBegin(Gl.GL_POLYGON); break; } float alphafactor; if (Material.GlowAttenuationData != 0) { alphafactor = (float)GetDistanceFactor(Vertices, ref Face, Material.GlowAttenuationData, CameraX, CameraY, CameraZ); float blend = inv255 * (float)Material.DaytimeNighttimeBlend + 1.0f - OptionLightingResultingAmount; if (blend > 1.0f) blend = 1.0f; alphafactor *= blend; } else { alphafactor = inv255 * (float)Material.DaytimeNighttimeBlend + 1.0f - OptionLightingResultingAmount; if (alphafactor > 1.0f) alphafactor = 1.0f; } Gl.glColor4f(inv255 * (float)Material.Color.R * factor, inv255 * Material.Color.G * factor, inv255 * (float)Material.Color.B * factor, inv255 * (float)Material.Color.A * alphafactor); if ((Material.Flags & World.MeshMaterial.EmissiveColorMask) != 0) { Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION, new float[] { inv255 * (float)Material.EmissiveColor.R, inv255 * (float)Material.EmissiveColor.G, inv255 * (float)Material.EmissiveColor.B, 1.0f }); EmissiveEnabled = true; } else if (EmissiveEnabled) { Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION, new float[] { 0.0f, 0.0f, 0.0f, 1.0f }); EmissiveEnabled = false; } for (int j = 0; j < Face.Vertices.Length; j++) { Gl.glTexCoord2f(Vertices[Face.Vertices[j].Index].TextureCoordinates.X, Vertices[Face.Vertices[j].Index].TextureCoordinates.Y); Gl.glVertex3f((float)(Vertices[Face.Vertices[j].Index].Coordinates.X - CameraX), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Y - CameraY), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Z - CameraZ)); } Gl.glEnd(); RestoreAlphaFunc(); if (!BlendEnabled) { Gl.glDisable(Gl.GL_BLEND); } } // normals if (OptionNormals) { if (TexturingEnabled) { Gl.glDisable(Gl.GL_TEXTURE_2D); TexturingEnabled = false; } for (int j = 0; j < Face.Vertices.Length; j++) { Gl.glBegin(Gl.GL_LINES); Gl.glColor4f(inv255 * (float)Material.Color.R, inv255 * (float)Material.Color.G, inv255 * (float)Material.Color.B, 1.0f); Gl.glVertex3f((float)(Vertices[Face.Vertices[j].Index].Coordinates.X - CameraX), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Y - CameraY), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Z - CameraZ)); Gl.glVertex3f((float)(Vertices[Face.Vertices[j].Index].Coordinates.X + Face.Vertices[j].Normal.X - CameraX), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Y + Face.Vertices[j].Normal.Y - CameraY), (float)(Vertices[Face.Vertices[j].Index].Coordinates.Z + Face.Vertices[j].Normal.Z - CameraZ)); Gl.glEnd(); } } // finalize if (Material.BlendMode == World.MeshMaterialBlendMode.Additive) { Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA); if (!BlendEnabled) Gl.glDisable(Gl.GL_BLEND); if (FogEnabled) { Gl.glEnable(Gl.GL_FOG); } } } // render background private static void RenderBackground(double dx, double dy, double dz, double TimeElapsed) { const float scale = 0.5f; // fog const float fogdistance = 600.0f; if (Game.CurrentFog.Start < Game.CurrentFog.End & Game.CurrentFog.Start < fogdistance) { float cr = inv255 * (float)Game.CurrentFog.Color.R; float cg = inv255 * (float)Game.CurrentFog.Color.G; float cb = inv255 * (float)Game.CurrentFog.Color.B; if (!FogEnabled) { Gl.glFogi(Gl.GL_FOG_MODE, Gl.GL_LINEAR); } float ratio = (float)World.BackgroundImageDistance / fogdistance; Gl.glFogf(Gl.GL_FOG_START, Game.CurrentFog.Start * ratio * scale); Gl.glFogf(Gl.GL_FOG_END, Game.CurrentFog.End * ratio * scale); Gl.glFogfv(Gl.GL_FOG_COLOR, new float[] { cr, cg, cb, 1.0f }); if (!FogEnabled) { Gl.glEnable(Gl.GL_FOG); FogEnabled = true; } } else if (FogEnabled) { Gl.glDisable(Gl.GL_FOG); FogEnabled = false; } // render if (World.TargetBackgroundCountdown >= 0.0) { // fade World.TargetBackgroundCountdown -= TimeElapsed; if (World.TargetBackgroundCountdown < 0.0) { World.CurrentBackground = World.TargetBackground; World.TargetBackgroundCountdown = -1.0; RenderBackground(World.CurrentBackground, dx, dy, dz, 1.0f, scale); } else { RenderBackground(World.CurrentBackground, dx, dy, dz, 1.0f, scale); SetAlphaFunc(Gl.GL_GREATER, 0.0f); // ### float Alpha = (float)(1.0 - World.TargetBackgroundCountdown / World.TargetBackgroundDefaultCountdown); RenderBackground(World.TargetBackground, dx, dy, dz, Alpha, scale); } } else { // single RenderBackground(World.CurrentBackground, dx, dy, dz, 1.0f, scale); } } private static void RenderBackground(World.Background Data, double dx, double dy, double dz, float Alpha, float scale) { if (Data.Texture != null && Textures.LoadTexture(Data.Texture, Textures.OpenGlTextureWrapMode.RepeatClamp)) { if (LightingEnabled) { Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; } if (!TexturingEnabled) { Gl.glEnable(Gl.GL_TEXTURE_2D); TexturingEnabled = true; } if (Alpha == 1.0f) { if (BlendEnabled) { Gl.glDisable(Gl.GL_BLEND); BlendEnabled = false; } } else if (!BlendEnabled) { Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; } Gl.glBindTexture(Gl.GL_TEXTURE_2D, Data.Texture.OpenGlTextures[(int)Textures.OpenGlTextureWrapMode.RepeatClamp].Name); Gl.glColor4f(1.0f, 1.0f, 1.0f, Alpha); float y0, y1; if (Data.KeepAspectRatio) { int tw = Data.Texture.Width; int th = Data.Texture.Height; double hh = Math.PI * World.BackgroundImageDistance * (double)th / ((double)tw * (double)Data.Repetition); y0 = (float)(-0.5 * hh); y1 = (float)(1.5 * hh); } else { y0 = (float)(-0.125 * World.BackgroundImageDistance); y1 = (float)(0.375 * World.BackgroundImageDistance); } const int n = 32; World.Vector3Df[] bottom = new World.Vector3Df[n]; World.Vector3Df[] top = new World.Vector3Df[n]; double angleValue = 2.61799387799149 - 3.14159265358979 / (double)n; double angleIncrement = 6.28318530717958 / (double)n; /* * To ensure that the whole background cylinder is rendered inside the viewing frustum, * the background is rendered before the scene with z-buffer writes disabled. Then, * the actual distance from the camera is irrelevant as long as it is inside the frustum. * */ for (int i = 0; i < n; i++) { float x = (float)(World.BackgroundImageDistance * Math.Cos(angleValue)); float z = (float)(World.BackgroundImageDistance * Math.Sin(angleValue)); bottom[i] = new World.Vector3Df(scale * x, scale * y0, scale * z); top[i] = new World.Vector3Df(scale * x, scale * y1, scale * z); angleValue += angleIncrement; } float textureStart = 0.5f * (float)Data.Repetition / (float)n; float textureIncrement = -(float)Data.Repetition / (float)n; double textureX = textureStart; for (int i = 0; i < n; i++) { int j = (i + 1) % n; // side wall Gl.glBegin(Gl.GL_QUADS); Gl.glTexCoord2d(textureX, 0.005f); Gl.glVertex3f(top[i].X, top[i].Y, top[i].Z); Gl.glTexCoord2d(textureX, 0.995f); Gl.glVertex3f(bottom[i].X, bottom[i].Y, bottom[i].Z); Gl.glTexCoord2d(textureX + textureIncrement, 0.995f); Gl.glVertex3f(bottom[j].X, bottom[j].Y, bottom[j].Z); Gl.glTexCoord2d(textureX + textureIncrement, 0.005f); Gl.glVertex3f(top[j].X, top[j].Y, top[j].Z); Gl.glEnd(); // top cap Gl.glBegin(Gl.GL_TRIANGLES); Gl.glTexCoord2d(textureX, 0.005f); Gl.glVertex3f(top[i].X, top[i].Y, top[i].Z); Gl.glTexCoord2d(textureX + textureIncrement, 0.005f); Gl.glVertex3f(top[j].X, top[j].Y, top[j].Z); Gl.glTexCoord2d(textureX + 0.5 * textureIncrement, 0.1f); Gl.glVertex3f(0.0f, top[i].Y, 0.0f); // bottom cap Gl.glTexCoord2d(textureX + 0.5 * textureIncrement, 0.9f); Gl.glVertex3f(0.0f, bottom[i].Y, 0.0f); Gl.glTexCoord2d(textureX + textureIncrement, 0.995f); Gl.glVertex3f(bottom[j].X, bottom[j].Y, bottom[j].Z); Gl.glTexCoord2d(textureX, 0.995f); Gl.glVertex3f(bottom[i].X, bottom[i].Y, bottom[i].Z); Gl.glEnd(); // finish textureX += textureIncrement; } Gl.glDisable(Gl.GL_TEXTURE_2D); TexturingEnabled = false; if (!BlendEnabled) { Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; } } } // render fullscreen motion blur private static void RenderFullscreenMotionBlur() { int w = Interface.CurrentOptions.NoTextureResize ? Screen.Width : Textures.RoundUpToPowerOfTwo(Screen.Width); int h = Interface.CurrentOptions.NoTextureResize ? Screen.Height : Textures.RoundUpToPowerOfTwo(Screen.Height); // render if (PixelBufferOpenGlTextureIndex >= 0) { double strength; switch (Interface.CurrentOptions.MotionBlur) { case Interface.MotionBlurMode.Low: strength = 0.0025; break; case Interface.MotionBlurMode.Medium: strength = 0.0040; break; case Interface.MotionBlurMode.High: strength = 0.0064; break; default: strength = 0.0040; break; } double speed = Math.Abs(World.CameraSpeed); double denominator = strength * Game.InfoFrameRate * Math.Sqrt(speed); float factor; if (denominator > 0.001) { factor = (float)Math.Exp(-1.0 / denominator); } else { factor = 0.0f; } // initialize Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA); if (!BlendEnabled) { Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; } if (LightingEnabled) { Gl.glDisable(Gl.GL_LIGHTING); LightingEnabled = false; } Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glPushMatrix(); Gl.glLoadIdentity(); Gl.glOrtho(0.0, (double)Screen.Width, 0.0, (double)Screen.Height, -1.0, 1.0); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); Gl.glLoadIdentity(); if (!TexturingEnabled) { Gl.glEnable(Gl.GL_TEXTURE_2D); TexturingEnabled = true; } // render Gl.glBindTexture(Gl.GL_TEXTURE_2D, PixelBufferOpenGlTextureIndex); Gl.glColor4f(1.0f, 1.0f, 1.0f, factor); Gl.glBegin(Gl.GL_POLYGON); Gl.glTexCoord2d(0.0, 0.0); Gl.glVertex2d(0.0, 0.0); Gl.glTexCoord2d(0.0, 1.0); Gl.glVertex2d(0.0, (double)h); Gl.glTexCoord2d(1.0, 1.0); Gl.glVertex2d((double)w, (double)h); Gl.glTexCoord2d(1.0, 0.0); Gl.glVertex2d((double)w, 0.0); Gl.glEnd(); // finalize Gl.glPopMatrix(); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glPopMatrix(); Gl.glMatrixMode(Gl.GL_MODELVIEW); } // retrieve buffer { Gl.glBindTexture(Gl.GL_TEXTURE_2D, PixelBufferOpenGlTextureIndex); Gl.glCopyTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, 0, 0, w, h, 0); } } // lamps private enum LampType { None, Ats, AtsOperation, AtsPPower, AtsPPattern, AtsPBrakeOverride, AtsPBrakeOperation, AtsP, AtsPFailure, Atc, AtcPower, AtcUse, AtcEmergency, Eb, ConstSpeed } private struct Lamp { internal LampType Type; internal string Text; internal float Width; internal float Height; internal Lamp(LampType Type) { this.Type = Type; switch (Type) { case LampType.None: this.Text = null; break; case LampType.Ats: this.Text = Interface.GetInterfaceString("lamps_ats"); break; case LampType.AtsOperation: this.Text = Interface.GetInterfaceString("lamps_atsoperation"); break; case LampType.AtsPPower: this.Text = Interface.GetInterfaceString("lamps_atsppower"); break; case LampType.AtsPPattern: this.Text = Interface.GetInterfaceString("lamps_atsppattern"); break; case LampType.AtsPBrakeOverride: this.Text = Interface.GetInterfaceString("lamps_atspbrakeoverride"); break; case LampType.AtsPBrakeOperation: this.Text = Interface.GetInterfaceString("lamps_atspbrakeoperation"); break; case LampType.AtsP: this.Text = Interface.GetInterfaceString("lamps_atsp"); break; case LampType.AtsPFailure: this.Text = Interface.GetInterfaceString("lamps_atspfailure"); break; case LampType.Atc: this.Text = Interface.GetInterfaceString("lamps_atc"); break; case LampType.AtcPower: this.Text = Interface.GetInterfaceString("lamps_atcpower"); break; case LampType.AtcUse: this.Text = Interface.GetInterfaceString("lamps_atcuse"); break; case LampType.AtcEmergency: this.Text = Interface.GetInterfaceString("lamps_atcemergency"); break; case LampType.Eb: this.Text = Interface.GetInterfaceString("lamps_eb"); break; case LampType.ConstSpeed: this.Text = Interface.GetInterfaceString("lamps_constspeed"); break; default: this.Text = "TEXT"; break; } Fonts.OpenGlFont font = Fonts.NormalFont; for (int i = 0; i < Interface.CurrentHudElements.Length; i++) { if (Interface.CurrentHudElements[i].Subject.Equals("ats", StringComparison.OrdinalIgnoreCase)) { font = Interface.CurrentHudElements[i].Font; break; } } System.Drawing.Size size = MeasureString(font, this.Text); this.Width = size.Width; this.Height = size.Height; } } private struct LampCollection { internal Lamp[] Lamps; internal float Width; } private static LampCollection CurrentLampCollection; private static void InitializeLamps() { bool atsSn = (TrainManager.PlayerTrain.Specs.DefaultSafetySystems & TrainManager.DefaultSafetySystems.AtsSn) != 0; bool atsP = (TrainManager.PlayerTrain.Specs.DefaultSafetySystems & TrainManager.DefaultSafetySystems.AtsP) != 0; bool atc = (TrainManager.PlayerTrain.Specs.DefaultSafetySystems & TrainManager.DefaultSafetySystems.Atc) != 0; bool eb = (TrainManager.PlayerTrain.Specs.DefaultSafetySystems & TrainManager.DefaultSafetySystems.Eb) != 0; CurrentLampCollection.Width = 0.0f; CurrentLampCollection.Lamps = new Lamp[17]; int Count; if (TrainManager.PlayerTrain.Plugin == null || !TrainManager.PlayerTrain.Plugin.IsDefault) { Count = 0; } else if (atsP & atc) { CurrentLampCollection.Lamps[0] = new Lamp(LampType.Ats); CurrentLampCollection.Lamps[1] = new Lamp(LampType.AtsOperation); CurrentLampCollection.Lamps[2] = new Lamp(LampType.None); CurrentLampCollection.Lamps[3] = new Lamp(LampType.AtsPPower); CurrentLampCollection.Lamps[4] = new Lamp(LampType.AtsPPattern); CurrentLampCollection.Lamps[5] = new Lamp(LampType.AtsPBrakeOverride); CurrentLampCollection.Lamps[6] = new Lamp(LampType.AtsPBrakeOperation); CurrentLampCollection.Lamps[7] = new Lamp(LampType.AtsP); CurrentLampCollection.Lamps[8] = new Lamp(LampType.AtsPFailure); CurrentLampCollection.Lamps[9] = new Lamp(LampType.None); CurrentLampCollection.Lamps[10] = new Lamp(LampType.Atc); CurrentLampCollection.Lamps[11] = new Lamp(LampType.AtcPower); CurrentLampCollection.Lamps[12] = new Lamp(LampType.AtcUse); CurrentLampCollection.Lamps[13] = new Lamp(LampType.AtcEmergency); Count = 14; } else if (atsP) { CurrentLampCollection.Lamps[0] = new Lamp(LampType.Ats); CurrentLampCollection.Lamps[1] = new Lamp(LampType.AtsOperation); CurrentLampCollection.Lamps[2] = new Lamp(LampType.None); CurrentLampCollection.Lamps[3] = new Lamp(LampType.AtsPPower); CurrentLampCollection.Lamps[4] = new Lamp(LampType.AtsPPattern); CurrentLampCollection.Lamps[5] = new Lamp(LampType.AtsPBrakeOverride); CurrentLampCollection.Lamps[6] = new Lamp(LampType.AtsPBrakeOperation); CurrentLampCollection.Lamps[7] = new Lamp(LampType.AtsP); CurrentLampCollection.Lamps[8] = new Lamp(LampType.AtsPFailure); Count = 9; } else if (atsSn & atc) { CurrentLampCollection.Lamps[0] = new Lamp(LampType.Ats); CurrentLampCollection.Lamps[1] = new Lamp(LampType.AtsOperation); CurrentLampCollection.Lamps[2] = new Lamp(LampType.None); CurrentLampCollection.Lamps[3] = new Lamp(LampType.Atc); CurrentLampCollection.Lamps[4] = new Lamp(LampType.AtcPower); CurrentLampCollection.Lamps[5] = new Lamp(LampType.AtcUse); CurrentLampCollection.Lamps[6] = new Lamp(LampType.AtcEmergency); Count = 7; } else if (atc) { CurrentLampCollection.Lamps[0] = new Lamp(LampType.Atc); CurrentLampCollection.Lamps[1] = new Lamp(LampType.AtcPower); CurrentLampCollection.Lamps[2] = new Lamp(LampType.AtcUse); CurrentLampCollection.Lamps[3] = new Lamp(LampType.AtcEmergency); Count = 4; } else if (atsSn) { CurrentLampCollection.Lamps[0] = new Lamp(LampType.Ats); CurrentLampCollection.Lamps[1] = new Lamp(LampType.AtsOperation); Count = 2; } else { Count = 0; } if (TrainManager.PlayerTrain.Plugin != null && TrainManager.PlayerTrain.Plugin.IsDefault) { if (Count != 0 & (eb | TrainManager.PlayerTrain.Specs.HasConstSpeed)) { CurrentLampCollection.Lamps[Count] = new Lamp(LampType.None); Count++; } if (eb) { CurrentLampCollection.Lamps[Count] = new Lamp(LampType.Eb); Count++; } if (TrainManager.PlayerTrain.Specs.HasConstSpeed) { CurrentLampCollection.Lamps[Count] = new Lamp(LampType.ConstSpeed); Count++; } } Array.Resize(ref CurrentLampCollection.Lamps, Count); for (int i = 0; i < Count; i++) { if (CurrentLampCollection.Lamps[i].Width > CurrentLampCollection.Width) { CurrentLampCollection.Width = CurrentLampCollection.Lamps[i].Width; } } } // render overlays private static void RenderOverlays(double TimeElapsed) { // initialize Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA); Gl.glEnable(Gl.GL_BLEND); BlendEnabled = true; Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glPushMatrix(); Gl.glLoadIdentity(); Gl.glOrtho(0.0, (double)Screen.Width, (double)Screen.Height, 0.0, -1.0, 1.0); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); Gl.glLoadIdentity(); System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; if (CurrentOutputMode == OutputMode.Default) { // hud TrainManager.TrainDoorState LeftDoors = TrainManager.GetDoorsState(TrainManager.PlayerTrain, true, false); TrainManager.TrainDoorState RightDoors = TrainManager.GetDoorsState(TrainManager.PlayerTrain, false, true); for (int i = 0; i < Interface.CurrentHudElements.Length; i++) { string Command = Interface.CurrentHudElements[i].Subject.ToLowerInvariant(); switch (Command) { case "messages": { // messages int n = Game.Messages.Length; float totalwidth = 16.0f; float[] widths = new float[n]; float[] heights = new float[n]; for (int j = 0; j < n; j++) { System.Drawing.Size size = MeasureString(Interface.CurrentHudElements[i].Font, Game.Messages[j].DisplayText); widths[j] = size.Width; heights[j] = size.Height; float a = widths[j] - j * Interface.CurrentHudElements[i].Value1; if (a > totalwidth) totalwidth = a; } Game.MessagesRendererSize.X += 16.0 * TimeElapsed * ((double)totalwidth - Game.MessagesRendererSize.X); totalwidth = (float)Game.MessagesRendererSize.X; double lcrh = 0.0; /// left width/height double lw = 0.0; if (Interface.CurrentHudElements[i].TopLeft.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].TopLeft.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].TopLeft.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].TopLeft.BackgroundTexture.Height; if (u > lw) lw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture.Height; if (u > lw) lw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture.Height; if (u > lw) lw = u; if (v > lcrh) lcrh = v; } } /// center height if (Interface.CurrentHudElements[i].TopMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].TopMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Interface.CurrentHudElements[i].TopMiddle.BackgroundTexture.Height; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture.Height; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].BottomMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].BottomMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Interface.CurrentHudElements[i].BottomMiddle.BackgroundTexture.Height; if (v > lcrh) lcrh = v; } } /// right width/height double rw = 0.0; if (Interface.CurrentHudElements[i].TopRight.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].TopRight.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].TopRight.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].TopRight.BackgroundTexture.Height; if (u > rw) rw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].CenterRight.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterRight.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].CenterRight.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].CenterRight.BackgroundTexture.Height; if (u > rw) rw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].BottomRight.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].BottomRight.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].BottomRight.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].BottomRight.BackgroundTexture.Height; if (u > rw) rw = u; if (v > lcrh) lcrh = v; } } /// start double w = totalwidth + lw + rw; double h = Interface.CurrentHudElements[i].Value2 * n; double x = Interface.CurrentHudElements[i].Alignment.X < 0 ? 0.0 : Interface.CurrentHudElements[i].Alignment.X > 0 ? Screen.Width - w : 0.5 * (Screen.Width - w); double y = Interface.CurrentHudElements[i].Alignment.Y < 0 ? 0.0 : Interface.CurrentHudElements[i].Alignment.Y > 0 ? Screen.Height - h : 0.5 * (Screen.Height - h); x += Interface.CurrentHudElements[i].Position.X; y += Interface.CurrentHudElements[i].Position.Y; int m = 0; for (int j = 0; j < n; j++) { float br, bg, bb, ba; CreateBackColor(Interface.CurrentHudElements[i].BackgroundColor, Game.Messages[j].Color, out br, out bg, out bb, out ba); float tr, tg, tb, ta; CreateTextColor(Interface.CurrentHudElements[i].TextColor, Game.Messages[j].Color, out tr, out tg, out tb, out ta); float or, og, ob, oa; CreateBackColor(Interface.CurrentHudElements[i].OverlayColor, Game.Messages[j].Color, out or, out og, out ob, out oa); double tx, ty; bool preserve = false; if ((Interface.CurrentHudElements[i].Transition & Interface.HudTransition.Move) != 0) { if (Game.SecondsSinceMidnight < Game.Messages[j].Timeout) { if (Game.Messages[j].RendererAlpha == 0.0) { Game.Messages[j].RendererPosition.X = x + Interface.CurrentHudElements[i].TransitionVector.X; Game.Messages[j].RendererPosition.Y = y + Interface.CurrentHudElements[i].TransitionVector.Y; Game.Messages[j].RendererAlpha = 1.0; } tx = x; ty = y + m * (Interface.CurrentHudElements[i].Value2); preserve = true; } else if (Interface.CurrentHudElements[i].Transition == Interface.HudTransition.MoveAndFade) { tx = x; ty = y + m * (Interface.CurrentHudElements[i].Value2); } else { tx = x + Interface.CurrentHudElements[i].TransitionVector.X; ty = y + (j + 1) * Interface.CurrentHudElements[i].TransitionVector.Y; } const double speed = 2.0; double dx = (speed * Math.Abs(tx - Game.Messages[j].RendererPosition.X) + 0.1) * TimeElapsed; double dy = (speed * Math.Abs(ty - Game.Messages[j].RendererPosition.Y) + 0.1) * TimeElapsed; if (Math.Abs(tx - Game.Messages[j].RendererPosition.X) < dx) { Game.Messages[j].RendererPosition.X = tx; } else { Game.Messages[j].RendererPosition.X += Math.Sign(tx - Game.Messages[j].RendererPosition.X) * dx; } if (Math.Abs(ty - Game.Messages[j].RendererPosition.Y) < dy) { Game.Messages[j].RendererPosition.Y = ty; } else { Game.Messages[j].RendererPosition.Y += Math.Sign(ty - Game.Messages[j].RendererPosition.Y) * dy; } } else { tx = x; ty = y + m * (Interface.CurrentHudElements[i].Value2); Game.Messages[j].RendererPosition.X = 0.0; const double speed = 12.0; double dy = (speed * Math.Abs(ty - Game.Messages[j].RendererPosition.Y) + 0.1) * TimeElapsed; Game.Messages[j].RendererPosition.X = x; if (Math.Abs(ty - Game.Messages[j].RendererPosition.Y) < dy) { Game.Messages[j].RendererPosition.Y = ty; } else { Game.Messages[j].RendererPosition.Y += Math.Sign(ty - Game.Messages[j].RendererPosition.Y) * dy; } } if ((Interface.CurrentHudElements[i].Transition & Interface.HudTransition.Fade) != 0) { if (Game.SecondsSinceMidnight >= Game.Messages[j].Timeout) { Game.Messages[j].RendererAlpha -= TimeElapsed; if (Game.Messages[j].RendererAlpha < 0.0) Game.Messages[j].RendererAlpha = 0.0; } else { Game.Messages[j].RendererAlpha += TimeElapsed; if (Game.Messages[j].RendererAlpha > 1.0) Game.Messages[j].RendererAlpha = 1.0; preserve = true; } } else if (Game.SecondsSinceMidnight > Game.Messages[j].Timeout) { if (Math.Abs(Game.Messages[j].RendererPosition.X - tx) < 0.1 & Math.Abs(Game.Messages[j].RendererPosition.Y - ty) < 0.1) { Game.Messages[j].RendererAlpha = 0.0; } } if (preserve) m++; double px = Game.Messages[j].RendererPosition.X + (double)j * (double)Interface.CurrentHudElements[i].Value1; double py = Game.Messages[j].RendererPosition.Y; float alpha = (float)(Game.Messages[j].RendererAlpha * Game.Messages[j].RendererAlpha); /// graphics Interface.HudImage Left = j == 0 ? Interface.CurrentHudElements[i].TopLeft : j < n - 1 ? Interface.CurrentHudElements[i].CenterLeft : Interface.CurrentHudElements[i].BottomLeft; Interface.HudImage Middle = j == 0 ? Interface.CurrentHudElements[i].TopMiddle : j < n - 1 ? Interface.CurrentHudElements[i].CenterMiddle : Interface.CurrentHudElements[i].BottomMiddle; Interface.HudImage Right = j == 0 ? Interface.CurrentHudElements[i].TopRight : j < n - 1 ? Interface.CurrentHudElements[i].CenterRight : Interface.CurrentHudElements[i].BottomRight; /// left background if (Left.BackgroundTexture != null) { if (Textures.LoadTexture(Left.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Left.BackgroundTexture.Width; double v = (double)Left.BackgroundTexture.Height; Gl.glColor4f(br, bg, bb, ba * alpha); RenderOverlayTexture(Left.BackgroundTexture, px, py, px + u, py + v); } } /// right background if (Right.BackgroundTexture != null) { if (Textures.LoadTexture(Right.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Right.BackgroundTexture.Width; double v = (double)Right.BackgroundTexture.Height; Gl.glColor4f(br, bg, bb, ba * alpha); RenderOverlayTexture(Right.BackgroundTexture, px + w - u, py, px + w, py + v); } } /// middle background if (Middle.BackgroundTexture != null) { if (Textures.LoadTexture(Middle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Middle.BackgroundTexture.Height; Gl.glColor4f(br, bg, bb, ba * alpha); RenderOverlayTexture(Middle.BackgroundTexture, px + lw, py, px + w - rw, py + v); } } { /// text string t = Game.Messages[j].DisplayText; double u = widths[j]; double v = heights[j]; double p = Math.Round((Interface.CurrentHudElements[i].TextAlignment.X < 0 ? px : Interface.CurrentHudElements[i].TextAlignment.X > 0 ? px + w - u : px + 0.5 * (w - u)) - j * Interface.CurrentHudElements[i].Value1); double q = Math.Round(Interface.CurrentHudElements[i].TextAlignment.Y < 0 ? py : Interface.CurrentHudElements[i].TextAlignment.Y > 0 ? py + lcrh - v : py + 0.5 * (lcrh - v)); p += Interface.CurrentHudElements[i].TextPosition.X; q += Interface.CurrentHudElements[i].TextPosition.Y; DrawString(Interface.CurrentHudElements[i].Font, t, new System.Drawing.Point((int)p, (int)q), TextAlignment.TopLeft, new Color128(tr, tg, tb, ta * alpha), Interface.CurrentHudElements[i].TextShadow); } /// left overlay if (Left.OverlayTexture != null) { if (Textures.LoadTexture(Left.OverlayTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Left.OverlayTexture.Width; double v = (double)Left.OverlayTexture.Height; Gl.glColor4f(or, og, ob, oa * alpha); RenderOverlayTexture(Left.OverlayTexture, px, py, px + u, py + v); } } /// right overlay if (Right.OverlayTexture != null) { if (Textures.LoadTexture(Right.OverlayTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Right.OverlayTexture.Width; double v = (double)Right.OverlayTexture.Height; Gl.glColor4f(or, og, ob, oa * alpha); RenderOverlayTexture(Right.OverlayTexture, px + w - u, py, px + w, py + v); } } /// middle overlay if (Middle.OverlayTexture != null) { if (Textures.LoadTexture(Middle.OverlayTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Middle.OverlayTexture.Height; Gl.glColor4f(or, og, ob, oa * alpha); RenderOverlayTexture(Middle.OverlayTexture, px + lw, py, px + w - rw, py + v); } } } } break; case "scoremessages": { // score messages int n = Game.ScoreMessages.Length; float totalwidth = 16.0f; float[] widths = new float[n]; float[] heights = new float[n]; for (int j = 0; j < n; j++) { System.Drawing.Size size = MeasureString(Interface.CurrentHudElements[i].Font, Game.ScoreMessages[j].Text); widths[j] = size.Width; heights[j] = size.Height; float a = widths[j] - j * Interface.CurrentHudElements[i].Value1; if (a > totalwidth) totalwidth = a; } Game.ScoreMessagesRendererSize.X += 16.0 * TimeElapsed * ((double)totalwidth - Game.ScoreMessagesRendererSize.X); totalwidth = (float)Game.ScoreMessagesRendererSize.X; double lcrh = 0.0; /// left width/height double lw = 0.0; if (Interface.CurrentHudElements[i].TopLeft.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].TopLeft.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].TopLeft.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].TopLeft.BackgroundTexture.Height; if (u > lw) lw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture.Height; if (u > lw) lw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture.Height; if (u > lw) lw = u; if (v > lcrh) lcrh = v; } } /// center height if (Interface.CurrentHudElements[i].TopMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].TopMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Interface.CurrentHudElements[i].TopMiddle.BackgroundTexture.Height; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture.Height; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].BottomMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].BottomMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Interface.CurrentHudElements[i].BottomMiddle.BackgroundTexture.Height; if (v > lcrh) lcrh = v; } } /// right width/height double rw = 0.0; if (Interface.CurrentHudElements[i].TopRight.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].TopRight.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].TopRight.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].TopRight.BackgroundTexture.Height; if (u > rw) rw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].CenterRight.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterRight.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].CenterRight.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].CenterRight.BackgroundTexture.Height; if (u > rw) rw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].BottomRight.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].BottomRight.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].BottomRight.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].BottomRight.BackgroundTexture.Height; if (u > rw) rw = u; if (v > lcrh) lcrh = v; } } /// start double w = Interface.CurrentHudElements[i].Alignment.X == 0 ? lw + rw + 128 : totalwidth + lw + rw; double h = Interface.CurrentHudElements[i].Value2 * n; double x = Interface.CurrentHudElements[i].Alignment.X < 0 ? 0.0 : Interface.CurrentHudElements[i].Alignment.X > 0 ? Screen.Width - w : 0.5 * (Screen.Width - w); double y = Interface.CurrentHudElements[i].Alignment.Y < 0 ? 0.0 : Interface.CurrentHudElements[i].Alignment.Y > 0 ? Screen.Height - h : 0.5 * (Screen.Height - h); x += Interface.CurrentHudElements[i].Position.X; y += Interface.CurrentHudElements[i].Position.Y; int m = 0; for (int j = 0; j < n; j++) { float br, bg, bb, ba; CreateBackColor(Interface.CurrentHudElements[i].BackgroundColor, Game.ScoreMessages[j].Color, out br, out bg, out bb, out ba); float tr, tg, tb, ta; CreateTextColor(Interface.CurrentHudElements[i].TextColor, Game.ScoreMessages[j].Color, out tr, out tg, out tb, out ta); float or, og, ob, oa; CreateBackColor(Interface.CurrentHudElements[i].OverlayColor, Game.ScoreMessages[j].Color, out or, out og, out ob, out oa); double tx, ty; bool preserve = false; if ((Interface.CurrentHudElements[i].Transition & Interface.HudTransition.Move) != 0) { if (Game.SecondsSinceMidnight < Game.ScoreMessages[j].Timeout) { if (Game.ScoreMessages[j].RendererAlpha == 0.0) { Game.ScoreMessages[j].RendererPosition.X = x + Interface.CurrentHudElements[i].TransitionVector.X; Game.ScoreMessages[j].RendererPosition.Y = y + Interface.CurrentHudElements[i].TransitionVector.Y; Game.ScoreMessages[j].RendererAlpha = 1.0; } tx = x; ty = y + m * (Interface.CurrentHudElements[i].Value2); preserve = true; } else if (Interface.CurrentHudElements[i].Transition == Interface.HudTransition.MoveAndFade) { tx = x; ty = y + m * (Interface.CurrentHudElements[i].Value2); } else { tx = x + Interface.CurrentHudElements[i].TransitionVector.X; ty = y + (j + 1) * Interface.CurrentHudElements[i].TransitionVector.Y; } const double speed = 2.0; double dx = (speed * Math.Abs(tx - Game.ScoreMessages[j].RendererPosition.X) + 0.1) * TimeElapsed; double dy = (speed * Math.Abs(ty - Game.ScoreMessages[j].RendererPosition.Y) + 0.1) * TimeElapsed; if (Math.Abs(tx - Game.ScoreMessages[j].RendererPosition.X) < dx) { Game.ScoreMessages[j].RendererPosition.X = tx; } else { Game.ScoreMessages[j].RendererPosition.X += Math.Sign(tx - Game.ScoreMessages[j].RendererPosition.X) * dx; } if (Math.Abs(ty - Game.ScoreMessages[j].RendererPosition.Y) < dy) { Game.ScoreMessages[j].RendererPosition.Y = ty; } else { Game.ScoreMessages[j].RendererPosition.Y += Math.Sign(ty - Game.ScoreMessages[j].RendererPosition.Y) * dy; } } else { tx = x; ty = y + m * (Interface.CurrentHudElements[i].Value2); Game.ScoreMessages[j].RendererPosition.X = 0.0; const double speed = 12.0; double dy = (speed * Math.Abs(ty - Game.ScoreMessages[j].RendererPosition.Y) + 0.1) * TimeElapsed; Game.ScoreMessages[j].RendererPosition.X = x; if (Math.Abs(ty - Game.ScoreMessages[j].RendererPosition.Y) < dy) { Game.ScoreMessages[j].RendererPosition.Y = ty; } else { Game.ScoreMessages[j].RendererPosition.Y += Math.Sign(ty - Game.ScoreMessages[j].RendererPosition.Y) * dy; } } if ((Interface.CurrentHudElements[i].Transition & Interface.HudTransition.Fade) != 0) { if (Game.SecondsSinceMidnight >= Game.ScoreMessages[j].Timeout) { Game.ScoreMessages[j].RendererAlpha -= TimeElapsed; if (Game.ScoreMessages[j].RendererAlpha < 0.0) Game.ScoreMessages[j].RendererAlpha = 0.0; } else { Game.ScoreMessages[j].RendererAlpha += TimeElapsed; if (Game.ScoreMessages[j].RendererAlpha > 1.0) Game.ScoreMessages[j].RendererAlpha = 1.0; preserve = true; } } else if (Game.SecondsSinceMidnight > Game.ScoreMessages[j].Timeout) { if (Math.Abs(Game.ScoreMessages[j].RendererPosition.X - tx) < 0.1 & Math.Abs(Game.ScoreMessages[j].RendererPosition.Y - ty) < 0.1) { Game.ScoreMessages[j].RendererAlpha = 0.0; } } if (preserve) m++; double px = Game.ScoreMessages[j].RendererPosition.X + (double)j * (double)Interface.CurrentHudElements[i].Value1; double py = Game.ScoreMessages[j].RendererPosition.Y; float alpha = (float)(Game.ScoreMessages[j].RendererAlpha * Game.ScoreMessages[j].RendererAlpha); /// graphics Interface.HudImage Left = j == 0 ? Interface.CurrentHudElements[i].TopLeft : j < n - 1 ? Interface.CurrentHudElements[i].CenterLeft : Interface.CurrentHudElements[i].BottomLeft; Interface.HudImage Middle = j == 0 ? Interface.CurrentHudElements[i].TopMiddle : j < n - 1 ? Interface.CurrentHudElements[i].CenterMiddle : Interface.CurrentHudElements[i].BottomMiddle; Interface.HudImage Right = j == 0 ? Interface.CurrentHudElements[i].TopRight : j < n - 1 ? Interface.CurrentHudElements[i].CenterRight : Interface.CurrentHudElements[i].BottomRight; /// left background if (Left.BackgroundTexture != null) { if (Textures.LoadTexture(Left.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Left.BackgroundTexture.Width; double v = (double)Left.BackgroundTexture.Height; Gl.glColor4f(br, bg, bb, ba * alpha); RenderOverlayTexture(Left.BackgroundTexture, px, py, px + u, py + v); } } /// right background if (Right.BackgroundTexture != null) { if (Textures.LoadTexture(Right.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Right.BackgroundTexture.Width; double v = (double)Right.BackgroundTexture.Height; Gl.glColor4f(br, bg, bb, ba * alpha); RenderOverlayTexture(Right.BackgroundTexture, px + w - u, py, px + w, py + v); } } /// middle background if (Middle.BackgroundTexture != null) { if (Textures.LoadTexture(Middle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Middle.BackgroundTexture.Height; Gl.glColor4f(br, bg, bb, ba * alpha); RenderOverlayTexture(Middle.BackgroundTexture, px + lw, py, px + w - rw, py + v); } } { /// text string t = Game.ScoreMessages[j].Text; double u = widths[j]; double v = heights[j]; double p = Math.Round((Interface.CurrentHudElements[i].TextAlignment.X < 0 ? px : Interface.CurrentHudElements[i].TextAlignment.X > 0 ? px + w - u : px + 0.5 * (w - u)) - j * Interface.CurrentHudElements[i].Value1); double q = Math.Round(Interface.CurrentHudElements[i].TextAlignment.Y < 0 ? py : Interface.CurrentHudElements[i].TextAlignment.Y > 0 ? py + lcrh - v : py + 0.5 * (lcrh - v)); p += Interface.CurrentHudElements[i].TextPosition.X; q += Interface.CurrentHudElements[i].TextPosition.Y; DrawString(Interface.CurrentHudElements[i].Font, t, new System.Drawing.Point((int)p, (int)q), TextAlignment.TopLeft, new Color128(tr, tg, tb, ta * alpha), Interface.CurrentHudElements[i].TextShadow); } /// left overlay if (Left.OverlayTexture != null) { if (Textures.LoadTexture(Left.OverlayTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Left.OverlayTexture.Width; double v = (double)Left.OverlayTexture.Height; Gl.glColor4f(or, og, ob, oa * alpha); RenderOverlayTexture(Left.OverlayTexture, px, py, px + u, py + v); } } /// right overlay if (Right.OverlayTexture != null) { if (Textures.LoadTexture(Right.OverlayTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Right.OverlayTexture.Width; double v = (double)Right.OverlayTexture.Height; Gl.glColor4f(or, og, ob, oa * alpha); RenderOverlayTexture(Right.OverlayTexture, px + w - u, py, px + w, py + v); } } /// middle overlay if (Middle.OverlayTexture != null) { if (Textures.LoadTexture(Middle.OverlayTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Middle.OverlayTexture.Height; Gl.glColor4f(or, og, ob, oa * alpha); RenderOverlayTexture(Middle.OverlayTexture, px + lw, py, px + w - rw, py + v); } } } } break; case "ats": { // ats lamps if (CurrentLampCollection.Lamps == null) { InitializeLamps(); } double lcrh = 0.0; /// left width/height double lw = 0.0; if (Interface.CurrentHudElements[i].TopLeft.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].TopLeft.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].TopLeft.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].TopLeft.BackgroundTexture.Height; if (u > lw) lw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].CenterLeft.BackgroundTexture.Height; if (u > lw) lw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].BottomLeft.BackgroundTexture.Height; if (u > lw) lw = u; if (v > lcrh) lcrh = v; } } /// center height if (Interface.CurrentHudElements[i].TopMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].TopMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Interface.CurrentHudElements[i].TopMiddle.BackgroundTexture.Height; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture.Height; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].BottomMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].BottomMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Interface.CurrentHudElements[i].BottomMiddle.BackgroundTexture.Height; if (v > lcrh) lcrh = v; } } /// right width/height double rw = 0.0; if (Interface.CurrentHudElements[i].TopRight.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].TopRight.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].TopRight.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].TopRight.BackgroundTexture.Height; if (u > rw) rw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].CenterRight.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterRight.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].CenterRight.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].CenterRight.BackgroundTexture.Height; if (u > rw) rw = u; if (v > lcrh) lcrh = v; } } if (Interface.CurrentHudElements[i].BottomRight.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].BottomRight.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Interface.CurrentHudElements[i].BottomRight.BackgroundTexture.Width; double v = (double)Interface.CurrentHudElements[i].BottomRight.BackgroundTexture.Height; if (u > rw) rw = u; if (v > lcrh) lcrh = v; } } /// start int n = CurrentLampCollection.Lamps.Length; double w = (double)CurrentLampCollection.Width + lw + rw; double h = Interface.CurrentHudElements[i].Value2 * n; double x = Interface.CurrentHudElements[i].Alignment.X < 0 ? 0.0 : Interface.CurrentHudElements[i].Alignment.X > 0 ? Screen.Width - w : 0.5 * (Screen.Width - w); double y = Interface.CurrentHudElements[i].Alignment.Y < 0 ? 0.0 : Interface.CurrentHudElements[i].Alignment.Y > 0 ? Screen.Height - h : 0.5 * (Screen.Height - h); x += Interface.CurrentHudElements[i].Position.X; y += Interface.CurrentHudElements[i].Position.Y; for (int j = 0; j < n; j++) { if (CurrentLampCollection.Lamps[j].Type != LampType.None) { int o; if (j == 0) { o = -1; } else if (CurrentLampCollection.Lamps[j - 1].Type == LampType.None) { o = -1; } else if (j < n - 1 && CurrentLampCollection.Lamps[j + 1].Type == LampType.None) { o = 1; } else if (j == n - 1) { o = 1; } else { o = 0; } Interface.HudImage Left = o < 0 ? Interface.CurrentHudElements[i].TopLeft : o == 0 ? Interface.CurrentHudElements[i].CenterLeft : Interface.CurrentHudElements[i].BottomLeft; Interface.HudImage Middle = o < 0 ? Interface.CurrentHudElements[i].TopMiddle : o == 0 ? Interface.CurrentHudElements[i].CenterMiddle : Interface.CurrentHudElements[i].BottomMiddle; Interface.HudImage Right = o < 0 ? Interface.CurrentHudElements[i].TopRight : o == 0 ? Interface.CurrentHudElements[i].CenterRight : Interface.CurrentHudElements[i].BottomRight; Game.MessageColor sc = Game.MessageColor.Gray; if (TrainManager.PlayerTrain.Plugin.Panel.Length >= 272) { switch (CurrentLampCollection.Lamps[j].Type) { case LampType.Ats: if (TrainManager.PlayerTrain.Plugin.Panel[256] != 0) { sc = Game.MessageColor.Orange; } break; case LampType.AtsOperation: if (TrainManager.PlayerTrain.Plugin.Panel[258] != 0) { sc = Game.MessageColor.Red; } break; case LampType.AtsPPower: if (TrainManager.PlayerTrain.Plugin.Panel[259] != 0) { sc = Game.MessageColor.Green; } break; case LampType.AtsPPattern: if (TrainManager.PlayerTrain.Plugin.Panel[260] != 0) { sc = Game.MessageColor.Orange; } break; case LampType.AtsPBrakeOverride: if (TrainManager.PlayerTrain.Plugin.Panel[261] != 0) { sc = Game.MessageColor.Orange; } break; case LampType.AtsPBrakeOperation: if (TrainManager.PlayerTrain.Plugin.Panel[262] != 0) { sc = Game.MessageColor.Orange; } break; case LampType.AtsP: if (TrainManager.PlayerTrain.Plugin.Panel[263] != 0) { sc = Game.MessageColor.Green; } break; case LampType.AtsPFailure: if (TrainManager.PlayerTrain.Plugin.Panel[264] != 0) { sc = Game.MessageColor.Red; } break; case LampType.Atc: if (TrainManager.PlayerTrain.Plugin.Panel[265] != 0) { sc = Game.MessageColor.Orange; } break; case LampType.AtcPower: if (TrainManager.PlayerTrain.Plugin.Panel[266] != 0) { sc = Game.MessageColor.Orange; } break; case LampType.AtcUse: if (TrainManager.PlayerTrain.Plugin.Panel[267] != 0) { sc = Game.MessageColor.Orange; } break; case LampType.AtcEmergency: if (TrainManager.PlayerTrain.Plugin.Panel[268] != 0) { sc = Game.MessageColor.Red; } break; case LampType.Eb: if (TrainManager.PlayerTrain.Plugin.Panel[270] != 0) { sc = Game.MessageColor.Green; } break; case LampType.ConstSpeed: if (TrainManager.PlayerTrain.Plugin.Panel[269] != 0) { sc = Game.MessageColor.Orange; } break; } } /// colors float br, bg, bb, ba; CreateBackColor(Interface.CurrentHudElements[i].BackgroundColor, sc, out br, out bg, out bb, out ba); float tr, tg, tb, ta; CreateTextColor(Interface.CurrentHudElements[i].TextColor, sc, out tr, out tg, out tb, out ta); float or, og, ob, oa; CreateBackColor(Interface.CurrentHudElements[i].OverlayColor, sc, out or, out og, out ob, out oa); /// left background if (Left.BackgroundTexture != null) { if (Textures.LoadTexture(Left.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Left.BackgroundTexture.Width; double v = (double)Left.BackgroundTexture.Height; Gl.glColor4f(br, bg, bb, ba); RenderOverlayTexture(Left.BackgroundTexture, x, y, x + u, y + v); } } /// right background if (Right.BackgroundTexture != null) { if (Textures.LoadTexture(Right.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Right.BackgroundTexture.Width; double v = (double)Right.BackgroundTexture.Height; Gl.glColor4f(br, bg, bb, ba); RenderOverlayTexture(Right.BackgroundTexture, x + w - u, y, x + w, y + v); } } /// middle background if (Middle.BackgroundTexture != null) { if (Textures.LoadTexture(Middle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Middle.BackgroundTexture.Height; Gl.glColor4f(br, bg, bb, ba); RenderOverlayTexture(Middle.BackgroundTexture, x + lw, y, x + w - rw, y + v); } } { /// text string t = CurrentLampCollection.Lamps[j].Text; double u = CurrentLampCollection.Lamps[j].Width; double v = CurrentLampCollection.Lamps[j].Height; double p = Math.Round(Interface.CurrentHudElements[i].TextAlignment.X < 0 ? x : Interface.CurrentHudElements[i].TextAlignment.X > 0 ? x + w - u : x + 0.5 * (w - u)); double q = Math.Round(Interface.CurrentHudElements[i].TextAlignment.Y < 0 ? y : Interface.CurrentHudElements[i].TextAlignment.Y > 0 ? y + lcrh - v : y + 0.5 * (lcrh - v)); p += Interface.CurrentHudElements[i].TextPosition.X; q += Interface.CurrentHudElements[i].TextPosition.Y; DrawString(Interface.CurrentHudElements[i].Font, t, new System.Drawing.Point((int)p, (int)q), TextAlignment.TopLeft, new Color128(tr, tg, tb, ta), Interface.CurrentHudElements[i].TextShadow); } /// left overlay if (Left.OverlayTexture != null) { if (Textures.LoadTexture(Left.OverlayTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Left.OverlayTexture.Width; double v = (double)Left.OverlayTexture.Height; Gl.glColor4f(or, og, ob, oa); RenderOverlayTexture(Left.OverlayTexture, x, y, x + u, y + v); } } /// right overlay if (Right.OverlayTexture != null) { if (Textures.LoadTexture(Right.OverlayTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double u = (double)Right.OverlayTexture.Width; double v = (double)Right.OverlayTexture.Height; Gl.glColor4f(or, og, ob, oa); RenderOverlayTexture(Right.OverlayTexture, x + w - u, y, x + w, y + v); } } /// middle overlay if (Middle.OverlayTexture != null) { if (Textures.LoadTexture(Middle.OverlayTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { double v = (double)Middle.OverlayTexture.Height; Gl.glColor4f(or, og, ob, oa); RenderOverlayTexture(Middle.OverlayTexture, x + lw, y, x + w - rw, y + v); } } } y += (double)Interface.CurrentHudElements[i].Value2; } } break; default: { // default double w, h; if (Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { w = (double)Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture.Width; h = (double)Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture.Height; } else { w = 0.0; h = 0.0; } } else { w = 0.0; h = 0.0; } double x = Interface.CurrentHudElements[i].Alignment.X < 0 ? 0.0 : Interface.CurrentHudElements[i].Alignment.X == 0 ? 0.5 * (Screen.Width - w) : Screen.Width - w; double y = Interface.CurrentHudElements[i].Alignment.Y < 0 ? 0.0 : Interface.CurrentHudElements[i].Alignment.Y == 0 ? 0.5 * (Screen.Height - h) : Screen.Height - h; x += Interface.CurrentHudElements[i].Position.X; y += Interface.CurrentHudElements[i].Position.Y; /// command const double speed = 1.0; Game.MessageColor sc = Game.MessageColor.None; string t; switch (Command) { case "reverser": if (TrainManager.PlayerTrain.Specs.CurrentReverser.Driver < 0) { sc = Game.MessageColor.Orange; t = Interface.QuickReferences.HandleBackward; } else if (TrainManager.PlayerTrain.Specs.CurrentReverser.Driver > 0) { sc = Game.MessageColor.Blue; t = Interface.QuickReferences.HandleForward; } else { sc = Game.MessageColor.Gray; t = Interface.QuickReferences.HandleNeutral; } Interface.CurrentHudElements[i].TransitionState = 0.0; break; case "power": if (TrainManager.PlayerTrain.Specs.SingleHandle) { continue; } else if (TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver == 0) { sc = Game.MessageColor.Gray; t = Interface.QuickReferences.HandlePowerNull; } else { sc = Game.MessageColor.Blue; t = Interface.QuickReferences.HandlePower + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver.ToString(Culture); } Interface.CurrentHudElements[i].TransitionState = 0.0; break; case "brake": if (TrainManager.PlayerTrain.Specs.SingleHandle) { continue; } else if (TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { if (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver) { sc = Game.MessageColor.Red; t = Interface.QuickReferences.HandleEmergency; } else if (TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Release) { sc = Game.MessageColor.Gray; t = Interface.QuickReferences.HandleRelease; } else if (TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap) { sc = Game.MessageColor.Blue; t = Interface.QuickReferences.HandleLap; } else { sc = Game.MessageColor.Orange; t = Interface.QuickReferences.HandleService; } } else { if (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver) { sc = Game.MessageColor.Red; t = Interface.QuickReferences.HandleEmergency; } else if (TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { sc = Game.MessageColor.Green; t = Interface.QuickReferences.HandleHoldBrake; } else if (TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver == 0) { sc = Game.MessageColor.Gray; t = Interface.QuickReferences.HandleBrakeNull; } else { sc = Game.MessageColor.Orange; t = Interface.QuickReferences.HandleBrake + TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver.ToString(Culture); } } Interface.CurrentHudElements[i].TransitionState = 0.0; break; case "single": if (!TrainManager.PlayerTrain.Specs.SingleHandle) { continue; } else if (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver) { sc = Game.MessageColor.Red; t = Interface.QuickReferences.HandleEmergency; } else if (TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver) { sc = Game.MessageColor.Green; t = Interface.QuickReferences.HandleHoldBrake; } else if (TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver > 0) { sc = Game.MessageColor.Orange; t = Interface.QuickReferences.HandleBrake + TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver.ToString(Culture); } else if (TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver > 0) { sc = Game.MessageColor.Blue; t = Interface.QuickReferences.HandlePower + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver.ToString(Culture); } else { sc = Game.MessageColor.Gray; t = Interface.QuickReferences.HandlePowerNull; } Interface.CurrentHudElements[i].TransitionState = 0.0; break; case "doorsleft": case "doorsright": { if ((LeftDoors & TrainManager.TrainDoorState.AllClosed) == 0 | (RightDoors & TrainManager.TrainDoorState.AllClosed) == 0) { Interface.CurrentHudElements[i].TransitionState -= speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState < 0.0) Interface.CurrentHudElements[i].TransitionState = 0.0; } else { Interface.CurrentHudElements[i].TransitionState += speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState > 1.0) Interface.CurrentHudElements[i].TransitionState = 1.0; } TrainManager.TrainDoorState Doors = Command == "doorsleft" ? LeftDoors : RightDoors; if ((Doors & TrainManager.TrainDoorState.Mixed) != 0) { sc = Game.MessageColor.Orange; } else if ((Doors & TrainManager.TrainDoorState.AllClosed) != 0) { sc = Game.MessageColor.Gray; } else if (TrainManager.PlayerTrain.Specs.DoorCloseMode == TrainManager.DoorMode.Manual) { sc = Game.MessageColor.Green; } else { sc = Game.MessageColor.Blue; } t = Command == "doorsleft" ? Interface.QuickReferences.DoorsLeft : Interface.QuickReferences.DoorsRight; } break; case "stopleft": case "stopright": case "stopnone": { int s = TrainManager.PlayerTrain.Station; if (s >= 0 && Game.PlayerStopsAtStation(s) && Interface.CurrentOptions.GameMode != Interface.GameMode.Expert) { bool cond; if (Command == "stopleft") { cond = Game.Stations[s].OpenLeftDoors; } else if (Command == "stopright") { cond = Game.Stations[s].OpenRightDoors; } else { cond = !Game.Stations[s].OpenLeftDoors & !Game.Stations[s].OpenRightDoors; } if (TrainManager.PlayerTrain.StationState == TrainManager.TrainStopState.Pending & cond) { Interface.CurrentHudElements[i].TransitionState -= speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState < 0.0) Interface.CurrentHudElements[i].TransitionState = 0.0; } else { Interface.CurrentHudElements[i].TransitionState += speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState > 1.0) Interface.CurrentHudElements[i].TransitionState = 1.0; } } else { Interface.CurrentHudElements[i].TransitionState += speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState > 1.0) Interface.CurrentHudElements[i].TransitionState = 1.0; } t = Interface.CurrentHudElements[i].Text; } break; case "stoplefttick": case "stoprighttick": case "stopnonetick": { int s = TrainManager.PlayerTrain.Station; if (s >= 0 && Game.PlayerStopsAtStation(s) && Interface.CurrentOptions.GameMode != Interface.GameMode.Expert) { int c = Game.GetStopIndex(s, TrainManager.PlayerTrain.Cars.Length); if (c >= 0) { bool cond; if (Command == "stoplefttick") { cond = Game.Stations[s].OpenLeftDoors; } else if (Command == "stoprighttick") { cond = Game.Stations[s].OpenRightDoors; } else { cond = !Game.Stations[s].OpenLeftDoors & !Game.Stations[s].OpenRightDoors; } if (TrainManager.PlayerTrain.StationState == TrainManager.TrainStopState.Pending & cond) { Interface.CurrentHudElements[i].TransitionState -= speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState < 0.0) Interface.CurrentHudElements[i].TransitionState = 0.0; } else { Interface.CurrentHudElements[i].TransitionState += speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState > 1.0) Interface.CurrentHudElements[i].TransitionState = 1.0; } double d = TrainManager.PlayerTrain.StationDistanceToStopPoint; double r; if (d > 0.0) { r = d / Game.Stations[s].Stops[c].BackwardTolerance; } else { r = d / Game.Stations[s].Stops[c].ForwardTolerance; } if (r < -1.0) r = -1.0; if (r > 1.0) r = 1.0; y -= r * (double)Interface.CurrentHudElements[i].Value1; } else { Interface.CurrentHudElements[i].TransitionState += speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState > 1.0) Interface.CurrentHudElements[i].TransitionState = 1.0; } } else { Interface.CurrentHudElements[i].TransitionState += speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState > 1.0) Interface.CurrentHudElements[i].TransitionState = 1.0; } t = Interface.CurrentHudElements[i].Text; } break; case "clock": { int hours = (int)Math.Floor(Game.SecondsSinceMidnight); int seconds = hours % 60; hours /= 60; int minutes = hours % 60; hours /= 60; hours %= 24; t = hours.ToString(Culture).PadLeft(2, '0') + ":" + minutes.ToString(Culture).PadLeft(2, '0') + ":" + seconds.ToString(Culture).PadLeft(2, '0'); if (OptionClock) { Interface.CurrentHudElements[i].TransitionState -= speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState < 0.0) Interface.CurrentHudElements[i].TransitionState = 0.0; } else { Interface.CurrentHudElements[i].TransitionState += speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState > 1.0) Interface.CurrentHudElements[i].TransitionState = 1.0; } } break; case "speed": if (OptionSpeed == SpeedDisplayMode.Kmph) { double kmph = Math.Abs(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed) * 3.6; t = kmph.ToString("0.00", Culture) + " km/h"; Interface.CurrentHudElements[i].TransitionState -= speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState < 0.0) Interface.CurrentHudElements[i].TransitionState = 0.0; } else if (OptionSpeed == SpeedDisplayMode.Mph) { double mph = Math.Abs(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed) * 2.2369362920544; t = mph.ToString("0.00", Culture) + " mph"; Interface.CurrentHudElements[i].TransitionState -= speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState < 0.0) Interface.CurrentHudElements[i].TransitionState = 0.0; } else { double mph = Math.Abs(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed) * 2.2369362920544; t = mph.ToString("0.00", Culture) + " mph"; Interface.CurrentHudElements[i].TransitionState += speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState > 1.0) Interface.CurrentHudElements[i].TransitionState = 1.0; } break; case "fps": int fps = (int)Math.Round(Game.InfoFrameRate); t = fps.ToString(Culture) + " fps"; if (OptionFrameRates) { Interface.CurrentHudElements[i].TransitionState -= speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState < 0.0) Interface.CurrentHudElements[i].TransitionState = 0.0; } else { Interface.CurrentHudElements[i].TransitionState += speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState > 1.0) Interface.CurrentHudElements[i].TransitionState = 1.0; } break; case "ai": t = "A.I."; if (TrainManager.PlayerTrain.AI != null) { Interface.CurrentHudElements[i].TransitionState -= speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState < 0.0) Interface.CurrentHudElements[i].TransitionState = 0.0; } else { Interface.CurrentHudElements[i].TransitionState += speed * TimeElapsed; if (Interface.CurrentHudElements[i].TransitionState > 1.0) Interface.CurrentHudElements[i].TransitionState = 1.0; } break; case "score": if (Interface.CurrentOptions.GameMode == Interface.GameMode.Arcade) { t = Game.CurrentScore.Value.ToString(Culture) + " / " + Game.CurrentScore.Maximum.ToString(Culture); if (Game.CurrentScore.Value < 0) { sc = Game.MessageColor.Red; } else if (Game.CurrentScore.Value > 0) { sc = Game.MessageColor.Green; } else { sc = Game.MessageColor.Gray; } Interface.CurrentHudElements[i].TransitionState = 0.0; } else { Interface.CurrentHudElements[i].TransitionState = 1.0; t = ""; } break; default: t = Interface.CurrentHudElements[i].Text; break; } // transitions float alpha = 1.0f; if ((Interface.CurrentHudElements[i].Transition & Interface.HudTransition.Move) != 0) { double s = Interface.CurrentHudElements[i].TransitionState; x += Interface.CurrentHudElements[i].TransitionVector.X * s * s; y += Interface.CurrentHudElements[i].TransitionVector.Y * s * s; } if ((Interface.CurrentHudElements[i].Transition & Interface.HudTransition.Fade) != 0) { alpha = (float)(1.0 - Interface.CurrentHudElements[i].TransitionState); } else if (Interface.CurrentHudElements[i].Transition == Interface.HudTransition.None) { alpha = (float)(1.0 - Interface.CurrentHudElements[i].TransitionState); } /// render if (alpha != 0.0f) { // background if (Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { float r, g, b, a; CreateBackColor(Interface.CurrentHudElements[i].BackgroundColor, sc, out r, out g, out b, out a); Gl.glColor4f(r, g, b, a * alpha); RenderOverlayTexture(Interface.CurrentHudElements[i].CenterMiddle.BackgroundTexture, x, y, x + w, y + h); } } { // text float u, v; System.Drawing.Size size = MeasureString(Interface.CurrentHudElements[i].Font, t); u = size.Width; v = size.Height; double p = Math.Round(Interface.CurrentHudElements[i].TextAlignment.X < 0 ? x : Interface.CurrentHudElements[i].TextAlignment.X == 0 ? x + 0.5 * (w - u) : x + w - u); double q = Math.Round(Interface.CurrentHudElements[i].TextAlignment.Y < 0 ? y : Interface.CurrentHudElements[i].TextAlignment.Y == 0 ? y + 0.5 * (h - v) : y + h - v); p += Interface.CurrentHudElements[i].TextPosition.X; q += Interface.CurrentHudElements[i].TextPosition.Y; float r, g, b, a; CreateTextColor(Interface.CurrentHudElements[i].TextColor, sc, out r, out g, out b, out a); DrawString(Interface.CurrentHudElements[i].Font, t, new System.Drawing.Point((int)p, (int)q), TextAlignment.TopLeft, new Color128(r, g, b, a * alpha), Interface.CurrentHudElements[i].TextShadow); } // overlay if (Interface.CurrentHudElements[i].CenterMiddle.OverlayTexture != null) { if (Textures.LoadTexture(Interface.CurrentHudElements[i].CenterMiddle.OverlayTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { float r, g, b, a; CreateBackColor(Interface.CurrentHudElements[i].OverlayColor, sc, out r, out g, out b, out a); Gl.glColor4f(r, g, b, a * alpha); RenderOverlayTexture(Interface.CurrentHudElements[i].CenterMiddle.OverlayTexture, x, y, x + w, y + h); } } } } break; } } // marker if (Interface.CurrentOptions.GameMode != Interface.GameMode.Expert) { double y = 8.0; for (int i = 0; i < Game.MarkerTextures.Length; i++) { if (Textures.LoadTexture(Game.MarkerTextures[i], Textures.OpenGlTextureWrapMode.ClampClamp)) { double w = (double)Game.MarkerTextures[i].Width; double h = (double)Game.MarkerTextures[i].Height; Gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); RenderOverlayTexture(Game.MarkerTextures[i], (double)Screen.Width - w - 8.0, y, (double)Screen.Width - 8.0, y + h); y += h + 8.0; } } } // timetable if (Timetable.CurrentTimetable == Timetable.TimetableState.Default) { // default if (Textures.LoadTexture(Timetable.DefaultTimetableTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { int w = Timetable.DefaultTimetableTexture.Width; int h = Timetable.DefaultTimetableTexture.Height; Gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); RenderOverlayTexture(Timetable.DefaultTimetableTexture, (double)(Screen.Width - w), Timetable.DefaultTimetablePosition, (double)Screen.Width, (double)h + Timetable.DefaultTimetablePosition); } } else if (Timetable.CurrentTimetable == Timetable.TimetableState.Custom & Timetable.CustomObjectsUsed == 0) { // custom if (Textures.LoadTexture(Timetable.CurrentCustomTimetableDaytimeTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { int w = Timetable.CurrentCustomTimetableDaytimeTexture.Width; int h = Timetable.CurrentCustomTimetableDaytimeTexture.Height; Gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); RenderOverlayTexture(Timetable.CurrentCustomTimetableDaytimeTexture, (double)(Screen.Width - w), Timetable.CustomTimetablePosition, (double)Screen.Width, (double)h + Timetable.CustomTimetablePosition); } if (Textures.LoadTexture(Timetable.CurrentCustomTimetableDaytimeTexture, Textures.OpenGlTextureWrapMode.ClampClamp)) { int w = Timetable.CurrentCustomTimetableDaytimeTexture.Width; int h = Timetable.CurrentCustomTimetableDaytimeTexture.Height; float alpha; if (Timetable.CurrentCustomTimetableDaytimeTexture != null) { double t = (TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].FrontAxle.Follower.TrackPosition - TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Brightness.PreviousTrackPosition) / (TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Brightness.NextTrackPosition - TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Brightness.PreviousTrackPosition); alpha = (float)((1.0 - t) * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Brightness.PreviousBrightness + t * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Brightness.NextBrightness); } else { alpha = 1.0f; } Gl.glColor4f(1.0f, 1.0f, 1.0f, alpha); RenderOverlayTexture(Timetable.CurrentCustomTimetableDaytimeTexture, (double)(Screen.Width - w), Timetable.CustomTimetablePosition, (double)Screen.Width, (double)h + Timetable.CustomTimetablePosition); } } } else if (CurrentOutputMode == OutputMode.Debug) { // debug Gl.glColor4d(0.5, 0.5, 0.5, 0.5); RenderOverlaySolid(0.0f, 0.0f, (double)Screen.Width, (double)Screen.Height); // actual handles { string t = "actual: " + (TrainManager.PlayerTrain.Specs.CurrentReverser.Actual == -1 ? "B" : TrainManager.PlayerTrain.Specs.CurrentReverser.Actual == 1 ? "F" : "N"); if (TrainManager.PlayerTrain.Specs.SingleHandle) { t += " - " + (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Actual ? "EMG" : TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Actual != 0 ? "B" + TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Actual.ToString(Culture) : TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Actual ? "HLD" : TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Actual != 0 ? "P" + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Actual.ToString(Culture) : "N"); } else if (TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { t += " - " + (TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Actual != 0 ? "P" + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Actual.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Actual ? "EMG" : TrainManager.PlayerTrain.Specs.AirBrake.Handle.Actual == TrainManager.AirBrakeHandleState.Service ? "SRV" : TrainManager.PlayerTrain.Specs.AirBrake.Handle.Actual == TrainManager.AirBrakeHandleState.Lap ? "LAP" : "REL"); } else { t += " - " + (TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Actual != 0 ? "P" + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Actual.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Actual ? "EMG" : TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Actual != 0 ? "B" + TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Actual.ToString(Culture) : TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Actual ? "HLD" : "N"); } DrawString(Fonts.SmallFont, t, new System.Drawing.Point(2, Screen.Height - 46), TextAlignment.TopLeft, Color128.White, true); } // safety handles { string t = "safety: " + (TrainManager.PlayerTrain.Specs.CurrentReverser.Actual == -1 ? "B" : TrainManager.PlayerTrain.Specs.CurrentReverser.Actual == 1 ? "F" : "N"); if (TrainManager.PlayerTrain.Specs.SingleHandle) { t += " - " + (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Safety ? "EMG" : TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Safety != 0 ? "B" + TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Safety.ToString(Culture) : TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Actual ? "HLD" : TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Safety != 0 ? "P" + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Safety.ToString(Culture) : "N"); } else if (TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { t += " - " + (TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Safety != 0 ? "P" + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Safety.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Safety ? "EMG" : TrainManager.PlayerTrain.Specs.AirBrake.Handle.Safety == TrainManager.AirBrakeHandleState.Service ? "SRV" : TrainManager.PlayerTrain.Specs.AirBrake.Handle.Safety == TrainManager.AirBrakeHandleState.Lap ? "LAP" : "REL"); } else { t += " - " + (TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Safety != 0 ? "P" + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Safety.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Safety ? "EMG" : TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Safety != 0 ? "B" + TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Safety.ToString(Culture) : TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Actual ? "HLD" : "N"); } DrawString(Fonts.SmallFont, t, new System.Drawing.Point(2, Screen.Height - 32), TextAlignment.TopLeft, Color128.White, true); } // driver handles { string t = "driver: " + (TrainManager.PlayerTrain.Specs.CurrentReverser.Driver == -1 ? "B" : TrainManager.PlayerTrain.Specs.CurrentReverser.Driver == 1 ? "F" : "N"); if (TrainManager.PlayerTrain.Specs.SingleHandle) { t += " - " + (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver ? "EMG" : TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver != 0 ? "B" + TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver.ToString(Culture) : TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver ? "HLD" : TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver != 0 ? "P" + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver.ToString(Culture) : "N"); } else if (TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { t += " - " + (TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver != 0 ? "P" + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver ? "EMG" : TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Service ? "SRV" : TrainManager.PlayerTrain.Specs.AirBrake.Handle.Driver == TrainManager.AirBrakeHandleState.Lap ? "LAP" : "REL"); } else { t += " - " + (TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver != 0 ? "P" + TrainManager.PlayerTrain.Specs.CurrentPowerNotch.Driver.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Specs.CurrentEmergencyBrake.Driver ? "EMG" : TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver != 0 ? "B" + TrainManager.PlayerTrain.Specs.CurrentBrakeNotch.Driver.ToString(Culture) : TrainManager.PlayerTrain.Specs.CurrentHoldBrake.Driver ? "HLD" : "N"); } DrawString(Fonts.SmallFont, t, new System.Drawing.Point(2, Screen.Height - 18), TextAlignment.TopLeft, Color128.White, true); } // debug information int texturesLoaded = Textures.GetNumberOfLoadedTextures(); int texturesRegistered = Textures.GetNumberOfRegisteredTextures();; int soundBuffersRegistered = Sounds.GetNumberOfLoadedBuffers(); int soundBuffersLoaded = Sounds.GetNumberOfLoadedBuffers(); int soundSourcesRegistered = Sounds.GetNumberOfRegisteredSources(); int soundSourcesPlaying = Sounds.GetNumberOfPlayingSources(); int car = 0; for (int i = 0; i < TrainManager.PlayerTrain.Cars.Length; i++) { if (TrainManager.PlayerTrain.Cars[i].Specs.IsMotorCar) { car = i; break; } } double mass = 0.0; for (int i = 0; i < TrainManager.PlayerTrain.Cars.Length; i++) { mass += TrainManager.PlayerTrain.Cars[i].Specs.MassCurrent; } string[] Lines = new string[] { "=system", "fps: " + Game.InfoFrameRate.ToString("0.0", Culture) + (MainLoop.LimitFramerate ? " (low cpu)" : ""), "score: " + Game.CurrentScore.Value.ToString(Culture), "", "=train", "speed: " + (Math.Abs(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed) * 3.6).ToString("0.00", Culture) + " km/h", "power (car " + car.ToString(Culture) + "): " + (TrainManager.PlayerTrain.Cars[car].Specs.CurrentAccelerationOutput < 0.0 ? TrainManager.PlayerTrain.Cars[car].Specs.CurrentAccelerationOutput * (double)Math.Sign(TrainManager.PlayerTrain.Cars[car].Specs.CurrentSpeed) : TrainManager.PlayerTrain.Cars[car].Specs.CurrentAccelerationOutput * (double)TrainManager.PlayerTrain.Specs.CurrentReverser.Actual).ToString("0.0000", Culture) + " m/s²", "acceleration: " + TrainManager.PlayerTrain.Specs.CurrentAverageAcceleration.ToString("0.0000", Culture) + " m/s²", "position: " + (TrainManager.PlayerTrain.Cars[0].FrontAxle.Follower.TrackPosition - TrainManager.PlayerTrain.Cars[0].FrontAxlePosition + 0.5 * TrainManager.PlayerTrain.Cars[0].Length).ToString("0.00", Culture) + " m", "elevation: " + (Game.RouteInitialElevation + TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].FrontAxle.Follower.WorldPosition.Y).ToString("0.00", Culture) + " m", "temperature: " + (TrainManager.PlayerTrain.Specs.CurrentAirTemperature - 273.15).ToString("0.00", Culture) + " °C", "air pressure: " + (0.001 * TrainManager.PlayerTrain.Specs.CurrentAirPressure).ToString("0.00", Culture) + " kPa", "air density: " + TrainManager.PlayerTrain.Specs.CurrentAirDensity.ToString("0.0000", Culture) + " kg/m³", "speed of sound: " + (Game.GetSpeedOfSound(TrainManager.PlayerTrain.Specs.CurrentAirDensity) * 3.6).ToString("0.00", Culture) + " km/h", "passenger ratio: " + TrainManager.PlayerTrain.Passengers.PassengerRatio.ToString("0.00"), "total mass: " + mass.ToString("0.00", Culture) + " kg", "", "=route", "track limit: " + (TrainManager.PlayerTrain.CurrentRouteLimit == double.PositiveInfinity ? "unlimited" : ((TrainManager.PlayerTrain.CurrentRouteLimit * 3.6).ToString("0.0", Culture) + " km/h")), "signal limit: " + (TrainManager.PlayerTrain.CurrentSectionLimit == double.PositiveInfinity ? "unlimited" : ((TrainManager.PlayerTrain.CurrentSectionLimit * 3.6).ToString("0.0", Culture) + " km/h")), "total static objects: " + ObjectManager.ObjectsUsed.ToString(Culture), "total static GL_TRIANGLES: " + Game.InfoTotalTriangles.ToString(Culture), "total static GL_TRIANGLE_STRIP: " + Game.InfoTotalTriangleStrip.ToString(Culture), "total static GL_QUADS: " + Game.InfoTotalQuads.ToString(Culture), "total static GL_QUAD_STRIP: " + Game.InfoTotalQuadStrip.ToString(Culture), "total static GL_POLYGON: " + Game.InfoTotalPolygon.ToString(Culture), "total animated objects: " + ObjectManager.AnimatedWorldObjectsUsed.ToString(Culture), "", "=renderer", "static opaque faces: " + Game.InfoStaticOpaqueFaceCount.ToString(Culture), "dynamic opaque faces: " + DynamicOpaque.FaceCount.ToString(Culture), "dynamic alpha faces: " + DynamicAlpha.FaceCount.ToString(Culture), "overlay opaque faces: " + OverlayOpaque.FaceCount.ToString(Culture), "overlay alpha faces: " + OverlayAlpha.FaceCount.ToString(Culture), "textures loaded: " + texturesLoaded.ToString(Culture) + " / " + texturesRegistered.ToString(Culture), "", "=camera", "position: " + World.CameraTrackFollower.TrackPosition.ToString("0.00", Culture) + " m", "curve radius: " + World.CameraTrackFollower.CurveRadius.ToString("0.00", Culture) + " m", "curve cant: " + (1000.0 * Math.Abs(World.CameraTrackFollower.CurveCant)).ToString("0.00", Culture) + " mm" + (World.CameraTrackFollower.CurveCant < 0.0 ? " (left)" : World.CameraTrackFollower.CurveCant > 0.0 ? " (right)" : ""), "", "=sound", "sound buffers: " + soundBuffersLoaded.ToString(Culture) + " / " + soundBuffersRegistered.ToString(Culture), "sound sources: " + soundSourcesPlaying.ToString(Culture) + " / " + soundSourcesRegistered.ToString(Culture), (Interface.CurrentOptions.SoundModel == Sounds.SoundModels.Experimental ? "log clamp factor: " + Sounds.ClampFactorLogarithm.ToString("0.00") : "outer radius factor: " + Sounds.OuterRadiusFactor.ToString("0.00", Culture)), "", "=debug", "train plugin status: " + (TrainManager.PlayerTrain.Plugin != null ? (TrainManager.PlayerTrain.Plugin.PluginValid ? "ok" : "error") : "n/a"), "train plugin message: " + (TrainManager.PlayerTrain.Plugin != null ? (TrainManager.PlayerTrain.Plugin.PluginMessage != null ? TrainManager.PlayerTrain.Plugin.PluginMessage : "n/a") : "n/a"), Game.InfoDebugString ?? "" }; double x = 4.0; double y = 4.0; for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { if (Lines[i][0] == '=') { string text = Lines[i].Substring(1); System.Drawing.Size size = MeasureString(Fonts.SmallFont, text); Gl.glColor4f(0.5f, 0.5f, 0.7f, 0.7f); RenderOverlaySolid(x, y, x + size.Width + 4.0f, y + size.Height + 2.0f); DrawString(Fonts.SmallFont, text, new System.Drawing.Point((int)x, (int)y), TextAlignment.TopLeft, Color128.White); } else { DrawString(Fonts.SmallFont, Lines[i], new System.Drawing.Point((int)x, (int)y), TextAlignment.TopLeft, Color128.White, true); } y += 14.0; } else if (y >= (double)Screen.Height - 240.0) { x += 280.0; y = 4.0; } else { y += 14.0; } } } // air brake debug output if (Interface.CurrentOptions.GameMode != Interface.GameMode.Expert & OptionBrakeSystems) { double oy = 64.0, y = oy, h = 16.0; bool[] heading = new bool[6]; for (int i = 0; i < TrainManager.PlayerTrain.Cars.Length; i++) { double x = 96.0, w = 128.0; // brake pipe if (TrainManager.PlayerTrain.Cars[i].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake | TrainManager.PlayerTrain.Cars[i].Specs.BrakeType == TrainManager.CarBrakeType.ElectromagneticStraightAirBrake) { if (!heading[0]) { DrawString(Fonts.SmallFont, "Brake pipe", new System.Drawing.Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true); heading[0] = true; } Gl.glColor3f(0.0f, 0.0f, 0.0f); RenderOverlaySolid(x, y, x + w, y + h); double p = TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure; double r = p / TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.BrakePipeNormalPressure; Gl.glColor3f(1.0f, 1.0f, 0.0f); RenderOverlaySolid(x, y, x + r * w, y + h); } x += w + 8.0; // auxillary reservoir if (TrainManager.PlayerTrain.Cars[i].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake | TrainManager.PlayerTrain.Cars[i].Specs.BrakeType == TrainManager.CarBrakeType.ElectromagneticStraightAirBrake) { if (!heading[1]) { //RenderString(x, oy - 16.0, Fonts.FontType.Small, "Auxillary reservoir", -1, 0.75f, 0.75f, 0.75f, true); DrawString(Fonts.SmallFont, "Auxillary reservoir", new System.Drawing.Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true); heading[1] = true; } Gl.glColor3f(0.0f, 0.0f, 0.0f); RenderOverlaySolid(x, y, x + w, y + h); double p = TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.AuxillaryReservoirCurrentPressure; double r = p / TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.AuxillaryReservoirMaximumPressure; Gl.glColor3f(0.5f, 0.5f, 0.5f); RenderOverlaySolid(x, y, x + r * w, y + h); } x += w + 8.0; // brake cylinder { if (!heading[2]) { //RenderString(x, oy - 16.0, Fonts.FontType.Small, "Brake cylinder", -1, 0.75f, 0.5f, 0.25f, true); DrawString(Fonts.SmallFont, "Brake cylinder", new System.Drawing.Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true); heading[2] = true; } Gl.glColor3f(0.0f, 0.0f, 0.0f); RenderOverlaySolid(x, y, x + w, y + h); double p = TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.BrakeCylinderCurrentPressure; double r = p / TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; Gl.glColor3f(0.75f, 0.5f, 0.25f); RenderOverlaySolid(x, y, x + r * w, y + h); } x += w + 8.0; // main reservoir if (TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.Type == TrainManager.AirBrakeType.Main) { if (!heading[3]) { //RenderString(x, oy - 16.0, Fonts.FontType.Small, "Main reservoir", -1, 1.0f, 0.0f, 0.0f, true); DrawString(Fonts.SmallFont, "Main reservoir", new System.Drawing.Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true); heading[3] = true; } Gl.glColor3f(0.0f, 0.0f, 0.0f); RenderOverlaySolid(x, y, x + w, y + h); double p = TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.MainReservoirCurrentPressure; double r = p / TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.AirCompressorMaximumPressure; Gl.glColor3f(1.0f, 0.0f, 0.0f); RenderOverlaySolid(x, y, x + r * w, y + h); } x += w + 8.0; // equalizing reservoir if (TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.Type == TrainManager.AirBrakeType.Main) { if (!heading[4]) { //RenderString(x, oy - 16.0, Fonts.FontType.Small, "Equalizing reservoir", -1, 0.0f, 0.75f, 0.0f, true); DrawString(Fonts.SmallFont, "Equalizing reservoir", new System.Drawing.Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true); heading[4] = true; } Gl.glColor3f(0.0f, 0.0f, 0.0f); RenderOverlaySolid(x, y, x + w, y + h); double p = TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.EqualizingReservoirCurrentPressure; double r = p / TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.EqualizingReservoirNormalPressure; Gl.glColor3f(0.0f, 0.75f, 0.0f); RenderOverlaySolid(x, y, x + r * w, y + h); } x += w + 8.0; // straight air pipe if (TrainManager.PlayerTrain.Cars[i].Specs.BrakeType == TrainManager.CarBrakeType.ElectromagneticStraightAirBrake & TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.Type == TrainManager.AirBrakeType.Main) { if (!heading[5]) { //RenderString(x, oy - 16.0, Fonts.FontType.Small, "Straight air pipe", -1, 0.0f, 0.75f, 1.0f, true); DrawString(Fonts.SmallFont, "Straight air pipe", new System.Drawing.Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true); heading[5] = true; } Gl.glColor3f(0.0f, 0.0f, 0.0f); RenderOverlaySolid(x, y, x + w, y + h); double p = TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.StraightAirPipeCurrentPressure; double r = p / TrainManager.PlayerTrain.Cars[i].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; Gl.glColor3f(0.0f, 0.75f, 1.0f); RenderOverlaySolid(x, y, x + r * w, y + h); } x += w + 8.0; Gl.glColor3f(0.0f, 0.0f, 0.0f); y += h + 8.0; } } // interface if (Game.CurrentInterface == Game.InterfaceType.Pause) { // pause Gl.glColor4f(0.0f, 0.0f, 0.0f, 0.5f); RenderOverlaySolid(0.0, 0.0, (double)Screen.Width, (double)Screen.Height); Gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); DrawString(Fonts.VeryLargeFont, "PAUSE", new System.Drawing.Point(Screen.Width / 2, Screen.Height / 2), TextAlignment.CenterMiddle, Color128.White, true); } else if (Game.CurrentInterface == Game.InterfaceType.Menu) { // menu Gl.glColor4f(0.0f, 0.0f, 0.0f, 0.5f); RenderOverlaySolid(0.0, 0.0, (double)Screen.Width, (double)Screen.Height); Gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); Game.MenuEntry[] m = Game.CurrentMenu; const double ra = 32.0; const double rb = 96.0; const double rc = 144.0; const double w = 256.0; const double h = 28.0; double x = 0.5 * (double)Screen.Width - w; for (int i = 0; i < Game.CurrentMenuSelection.Length; i++) { double o = Game.CurrentMenuOffsets[i]; double oc = o == double.NegativeInfinity ? 0.0 : o; double y = 0.5 * ((double)Screen.Height - (double)m.Length * h) + oc; double ys = y + (double)Game.CurrentMenuSelection[i] * h; double ot; if (ys < rc) { ot = oc + rc - ys; } else if (ys > (double)Screen.Height - rc) { ot = oc + (double)Screen.Height - rc - ys; } else { ot = oc; } if (o == double.NegativeInfinity) { o = ot; } else if (o < ot) { double d = ot - o; o += (0.03 * d * d + 0.1) * TimeElapsed; if (o > ot) o = ot; } else if (o > ot) { double d = o - ot; o -= (0.03 * d * d + 0.1) * TimeElapsed; if (o < ot) o = ot; } Game.CurrentMenuOffsets[i] = o; for (int j = 0; j < m.Length; j++) { double ta; if (y < rb) { ta = (y - ra) / (rb - ra); if (ta < 0.0) ta = 0.0; if (ta > 1.0) ta = 1.0; } else if (y > (double)Screen.Height - rb) { ta = ((double)Screen.Height - y - ra) / (rb - ra); if (ta < 0.0) ta = 0.0; if (ta > 1.0) ta = 1.0; } else { ta = 1.0; } if (ta < m[j].Alpha) { m[j].Alpha -= 4.0 * TimeElapsed; if (m[j].Alpha < ta) m[j].Alpha = ta; } else if (ta > m[j].Alpha) { m[j].Alpha += 4.0 * TimeElapsed; if (m[j].Alpha > ta) m[j].Alpha = ta; } if (j == Game.CurrentMenuSelection[i]) { m[j].Highlight = 1.0; } else { m[j].Highlight -= 4.0 * TimeElapsed; if (m[j].Highlight < 0.0) m[j].Highlight = 0.0; } float r = 1.0f; float g = 1.0f; float b = (float)(1.0 - m[j].Highlight); float a = (float)m[j].Alpha; if (j == Game.CurrentMenuSelection[i]) { DrawString(Fonts.NormalFont, "➢", new System.Drawing.Point((int)x, (int)y), TextAlignment.TopLeft, new Color128(r, g, b, a), true); } if (m[j] is Game.MenuCaption) { DrawString(Fonts.NormalFont, m[j].Text, new System.Drawing.Point((int)x + 24, (int)y), TextAlignment.TopLeft, new Color128(0.5f, 0.75f, 1.0f, a), true); } else if (m[j] is Game.MenuCommand) { DrawString(Fonts.NormalFont, m[j].Text, new System.Drawing.Point((int)x + 24, (int)y), TextAlignment.TopLeft, new Color128(r, g, b, a), true); } else { DrawString(Fonts.NormalFont, " ➟ " + m[j].Text, new System.Drawing.Point((int)x + 24, (int)y), TextAlignment.TopLeft, new Color128(r, g, b, a), true); } y += h; } x += w; Game.MenuSubmenu n = m[Game.CurrentMenuSelection[i]] as Game.MenuSubmenu; m = n == null ? null : n.Entries; } } // fade to black on change ends if (TrainManager.PlayerTrain.Station >= 0 && Game.Stations[TrainManager.PlayerTrain.Station].StationType == Game.StationType.ChangeEnds && TrainManager.PlayerTrain.StationState == TrainManager.TrainStopState.Boarding) { double time = TrainManager.PlayerTrain.StationDepartureTime - Game.SecondsSinceMidnight; if (time < 1.0) { FadeToBlackDueToChangeEnds = Math.Max(0.0, 1.0 - time); } else if (FadeToBlackDueToChangeEnds > 0.0) { FadeToBlackDueToChangeEnds -= TimeElapsed; if (FadeToBlackDueToChangeEnds < 0.0) { FadeToBlackDueToChangeEnds = 0.0; } } } else if (FadeToBlackDueToChangeEnds > 0.0) { FadeToBlackDueToChangeEnds -= TimeElapsed; if (FadeToBlackDueToChangeEnds < 0.0) { FadeToBlackDueToChangeEnds = 0.0; } } if (FadeToBlackDueToChangeEnds > 0.0 & (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead)) { Gl.glColor4d(0.0, 0.0, 0.0, FadeToBlackDueToChangeEnds); RenderOverlaySolid(0.0, 0.0, (double)Screen.Width, (double)Screen.Height); } // finalize Gl.glPopMatrix(); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glPopMatrix(); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glDisable(Gl.GL_BLEND); } // get color private static void CreateBackColor(Color32 Original, Game.MessageColor SystemColor, out float R, out float G, out float B, out float A) { if (Original.R == 0 & Original.G == 0 & Original.B == 0) { switch (SystemColor) { case Game.MessageColor.Black: R = 0.0f; G = 0.0f; B = 0.0f; break; case Game.MessageColor.Gray: R = 0.4f; G = 0.4f; B = 0.4f; break; case Game.MessageColor.White: R = 1.0f; G = 1.0f; B = 1.0f; break; case Game.MessageColor.Red: R = 1.0f; G = 0.0f; B = 0.0f; break; case Game.MessageColor.Orange: R = 0.9f; G = 0.7f; B = 0.0f; break; case Game.MessageColor.Green: R = 0.2f; G = 0.8f; B = 0.0f; break; case Game.MessageColor.Blue: R = 0.0f; G = 0.7f; B = 1.0f; break; case Game.MessageColor.Magenta: R = 1.0f; G = 0.0f; B = 0.7f; break; default: R = 1.0f; G = 1.0f; B = 1.0f; break; } } else { R = inv255 * (float)Original.R; G = inv255 * (float)Original.G; B = inv255 * (float)Original.B; } A = inv255 * (float)Original.A; } private static void CreateTextColor(Color32 Original, Game.MessageColor SystemColor, out float R, out float G, out float B, out float A) { if (Original.R == 0 & Original.G == 0 & Original.B == 0) { switch (SystemColor) { case Game.MessageColor.Black: R = 0.0f; G = 0.0f; B = 0.0f; break; case Game.MessageColor.Gray: R = 0.4f; G = 0.4f; B = 0.4f; break; case Game.MessageColor.White: R = 1.0f; G = 1.0f; B = 1.0f; break; case Game.MessageColor.Red: R = 1.0f; G = 0.0f; B = 0.0f; break; case Game.MessageColor.Orange: R = 0.9f; G = 0.7f; B = 0.0f; break; case Game.MessageColor.Green: R = 0.3f; G = 1.0f; B = 0.0f; break; case Game.MessageColor.Blue: R = 1.0f; G = 1.0f; B = 1.0f; break; case Game.MessageColor.Magenta: R = 1.0f; G = 0.0f; B = 0.7f; break; default: R = 1.0f; G = 1.0f; B = 1.0f; break; } } else { R = inv255 * (float)Original.R; G = inv255 * (float)Original.G; B = inv255 * (float)Original.B; } A = inv255 * (float)Original.A; } // render overlay texture // private static void RenderOverlayTexture(Textures.Texture texture, double ax, double ay, double bx, double by) { // double nay = (double)Screen.Height - ay; // double nby = (double)Screen.Height - by; // if (Textures.LoadTexture(texture)) { // if (!TexturingEnabled) { // Gl.glEnable(Gl.GL_TEXTURE_2D); // TexturingEnabled = true; // } // Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture.OpenGlTextureName); // } else if (TexturingEnabled) { // Gl.glDisable(Gl.GL_TEXTURE_2D); // TexturingEnabled = false; // } // Gl.glBegin(Gl.GL_QUADS); // Gl.glTexCoord2d(0.0, 1.0); // Gl.glVertex2d(ax, nby); // Gl.glTexCoord2d(0.0, 0.0); // Gl.glVertex2d(ax, nay); // Gl.glTexCoord2d(1.0, 0.0); // Gl.glVertex2d(bx, nay); // Gl.glTexCoord2d(1.0, 1.0); // Gl.glVertex2d(bx, nby); // Gl.glEnd(); // } // render overlay solid // private static void RenderOverlaySolid(double ax, double ay, double bx, double by) { // double nay = (double)Screen.Height - ay; // double nby = (double)Screen.Height - by; // if (TexturingEnabled) { // Gl.glDisable(Gl.GL_TEXTURE_2D); // TexturingEnabled = false; // } // Gl.glBegin(Gl.GL_QUADS); // Gl.glVertex2d(ax, nby); // Gl.glVertex2d(ax, nay); // Gl.glVertex2d(bx, nay); // Gl.glVertex2d(bx, nby); // Gl.glEnd(); // } // re-add objects internal static void ReAddObjects() { Object[] list = new Object[ObjectCount]; for (int i = 0; i < ObjectCount; i++) { list[i] = Objects[i]; } for (int i = 0; i < list.Length; i++) { HideObject(list[i].ObjectIndex); } for (int i = 0; i < list.Length; i++) { ShowObject(list[i].ObjectIndex, list[i].Type); } } // show object internal static void ShowObject(int ObjectIndex, ObjectType Type) { if (ObjectManager.Objects[ObjectIndex] == null) { return; } if (ObjectManager.Objects[ObjectIndex].RendererIndex == 0) { if (ObjectCount >= Objects.Length) { Array.Resize(ref Objects, Objects.Length << 1); } Objects[ObjectCount].ObjectIndex = ObjectIndex; Objects[ObjectCount].Type = Type; int f = ObjectManager.Objects[ObjectIndex].Mesh.Faces.Length; Objects[ObjectCount].FaceListReferences = new ObjectListReference[f]; for (int i = 0; i < f; i++) { bool alpha = false; int k = ObjectManager.Objects[ObjectIndex].Mesh.Faces[i].Material; Textures.OpenGlTextureWrapMode wrap = Textures.OpenGlTextureWrapMode.ClampClamp; if (ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].DaytimeTexture != null | ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].NighttimeTexture != null) { // HACK: Objects do not store information on the texture wrapping mode. // Let's determine the best wrapping mode now and then save it // so we can quickly access it in the rendering loop. if (ObjectManager.Objects[ObjectIndex].Dynamic) { wrap = Textures.OpenGlTextureWrapMode.RepeatRepeat; } else { for (int v = 0; v < ObjectManager.Objects[ObjectIndex].Mesh.Vertices.Length; v++) { if (ObjectManager.Objects[ObjectIndex].Mesh.Vertices[v].TextureCoordinates.X < 0.0f | ObjectManager.Objects[ObjectIndex].Mesh.Vertices[v].TextureCoordinates.X > 1.0f) { wrap |= Textures.OpenGlTextureWrapMode.RepeatClamp; } if (ObjectManager.Objects[ObjectIndex].Mesh.Vertices[v].TextureCoordinates.Y < 0.0f | ObjectManager.Objects[ObjectIndex].Mesh.Vertices[v].TextureCoordinates.Y > 1.0f) { wrap |= Textures.OpenGlTextureWrapMode.ClampRepeat; } } } if (ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].DaytimeTexture != null) { if (Textures.LoadTexture(ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].DaytimeTexture, wrap)) { OpenBveApi.Textures.TextureTransparencyType type = ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].DaytimeTexture.Transparency; if (type == OpenBveApi.Textures.TextureTransparencyType.Alpha) { alpha = true; } else if (type == OpenBveApi.Textures.TextureTransparencyType.Partial & Interface.CurrentOptions.TransparencyMode == TransparencyMode.Quality) { alpha = true; } } } if (ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].NighttimeTexture != null) { if (Textures.LoadTexture(ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].NighttimeTexture, wrap)) { OpenBveApi.Textures.TextureTransparencyType type = ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].NighttimeTexture.Transparency; if (type == OpenBveApi.Textures.TextureTransparencyType.Alpha) { alpha = true; } else if (type == OpenBveApi.Textures.TextureTransparencyType.Partial & Interface.CurrentOptions.TransparencyMode == TransparencyMode.Quality) { alpha = true; } } } } if (Type == ObjectType.Overlay & World.CameraRestriction != World.CameraRestrictionMode.NotAvailable) { alpha = true; } else if (ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].Color.A != 255) { alpha = true; } else if (ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].BlendMode == World.MeshMaterialBlendMode.Additive) { alpha = true; } else if (ObjectManager.Objects[ObjectIndex].Mesh.Materials[k].GlowAttenuationData != 0) { alpha = true; } ObjectListType listType; switch (Type) { case ObjectType.Static: listType = alpha ? ObjectListType.DynamicAlpha : ObjectListType.StaticOpaque; break; case ObjectType.Dynamic: listType = alpha ? ObjectListType.DynamicAlpha : ObjectListType.DynamicOpaque; break; case ObjectType.Overlay: listType = alpha ? ObjectListType.OverlayAlpha : ObjectListType.OverlayOpaque; break; default: throw new InvalidOperationException(); } if (listType == ObjectListType.StaticOpaque) { /* * For the static opaque list, insert the face into * the first vacant position in the matching group's list. * */ int groupIndex = (int)ObjectManager.Objects[ObjectIndex].GroupIndex; if (groupIndex >= StaticOpaque.Length) { if (StaticOpaque.Length == 0) { StaticOpaque = new ObjectGroup[16]; } while (groupIndex >= StaticOpaque.Length) { Array.Resize(ref StaticOpaque, StaticOpaque.Length << 1); } } if (StaticOpaque[groupIndex] == null) { StaticOpaque[groupIndex] = new ObjectGroup(); } ObjectList list = StaticOpaque[groupIndex].List; int newIndex = list.FaceCount; for (int j = 0; j < list.FaceCount; j++) { if (list.Faces[j] == null) { newIndex = j; break; } } if (newIndex == list.FaceCount) { if (list.FaceCount == list.Faces.Length) { Array.Resize(ref list.Faces, list.Faces.Length << 1); } list.FaceCount++; } list.Faces[newIndex] = new ObjectFace(); list.Faces[newIndex].ObjectListIndex = ObjectCount; list.Faces[newIndex].ObjectIndex = ObjectIndex; list.Faces[newIndex].FaceIndex = i; // HACK: Let's store the wrapping mode. list.Faces[newIndex].Wrap = wrap; StaticOpaque[groupIndex].Update = true; Objects[ObjectCount].FaceListReferences[i] = new ObjectListReference(listType, newIndex); Game.InfoStaticOpaqueFaceCount++; } else { /* * For all other lists, insert the face at the end of the list. * */ ObjectList list; switch (listType) { case ObjectListType.DynamicOpaque: list = DynamicOpaque; break; case ObjectListType.DynamicAlpha: list = DynamicAlpha; break; case ObjectListType.OverlayOpaque: list = OverlayOpaque; break; case ObjectListType.OverlayAlpha: list = OverlayAlpha; break; default: throw new InvalidOperationException(); } if (list.FaceCount == list.Faces.Length) { Array.Resize(ref list.Faces, list.Faces.Length << 1); } list.Faces[list.FaceCount] = new ObjectFace(); list.Faces[list.FaceCount].ObjectListIndex = ObjectCount; list.Faces[list.FaceCount].ObjectIndex = ObjectIndex; list.Faces[list.FaceCount].FaceIndex = i; // HACK: Let's store the wrapping mode. list.Faces[list.FaceCount].Wrap = wrap; Objects[ObjectCount].FaceListReferences[i] = new ObjectListReference(listType, list.FaceCount); list.FaceCount++; } } ObjectManager.Objects[ObjectIndex].RendererIndex = ObjectCount + 1; ObjectCount++; } } // hide object internal static void HideObject(int ObjectIndex) { if (ObjectManager.Objects[ObjectIndex] == null) { return; } int k = ObjectManager.Objects[ObjectIndex].RendererIndex - 1; if (k >= 0) { // remove faces for (int i = 0; i < Objects[k].FaceListReferences.Length; i++) { ObjectListType listType = Objects[k].FaceListReferences[i].Type; if (listType == ObjectListType.StaticOpaque) { /* * For static opaque faces, set the face to be removed * to a null reference. If there are null entries at * the end of the list, update the number of faces used * accordingly. * */ int groupIndex = (int)ObjectManager.Objects[Objects[k].ObjectIndex].GroupIndex; ObjectList list = StaticOpaque[groupIndex].List; int listIndex = Objects[k].FaceListReferences[i].Index; list.Faces[listIndex] = null; if (listIndex == list.FaceCount - 1) { int count = 0; for (int j = list.FaceCount - 2; j >= 0; j--) { if (list.Faces[j] != null) { count = j + 1; break; } } list.FaceCount = count; } StaticOpaque[groupIndex].Update = true; Game.InfoStaticOpaqueFaceCount--; } else { /* * For all other kinds of faces, move the last face into place * of the face to be removed and decrement the face counter. * */ ObjectList list; switch (listType) { case ObjectListType.DynamicOpaque: list = DynamicOpaque; break; case ObjectListType.DynamicAlpha: list = DynamicAlpha; break; case ObjectListType.OverlayOpaque: list = OverlayOpaque; break; case ObjectListType.OverlayAlpha: list = OverlayAlpha; break; default: throw new InvalidOperationException(); } int listIndex = Objects[k].FaceListReferences[i].Index; list.Faces[listIndex] = list.Faces[list.FaceCount - 1]; Objects[list.Faces[listIndex].ObjectListIndex].FaceListReferences[list.Faces[listIndex].FaceIndex].Index = listIndex; list.FaceCount--; } } // remove object if (k == ObjectCount - 1) { ObjectCount--; } else { Objects[k] = Objects[ObjectCount - 1]; ObjectCount--; for (int i = 0; i < Objects[k].FaceListReferences.Length; i++) { ObjectListType listType = Objects[k].FaceListReferences[i].Type; ObjectList list; switch (listType) { case ObjectListType.StaticOpaque: { int groupIndex = (int)ObjectManager.Objects[Objects[k].ObjectIndex].GroupIndex; list = StaticOpaque[groupIndex].List; } break; case ObjectListType.DynamicOpaque: list = DynamicOpaque; break; case ObjectListType.DynamicAlpha: list = DynamicAlpha; break; case ObjectListType.OverlayOpaque: list = OverlayOpaque; break; case ObjectListType.OverlayAlpha: list = OverlayAlpha; break; default: throw new InvalidOperationException(); } int listIndex = Objects[k].FaceListReferences[i].Index; list.Faces[listIndex].ObjectListIndex = k; } ObjectManager.Objects[Objects[k].ObjectIndex].RendererIndex = k + 1; } ObjectManager.Objects[ObjectIndex].RendererIndex = 0; } } // sort polygons private static void SortPolygons(ObjectList List) { // calculate distance double cx = World.AbsoluteCameraPosition.X; double cy = World.AbsoluteCameraPosition.Y; double cz = World.AbsoluteCameraPosition.Z; for (int i = 0; i < List.FaceCount; i++) { int o = List.Faces[i].ObjectIndex; int f = List.Faces[i].FaceIndex; if (ObjectManager.Objects[o].Mesh.Faces[f].Vertices.Length >= 3) { int v0 = ObjectManager.Objects[o].Mesh.Faces[f].Vertices[0].Index; int v1 = ObjectManager.Objects[o].Mesh.Faces[f].Vertices[1].Index; int v2 = ObjectManager.Objects[o].Mesh.Faces[f].Vertices[2].Index; double v0x = ObjectManager.Objects[o].Mesh.Vertices[v0].Coordinates.X; double v0y = ObjectManager.Objects[o].Mesh.Vertices[v0].Coordinates.Y; double v0z = ObjectManager.Objects[o].Mesh.Vertices[v0].Coordinates.Z; double v1x = ObjectManager.Objects[o].Mesh.Vertices[v1].Coordinates.X; double v1y = ObjectManager.Objects[o].Mesh.Vertices[v1].Coordinates.Y; double v1z = ObjectManager.Objects[o].Mesh.Vertices[v1].Coordinates.Z; double v2x = ObjectManager.Objects[o].Mesh.Vertices[v2].Coordinates.X; double v2y = ObjectManager.Objects[o].Mesh.Vertices[v2].Coordinates.Y; double v2z = ObjectManager.Objects[o].Mesh.Vertices[v2].Coordinates.Z; double w1x = v1x - v0x, w1y = v1y - v0y, w1z = v1z - v0z; double w2x = v2x - v0x, w2y = v2y - v0y, w2z = v2z - v0z; double dx = -w1z * w2y + w1y * w2z; double dy = w1z * w2x - w1x * w2z; double dz = -w1y * w2x + w1x * w2y; double t = dx * dx + dy * dy + dz * dz; if (t != 0.0) { t = 1.0 / Math.Sqrt(t); dx *= t; dy *= t; dz *= t; double w0x = v0x - cx, w0y = v0y - cy, w0z = v0z - cz; t = dx * w0x + dy * w0y + dz * w0z; List.Faces[i].Distance = -t * t; } } } // sort double[] distances = new double[List.FaceCount]; for (int i = 0; i < List.FaceCount; i++) { distances[i] = List.Faces[i].Distance; } Array.Sort(distances, List.Faces, 0, List.FaceCount); // update objects for (int i = 0; i < List.FaceCount; i++) { Objects[List.Faces[i].ObjectListIndex].FaceListReferences[List.Faces[i].FaceIndex].Index = i; } } // get distance factor private static double GetDistanceFactor(World.Vertex[] Vertices, ref World.MeshFace Face, ushort GlowAttenuationData, double CameraX, double CameraY, double CameraZ) { if (Face.Vertices.Length != 0) { World.GlowAttenuationMode mode; double halfdistance; World.SplitGlowAttenuationData(GlowAttenuationData, out mode, out halfdistance); int i = (int)Face.Vertices[0].Index; double dx = Vertices[i].Coordinates.X - CameraX; double dy = Vertices[i].Coordinates.Y - CameraY; double dz = Vertices[i].Coordinates.Z - CameraZ; switch (mode) { case World.GlowAttenuationMode.DivisionExponent2: { double t = dx * dx + dy * dy + dz * dz; return t / (t + halfdistance * halfdistance); } case World.GlowAttenuationMode.DivisionExponent4: { double t = dx * dx + dy * dy + dz * dz; t *= t; halfdistance *= halfdistance; return t / (t + halfdistance * halfdistance); } default: return 1.0; } } else { return 1.0; } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/SoundManager.cs000066400000000000000000000560471171674032100224660ustar00rootroot00000000000000// === NOT COMPILED === using System; using Tao.OpenAl; namespace OpenBve { internal static class SoundManager { // general internal struct OpenAlIndex { internal int Index; internal bool Valid; internal OpenAlIndex(int Index, bool Valid) { this.Index = Index; this.Valid = Valid; } } // sound buffers private class SoundBuffer { internal string FileName; internal double Duration; internal OpenAlIndex OpenAlBufferIndex; internal bool TriedLoading; internal float Radius; } private static SoundBuffer[] SoundBuffers = new SoundBuffer[16]; // sound sources internal class SoundSource { internal World.Vector3D Position; internal float[] OpenAlPosition; internal float[] OpenAlVelocity; internal OpenAlIndex OpenAlSourceIndex; internal int SoundBufferIndex; internal double Radius; internal float Pitch; internal float Gain; internal bool Looped; internal bool Suppressed; internal bool FinishedPlaying; internal bool HasHandle; internal TrainManager.Train Train; internal int CarIndex; } internal static SoundSource[] SoundSources = new SoundSource[16]; // listener private static float[] ListenerPosition = new float[] { 0.0f, 0.0f, 0.0f }; private static float[] ListenerVelocity = new float[] { 0.0f, 0.0f, 0.0f }; private static float[] ListenerOrientation = new float[] { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f }; private static double InternalTimer = 0.0; // openal private static IntPtr OpenAlDevice = IntPtr.Zero; private static IntPtr OpenAlContext = IntPtr.Zero; // misc internal static double OuterRadiusFactor = 8.0; private static double OuterRadiusFactorMinimum = 2.0; private static double OuterRadiusFactorMaximum = 8.0; private static double OuterRadiusSpeed = 0.0; private const double OuterRadiusAcceleration = 0.5; private const double OuterRadiusDeceleration = 1.0; private static int SoundsQueriedPlaying = 0; private static int SoundsActuallyPlaying = 0; // options internal static bool Mute = false; // initialize internal static void Initialize() { // openal OpenAlDevice = Alc.alcOpenDevice(null); if (OpenAlDevice != IntPtr.Zero) { OpenAlContext = Alc.alcCreateContext(OpenAlDevice, IntPtr.Zero); if (OpenAlContext != IntPtr.Zero) { Alc.alcMakeContextCurrent(OpenAlContext); Al.alSpeedOfSound(343.0f); Al.alDistanceModel(Al.AL_NONE); } else { Alc.alcCloseDevice(OpenAlDevice); OpenAlDevice = IntPtr.Zero; System.Windows.Forms.MessageBox.Show("The sound device could be opened, but the sound context could not be created.", "openBVE", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Hand); } } else { OpenAlContext = IntPtr.Zero; System.Windows.Forms.MessageBox.Show("The sound device could not be opened.", "openBVE", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Hand); } // outer radius switch (Interface.CurrentOptions.SoundRange) { case Interface.SoundRange.Low: OuterRadiusFactorMinimum = 2.0; OuterRadiusFactorMaximum = 8.0; break; case Interface.SoundRange.Medium: OuterRadiusFactorMinimum = 4.0; OuterRadiusFactorMaximum = 16.0; break; case Interface.SoundRange.High: OuterRadiusFactorMinimum = 8.0; OuterRadiusFactorMaximum = 32.0; break; } OuterRadiusFactor = OuterRadiusFactorMaximum; } // deinitialize internal static void Deinitialize() { if (OpenAlContext != IntPtr.Zero) { SoundManager.StopAllSounds(true); SoundManager.UnuseAllSoundsBuffers(); Alc.alcMakeContextCurrent(IntPtr.Zero); Alc.alcDestroyContext(OpenAlContext); OpenAlContext = IntPtr.Zero; } if (OpenAlDevice != IntPtr.Zero) { Alc.alcCloseDevice(OpenAlDevice); OpenAlDevice = IntPtr.Zero; } } // update internal static void Update(double TimeElapsed) { if (OpenAlContext != IntPtr.Zero) { // listener double vx = World.CameraTrackFollower.WorldDirection.X * World.CameraSpeed; double vy = World.CameraTrackFollower.WorldDirection.Y * World.CameraSpeed; double vz = World.CameraTrackFollower.WorldDirection.Z * World.CameraSpeed; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) { ListenerVelocity[0] = 0.0f; ListenerVelocity[1] = 0.0f; ListenerVelocity[2] = 0.0f; } else { ListenerVelocity[0] = (float)vx; ListenerVelocity[1] = (float)vy; ListenerVelocity[2] = (float)vz; } ListenerOrientation[0] = (float)World.AbsoluteCameraDirection.X; ListenerOrientation[1] = (float)World.AbsoluteCameraDirection.Y; ListenerOrientation[2] = (float)World.AbsoluteCameraDirection.Z; ListenerOrientation[3] = (float)-World.AbsoluteCameraUp.X; ListenerOrientation[4] = (float)-World.AbsoluteCameraUp.Y; ListenerOrientation[5] = (float)-World.AbsoluteCameraUp.Z; Al.alListenerfv(Al.AL_POSITION, ListenerPosition); Al.alListenerfv(Al.AL_VELOCITY, ListenerVelocity); Al.alListenerfv(Al.AL_ORIENTATION, ListenerOrientation); double cx = World.AbsoluteCameraPosition.X; double cy = World.AbsoluteCameraPosition.Y; double cz = World.AbsoluteCameraPosition.Z; if (Mute) { // mute for (int i = 0; i < SoundSources.Length; i++) { if (SoundSources[i] != null && !SoundSources[i].FinishedPlaying) { if (!SoundSources[i].Suppressed) { if (SoundSources[i].Looped) { if (SoundSources[i].OpenAlSourceIndex.Valid) { int j = SoundSources[i].OpenAlSourceIndex.Index; Al.alSourceStop(j); Al.alDeleteSources(1, ref j); } SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(0, false); SoundSources[i].Suppressed = true; } else { StopSound(i, false); } } else if (!SoundSources[i].Looped) { StopSound(i, false); } } } } else { // outer radius int n = Interface.CurrentOptions.SoundNumber - 3; if (SoundsActuallyPlaying >= n) { OuterRadiusSpeed -= OuterRadiusDeceleration * TimeElapsed; if (OuterRadiusSpeed < -1.0) OuterRadiusSpeed = -1.0; } else if (SoundsQueriedPlaying < n) { OuterRadiusSpeed += OuterRadiusAcceleration * TimeElapsed; if (OuterRadiusSpeed > 1.0) OuterRadiusSpeed = 1.0; } else { OuterRadiusSpeed -= (double)Math.Sign(OuterRadiusSpeed) * OuterRadiusDeceleration * TimeElapsed; if (OuterRadiusSpeed * OuterRadiusSpeed <= TimeElapsed * TimeElapsed) { OuterRadiusSpeed = 0.0; } } OuterRadiusFactor += OuterRadiusSpeed * TimeElapsed; if (OuterRadiusFactor < OuterRadiusFactorMinimum) { OuterRadiusFactor = OuterRadiusFactorMinimum; } else if (OuterRadiusFactor > OuterRadiusFactorMaximum) { OuterRadiusFactor = OuterRadiusFactorMaximum; } // sources SoundsQueriedPlaying = 0; SoundsActuallyPlaying = 0; for (int i = 0; i < SoundSources.Length; i++) { if (SoundSources[i] != null && !SoundSources[i].FinishedPlaying) { double rx = SoundSources[i].Position.X; double ry = SoundSources[i].Position.Y; double rz = SoundSources[i].Position.Z; double px, py, pz; if (SoundSources[i].Train != null) { int c = SoundSources[i].CarIndex; double tx, ty, tz; TrainManager.CreateWorldCoordinates(SoundSources[i].Train, c, rx, ry, rz, out px, out py, out pz, out tx, out ty, out tz); px -= cx; py -= cy; pz -= cz; double sp = SoundSources[i].Train.Specs.CurrentAverageSpeed; if (World.CameraMode != World.CameraViewMode.Interior & World.CameraMode != World.CameraViewMode.InteriorLookAhead) { SoundSources[i].OpenAlVelocity[0] = (float)(tx * sp); SoundSources[i].OpenAlVelocity[1] = (float)(ty * sp); SoundSources[i].OpenAlVelocity[2] = (float)(tz * sp); } else { SoundSources[i].OpenAlVelocity[0] = (float)(tx * sp - vx); SoundSources[i].OpenAlVelocity[1] = (float)(ty * sp - vy); SoundSources[i].OpenAlVelocity[2] = (float)(tz * sp - vz); } } else { px = rx - cx; py = ry - cy; pz = rz - cz; if (World.CameraMode != World.CameraViewMode.Interior & World.CameraMode != World.CameraViewMode.InteriorLookAhead) { SoundSources[i].OpenAlVelocity[0] = 0.0f; SoundSources[i].OpenAlVelocity[1] = 0.0f; SoundSources[i].OpenAlVelocity[2] = 0.0f; } else { SoundSources[i].OpenAlVelocity[0] = (float)-vx; SoundSources[i].OpenAlVelocity[1] = (float)-vy; SoundSources[i].OpenAlVelocity[2] = (float)-vz; } } // play the sound only if within the outer radius double distanceSquared = px * px + py * py + pz * pz; double distance = Math.Sqrt(distanceSquared); double innerRadius = SoundSources[i].Radius; double outerRadius = OuterRadiusFactor * innerRadius; double outerRadiusSquared = outerRadius * outerRadius; if (distanceSquared < outerRadiusSquared) { // sound is in range double gain; double innerRadiusSquared = innerRadius * innerRadius; const double rollOffFactor = 0.9; if (distanceSquared < innerRadiusSquared) { gain = 1.0 - (1.0 - rollOffFactor) * distanceSquared / innerRadiusSquared; } else { double value = distance / outerRadius; gain = innerRadius * rollOffFactor * (1.0 - value * value * value) / distance; } SoundsQueriedPlaying++; SoundsActuallyPlaying++; bool startPlaying = false; // play sound if currently suppressed if (SoundSources[i].Suppressed) { if (SoundSources[i].SoundBufferIndex >= 0) { UseSoundBuffer(SoundSources[i].SoundBufferIndex); if (SoundBuffers[SoundSources[i].SoundBufferIndex].OpenAlBufferIndex.Valid) { int j; Al.alGetError(); Al.alGenSources(1, out j); int err = Al.alGetError(); if (err == Al.AL_NO_ERROR) { SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(j, true); Al.alSourcei(j, Al.AL_BUFFER, SoundBuffers[SoundSources[i].SoundBufferIndex].OpenAlBufferIndex.Index); SoundSources[i].Suppressed = false; startPlaying = true; } else { continue; } } else { StopSound(i, false); continue; } } else { StopSound(i, false); continue; } } // play or stop sound if (startPlaying || IsPlaying(i)) { SoundSources[i].OpenAlPosition[0] = (float)px; SoundSources[i].OpenAlPosition[1] = (float)py; SoundSources[i].OpenAlPosition[2] = (float)pz; if (!SoundSources[i].OpenAlSourceIndex.Valid) { throw new InvalidOperationException("A bug in the sound manager. (9431)"); } int j = SoundSources[i].OpenAlSourceIndex.Index; Al.alSourcefv(j, Al.AL_POSITION, SoundSources[i].OpenAlPosition); Al.alSourcefv(j, Al.AL_VELOCITY, SoundSources[i].OpenAlVelocity); Al.alSourcef(j, Al.AL_PITCH, SoundSources[i].Pitch); float g = SoundSources[i].Gain * SoundSources[i].Gain * (float)gain; if (g > 1.0f) g = 1.0f; Al.alSourcef(j, Al.AL_GAIN, g); } else { StopSound(i, false); continue; } // update position and velocity of sound if (startPlaying) { if (!SoundSources[i].OpenAlSourceIndex.Valid) { throw new InvalidOperationException("A bug in the sound manager. (7625)"); } int j = SoundSources[i].OpenAlSourceIndex.Index; Al.alSourcei(j, Al.AL_LOOPING, SoundSources[i].Looped ? Al.AL_TRUE : Al.AL_FALSE); Al.alSourcef(j, Al.AL_REFERENCE_DISTANCE, SoundBuffers[SoundSources[i].SoundBufferIndex].Radius); Al.alSourcePlay(j); } } else { // sound is not in range if (!SoundSources[i].Suppressed) { if (SoundSources[i].Looped) { if (SoundSources[i].OpenAlSourceIndex.Valid) { int j = SoundSources[i].OpenAlSourceIndex.Index; Al.alSourceStop(j); Al.alDeleteSources(1, ref j); } SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(0, false); SoundSources[i].Suppressed = true; } else { StopSound(i, false); } } else if (!SoundSources[i].Looped) { StopSound(i, false); } } } } } // infrequent updates InternalTimer += TimeElapsed; if (InternalTimer > 1.0) { InternalTimer = 0.0; double Elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double AirTemperature = Game.GetAirTemperature(Elevation); double AirPressure = Game.GetAirPressure(Elevation, AirTemperature); double SpeedOfSound = Game.GetSpeedOfSound(AirPressure, AirTemperature); Al.alSpeedOfSound((float)SpeedOfSound); } } } // use sound buffer private static void UseSoundBuffer(int SoundBufferIndex) { if (OpenAlContext != IntPtr.Zero) { if (SoundBufferIndex >= 0) { if (!SoundBuffers[SoundBufferIndex].TriedLoading) { SoundBuffers[SoundBufferIndex].TriedLoading = true; if (!SoundBuffers[SoundBufferIndex].OpenAlBufferIndex.Valid) { try{ WaveParser.WaveData data = WaveParser.LoadFromFile(SoundBuffers[SoundBufferIndex].FileName); data = WaveParser.ConvertToMono8Or16(data); if (data.Format.BitsPerSample == 8) { int buffer; Al.alGenBuffers(1, out buffer); Al.alBufferData(buffer, Al.AL_FORMAT_MONO8, data.Bytes, data.Bytes.Length, data.Format.SampleRate); SoundBuffers[SoundBufferIndex].OpenAlBufferIndex = new OpenAlIndex(buffer, true); SoundBuffers[SoundBufferIndex].Duration = (double)data.Bytes.Length / (double)(data.Format.SampleRate); } else if (data.Format.BitsPerSample == 16) { int buffer; Al.alGenBuffers(1, out buffer); Al.alBufferData(buffer, Al.AL_FORMAT_MONO16, data.Bytes, data.Bytes.Length, data.Format.SampleRate); SoundBuffers[SoundBufferIndex].OpenAlBufferIndex = new OpenAlIndex(buffer, true); SoundBuffers[SoundBufferIndex].Duration = (double)data.Bytes.Length / (double)(2 * data.Format.SampleRate); } else { SoundBuffers[SoundBufferIndex].OpenAlBufferIndex = new OpenAlIndex(0, false); } } catch { SoundBuffers[SoundBufferIndex].OpenAlBufferIndex = new OpenAlIndex(0, false); } } } } } } // unuse sound buffer private static void UnuseSoundBuffer(int SoundBufferIndex) { if (OpenAlContext != IntPtr.Zero) { if (SoundBuffers[SoundBufferIndex].OpenAlBufferIndex.Valid) { Al.alDeleteBuffers(1, ref SoundBuffers[SoundBufferIndex].OpenAlBufferIndex.Index); SoundBuffers[SoundBufferIndex].OpenAlBufferIndex = new OpenAlIndex(0, false); } } } private static void UnuseAllSoundsBuffers() { if (OpenAlContext != IntPtr.Zero) { for (int i = 0; i < SoundBuffers.Length; i++) { if (SoundBuffers[i] != null) { UnuseSoundBuffer(i); } } } } // load sound internal static int LoadSound(string FileName, double Radius) { if (OpenAlContext != IntPtr.Zero) { int i; for (i = 0; i < SoundBuffers.Length; i++) { if (SoundBuffers[i] != null && string.Compare(SoundBuffers[i].FileName, FileName, StringComparison.OrdinalIgnoreCase) == 0 & SoundBuffers[i].Radius == Radius) { return i; } } if (!FileName.EndsWith(".wav", StringComparison.OrdinalIgnoreCase)) { Interface.AddMessage(Interface.MessageType.Warning, false, "The file extension is not recognized - will be assumed to be a .wav file: " + FileName); } for (i = 0; i < SoundBuffers.Length; i++) { if (SoundBuffers[i] == null) break; } if (i == SoundBuffers.Length) { Array.Resize(ref SoundBuffers, SoundBuffers.Length << 1); } SoundBuffers[i] = new SoundBuffer(); SoundBuffers[i].FileName = FileName; SoundBuffers[i].OpenAlBufferIndex = new OpenAlIndex(0, false); SoundBuffers[i].Radius = (float)Radius; return i; } else { return -1; } } // get sound length internal static double GetSoundLength(int SoundBufferIndex) { if (SoundBuffers[SoundBufferIndex].Duration != 0.0) { return SoundBuffers[SoundBufferIndex].Duration; } else if (OpenAlContext != IntPtr.Zero) { UseSoundBuffer(SoundBufferIndex); return SoundBuffers[SoundBufferIndex].Duration; } else { return 1.0; } } // play sound internal enum Importance { DontCare, AlwaysPlay } internal static void PlaySound(ref int SoundSourceIndex, int SoundBufferIndex, World.Vector3D Position, Importance Important, bool Looped) { PlaySound(ref SoundSourceIndex, true, SoundBufferIndex, null, -1, Position, Important, Looped, 1.0, 1.0); } internal static void PlaySound(int SoundBufferIndex, World.Vector3D Position, Importance Important, bool Looped) { int a = -1; PlaySound(ref a, false, SoundBufferIndex, null, -1, Position, Important, Looped, 1.0, 1.0); } internal static void PlaySound(int SoundBufferIndex, TrainManager.Train Train, int CarIndex, World.Vector3D Position, Importance Important, bool Looped) { int a = -1; PlaySound(ref a, false, SoundBufferIndex, Train, CarIndex, Position, Important, Looped, 1.0, 1.0); } internal static void PlaySound(ref int SoundSourceIndex, int SoundBufferIndex, TrainManager.Train Train, int CarIndex, World.Vector3D Position, Importance Important, bool Looped) { PlaySound(ref SoundSourceIndex, true, SoundBufferIndex, Train, CarIndex, Position, Important, Looped, 1.0, 1.0); } internal static void PlaySound(int SoundBufferIndex, TrainManager.Train Train, int CarIndex, World.Vector3D Position, Importance Important, bool Looped, double Pitch, double Gain) { int a = -1; PlaySound(ref a, false, SoundBufferIndex, Train, CarIndex, Position, Important, Looped, Pitch, Gain); } internal static void PlaySound(ref int SoundSourceIndex, int SoundBufferIndex, TrainManager.Train Train, int CarIndex, World.Vector3D Position, Importance Important, bool Looped, double Pitch, double Gain) { PlaySound(ref SoundSourceIndex, true, SoundBufferIndex, Train, CarIndex, Position, Important, Looped, Pitch, Gain); } private static void PlaySound(ref int SoundSourceIndex, bool ReturnHandle, int SoundBufferIndex, TrainManager.Train Train, int CarIndex, World.Vector3D Position, Importance Important, bool Looped, double Pitch, double Gain) { if (OpenAlContext != IntPtr.Zero) { if (Game.MinimalisticSimulation & Important == Importance.DontCare | SoundBufferIndex == -1) { return; } if (SoundSourceIndex >= 0) { StopSound(ref SoundSourceIndex); } int i; for (i = 0; i < SoundSources.Length; i++) { if (SoundSources[i] == null) break; } if (i >= SoundSources.Length) { Array.Resize(ref SoundSources, SoundSources.Length << 1); } SoundSources[i] = new SoundSourceX(); SoundSources[i].Position = Position; SoundSources[i].OpenAlPosition = new float[] { 0.0f, 0.0f, 0.0f }; SoundSources[i].OpenAlVelocity = new float[] { 0.0f, 0.0f, 0.0f }; SoundSources[i].SoundBufferIndex = SoundBufferIndex; SoundSources[i].Radius = SoundBuffers[SoundBufferIndex].Radius; SoundSources[i].Pitch = (float)Pitch; SoundSources[i].Gain = (float)Gain; SoundSources[i].Looped = Looped; SoundSources[i].Suppressed = true; SoundSources[i].FinishedPlaying = false; SoundSources[i].Train = Train; SoundSources[i].CarIndex = CarIndex; SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(0, false); SoundSources[i].HasHandle = ReturnHandle; SoundSourceIndex = i; } } // modulate sound internal static void ModulateSound(int SoundSourceIndex, double Pitch, double Gain) { if (OpenAlContext != IntPtr.Zero) { if (SoundSourceIndex >= 0 && SoundSources[SoundSourceIndex] != null) { SoundSources[SoundSourceIndex].Pitch = (float)Pitch; SoundSources[SoundSourceIndex].Gain = (float)Gain; } } } // stop sound private static void StopSound(int SoundSourceIndex, bool InvalidateHandle) { if (OpenAlContext != IntPtr.Zero) { if (SoundSources[SoundSourceIndex].HasHandle & !InvalidateHandle) { SoundSources[SoundSourceIndex].FinishedPlaying = true; } else { StopSound(ref SoundSourceIndex); } } } internal static void StopSound(ref int SoundSourceIndex) { if (OpenAlContext != IntPtr.Zero) { if (SoundSourceIndex >= 0 && SoundSourceIndex < SoundSources.Length && SoundSources[SoundSourceIndex] != null) { if (SoundSources[SoundSourceIndex].OpenAlSourceIndex.Valid) { int i = SoundSources[SoundSourceIndex].OpenAlSourceIndex.Index; Al.alSourceStop(i); Al.alDeleteSources(1, ref i); } SoundSources[SoundSourceIndex] = null; } SoundSourceIndex = -1; } } internal static void StopAllSounds(bool InvalidateHandles) { if (OpenAlContext != IntPtr.Zero) { for (int i = 0; i < SoundSources.Length; i++) { if (SoundSources[i] != null) { StopSound(i, InvalidateHandles); } } } } internal static void StopAllSounds(TrainManager.Train Train, bool InvalidateHandles) { if (OpenAlContext != IntPtr.Zero) { for (int i = 0; i < SoundSources.Length; i++) { if (SoundSources[i] != null && SoundSources[i].Train == Train) { StopSound(i, InvalidateHandles); } } } } // is playing internal static bool IsPlaying(int SoundSourceIndex) { if (OpenAlContext != IntPtr.Zero) { if (SoundSourceIndex >= 0 && SoundSourceIndex < SoundSources.Length && SoundSources[SoundSourceIndex] != null) { if (SoundSources[SoundSourceIndex].FinishedPlaying) { return false; } else if (SoundSources[SoundSourceIndex].Suppressed) { return true; } else { if (SoundSources[SoundSourceIndex].OpenAlSourceIndex.Valid) { int i = SoundSources[SoundSourceIndex].OpenAlSourceIndex.Index; int state; Al.alGetSourcei(i, Al.AL_SOURCE_STATE, out state); return state == Al.AL_PLAYING; } else { return false; } } } else { return false; } } else { return false; } } // has finished playing internal static bool HasFinishedPlaying(int SoundSourceIndex) { if (OpenAlContext != IntPtr.Zero) { if (SoundSourceIndex >= 0 && SoundSources[SoundSourceIndex] != null) { return SoundSources[SoundSourceIndex].FinishedPlaying; } else { return true; } } else { return false; } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/Timers.cs000066400000000000000000000007031171674032100213320ustar00rootroot00000000000000using System; using Tao.Sdl; namespace OpenBve { internal static class Timers { // members private static double SdlTime = 0.0; // initialize internal static void Initialize() { SdlTime = 0.001 * (double)Sdl.SDL_GetTicks(); } // get elapsed time internal static double GetElapsedTime() { double a = 0.001 * (double)Sdl.SDL_GetTicks(); double d = a - SdlTime; SdlTime = a; return d; } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/Timetable.cs000066400000000000000000000424611171674032100220040ustar00rootroot00000000000000using System; using System.Drawing; namespace OpenBve { internal static class Timetable { // members (built-in timetable) internal static string DefaultTimetableDescription = ""; internal static Textures.Texture DefaultTimetableTexture = null; internal static double DefaultTimetablePosition = 0.0; // members (custom timetable) internal static ObjectManager.AnimatedObject[] CustomObjects = new ObjectManager.AnimatedObject[16]; internal static int CustomObjectsUsed = 0; internal static Textures.Texture[] CustomTextures = new Textures.Texture[] { }; internal static bool CustomTimetableAvailable = false; internal static Textures.Texture CurrentCustomTimetableDaytimeTexture = null; internal static Textures.Texture CurrentCustomTimetableNighttimeTexture = null; internal static double CustomTimetablePosition = 0.0; // members (interface) internal enum TimetableState { None = 0, Custom = 1, Default = 2 } internal static TimetableState CurrentTimetable = TimetableState.None; // data internal struct Time { internal string Hour; internal string Minute; internal string Second; } internal struct Station { internal string Name; internal bool NameJapanese; internal Time Arrival; internal Time Departure; internal bool Pass; internal bool Terminal; } internal struct Track { internal Time Time; internal string Speed; } internal struct Table { internal Station[] Stations; internal Track[] Tracks; } // create timetable internal static void CreateTimetable() { Table Table; CollectData(out Table); RenderData(ref Table); } // collect data internal static void CollectData(out Table Table) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; Table.Stations = new Station[16]; Table.Tracks = new Track[16]; int n = 0; double Limit = -1.0, LastLimit = 6.94444444444444; int LastArrivalHours = -1, LastDepartureHours = -1; double LastTime = -1.0; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { TrackManager.StationStartEvent sse = TrackManager.CurrentTrack.Elements[i].Events[j] as TrackManager.StationStartEvent; if (sse != null && Game.Stations[sse.StationIndex].Name != string.Empty) { if (Limit == -1.0) Limit = LastLimit; // update station if (n == Table.Stations.Length) { Array.Resize(ref Table.Stations, Table.Stations.Length << 1); } Table.Stations[n].Name = Game.Stations[sse.StationIndex].Name; Table.Stations[n].NameJapanese = Interface.IsJapanese(Game.Stations[sse.StationIndex].Name); Table.Stations[n].Pass = !Game.PlayerStopsAtStation(sse.StationIndex); Table.Stations[n].Terminal = Game.Stations[sse.StationIndex].StationType != Game.StationType.Normal; double x; if (Game.Stations[sse.StationIndex].ArrivalTime >= 0.0) { x = Game.Stations[sse.StationIndex].ArrivalTime; x -= 86400.0 * Math.Floor(x / 86400.0); int hours = (int)Math.Floor(x / 3600.0); x -= 3600.0 * (double)hours; int minutes = (int)Math.Floor(x / 60.0); x -= 60.0 * (double)minutes; int seconds = (int)Math.Floor(x); Table.Stations[n].Arrival.Hour = hours != LastArrivalHours ? hours.ToString("00", Culture) : ""; Table.Stations[n].Arrival.Minute = minutes.ToString("00", Culture); Table.Stations[n].Arrival.Second = seconds.ToString("00", Culture); LastArrivalHours = hours; } else { Table.Stations[n].Arrival.Hour = ""; Table.Stations[n].Arrival.Minute = ""; Table.Stations[n].Arrival.Second = ""; } if (Game.Stations[sse.StationIndex].DepartureTime >= 0.0) { x = Game.Stations[sse.StationIndex].DepartureTime; x -= 86400.0 * Math.Floor(x / 86400.0); int hours = (int)Math.Floor(x / 3600.0); x -= 3600.0 * (double)hours; int minutes = (int)Math.Floor(x / 60.0); x -= 60.0 * (double)minutes; int seconds = (int)Math.Floor(x); Table.Stations[n].Departure.Hour = hours != LastDepartureHours ? hours.ToString("00", Culture) : ""; Table.Stations[n].Departure.Minute = minutes.ToString("00", Culture); Table.Stations[n].Departure.Second = seconds.ToString("00", Culture); LastDepartureHours = hours; } else { Table.Stations[n].Departure.Hour = ""; Table.Stations[n].Departure.Minute = ""; Table.Stations[n].Departure.Second = ""; } // update track if (n >= 1) { int m = n - 1; if (m == Table.Tracks.Length) { Array.Resize(ref Table.Tracks, Table.Tracks.Length << 1); } // speed x = Math.Round(3.6 * Limit); Table.Tracks[m].Speed = x.ToString(Culture); // time if (LastTime >= 0.0) { if (Game.Stations[sse.StationIndex].ArrivalTime >= 0.0) { x = Game.Stations[sse.StationIndex].ArrivalTime; } else if (Game.Stations[sse.StationIndex].DepartureTime >= 0.0) { x = Game.Stations[sse.StationIndex].DepartureTime; } else x = -1.0; if (x >= 0.0) { x -= LastTime; int hours = (int)Math.Floor(x / 3600.0); x -= 3600.0 * (double)hours; int minutes = (int)Math.Floor(x / 60.0); x -= 60.0 * (double)minutes; int seconds = (int)Math.Floor(x); Table.Tracks[m].Time.Hour = hours != 0 ? hours.ToString("0", Culture) : ""; Table.Tracks[m].Time.Minute = minutes != 0 ? minutes.ToString("00", Culture) : ""; Table.Tracks[m].Time.Second = seconds != 0 ? seconds.ToString("00", Culture) : ""; } else { Table.Tracks[m].Time.Hour = ""; Table.Tracks[m].Time.Minute = ""; Table.Tracks[m].Time.Second = ""; } } else { Table.Tracks[m].Time.Hour = ""; Table.Tracks[m].Time.Minute = ""; Table.Tracks[m].Time.Second = ""; } } // update last data if (Game.Stations[sse.StationIndex].DepartureTime >= 0.0) { LastTime = Game.Stations[sse.StationIndex].DepartureTime; } else if (Game.Stations[sse.StationIndex].ArrivalTime >= 0.0) { LastTime = Game.Stations[sse.StationIndex].ArrivalTime; } else { LastTime = -1.0; } LastLimit = Limit; Limit = -1.0; n++; } if (n >= 1) { TrackManager.LimitChangeEvent lce = TrackManager.CurrentTrack.Elements[i].Events[j] as TrackManager.LimitChangeEvent; if (lce != null) { if (lce.NextSpeedLimit != double.PositiveInfinity & lce.NextSpeedLimit > Limit) Limit = lce.NextSpeedLimit; } } } } Array.Resize(ref Table.Stations, n); if (n >= 2) { Array.Resize(ref Table.Tracks, n - 1); } else { Table.Tracks = new Track[] { }; } } // render data private static void RenderData(ref Table Table) { // prepare timetable int w = 384, h = 192; int offsetx = 0; int actualheight = h; float descriptionwidth = 256; float descriptionheight = 16; float stationnamewidth = 16; for (int k = 0; k < 2; k++) { Bitmap b = new Bitmap(w, h); Graphics g = Graphics.FromImage(b); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.Clear(Color.Transparent); g.FillRectangle(Brushes.White, new RectangleF(offsetx, 0, w, actualheight)); Font f = new Font(FontFamily.GenericSansSerif, 13.0f, GraphicsUnit.Pixel); Font fs = new Font(FontFamily.GenericSansSerif, 11.0f, GraphicsUnit.Pixel); Font fss = new Font(FontFamily.GenericSansSerif, 9.0f, GraphicsUnit.Pixel); // draw timetable string t; SizeF s; // description float x0 = offsetx + 8; float y0 = 8; if (k == 1) { t = DefaultTimetableDescription; g.DrawString(t, f, Brushes.Black, new RectangleF(x0, 6, descriptionwidth, descriptionheight + 8)); y0 += descriptionheight + 2; } // highest speed t = Interface.GetInterfaceString("timetable_highestspeed"); s = g.MeasureString(t, fs); g.DrawString(t, fs, Brushes.Black, x0, y0); float y0a = y0 + s.Height + 2; float x1 = x0 + s.Width + 4; for (int i = 0; i < Table.Tracks.Length; i++) { float y = y0a + 18 * i; t = Table.Tracks[i].Speed; g.DrawString(t, f, Brushes.Black, x0, y); s = g.MeasureString(t, f); float x = x0 + s.Width + 4; if (x > x1) x1 = x; } g.DrawLine(Pens.LightGray, new PointF(x1 - 2, 4 + descriptionheight), new PointF(x1 - 2, y0a + 18 * Table.Tracks.Length - 1)); // driving time t = Interface.GetInterfaceString("timetable_drivingtime"); s = g.MeasureString(t, fs); g.DrawString(t, fs, Brushes.Black, x1, y0); float x2 = x1 + s.Width + 4; for (int i = 0; i < Table.Tracks.Length; i++) { float y = y0a + 18 * i; if (Table.Tracks[i].Time.Hour.Length != 0) { t = Table.Tracks[i].Time.Hour; g.DrawString(t, fss, Brushes.Black, x1, y + 2); } else { t = "0"; } s = g.MeasureString(t, fss, 9999, StringFormat.GenericTypographic); float x = x1 + s.Width - 1; if (Table.Tracks[i].Time.Minute.Length != 0) { t = Table.Tracks[i].Time.Minute; g.DrawString(t, fs, Brushes.Black, x, y + 2); } else { t = "00:"; } s = g.MeasureString(t, fs, 9999, StringFormat.GenericTypographic); x += s.Width + 1; t = Table.Tracks[i].Time.Second; g.DrawString(t, fss, Brushes.Black, x, y + 2); s = g.MeasureString(t, fss, 9999, StringFormat.GenericTypographic); x += s.Width + 8; if (x > x2) x2 = x; } for (int i = 0; i < Table.Tracks.Length; i++) { float y = y0a + 18 * i; g.DrawLine(Pens.LightGray, new PointF(offsetx + 4, y - 1), new PointF(x2 - 2, y - 1)); } g.DrawLine(Pens.LightGray, new PointF(x2 - 2, 4 + descriptionheight), new PointF(x2 - 2, y0a + 18 * Table.Tracks.Length - 1)); // station name float y2 = y0; t = Interface.GetInterfaceString("timetable_stationname"); s = g.MeasureString(t, f); g.DrawString(t, f, Brushes.Black, x2, y2); float x3 = x2 + s.Width + 4; for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; g.DrawLine(Pens.LightGray, new PointF(x2 - 2, y - 1), new PointF(w - 4, y - 1)); t = Table.Stations[i].Name; if (Table.Stations[i].NameJapanese & Table.Stations[i].Name.Length > 1) { float[] sizes = new float[t.Length]; float totalsize = 0.0f; for (int j = 0; j < t.Length; j++) { sizes[j] = g.MeasureString(new string(t[j], 1), f, 9999, StringFormat.GenericTypographic).Width; totalsize += sizes[j]; } float space = (stationnamewidth - totalsize) / (float)(t.Length - 1); float x = 0.0f; for (int j = 0; j < t.Length; j++) { g.DrawString(new string(t[j], 1), f, Brushes.Black, x2 + x, y); x += sizes[j] + space; } } else { g.DrawString(t, f, Brushes.Black, x2, y); } s = g.MeasureString(t, f); { float x = x2 + s.Width + 4; if (x > x3) x3 = x; } } g.DrawLine(Pens.LightGray, new PointF(x3 - 2, 4 + descriptionheight), new PointF(x3 - 2, y0 + 18 * (Table.Stations.Length + 1))); if (k == 0) { stationnamewidth = x3 - x2 - 6; } // arrival time t = Interface.GetInterfaceString("timetable_arrivaltime"); s = g.MeasureString(t, f); g.DrawString(t, f, Brushes.Black, x3, y2); float x4 = x3 + s.Width + 4; for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; if (Table.Stations[i].Pass) { t = "00"; s = g.MeasureString(t, fs); float x = x3 + s.Width; t = " ↓"; g.DrawString(t, f, Brushes.Black, x, y); s = g.MeasureString(t, f); x += +s.Width + 4; if (x > x4) x4 = x; } else { if (Table.Stations[i].Arrival.Hour.Length != 0) { t = Table.Stations[i].Arrival.Hour; g.DrawString(t, fs, Brushes.Black, x3, y); } else { t = "00"; } s = g.MeasureString(t, fs); float x = x3 + s.Width; if (Table.Stations[i].Arrival.Minute.Length != 0 & Table.Stations[i].Arrival.Second.Length != 0) { t = Table.Stations[i].Arrival.Minute + ":" + Table.Stations[i].Arrival.Second; } else t = ""; g.DrawString(t, f, Brushes.Black, x, y); s = g.MeasureString(t, f); x += s.Width + 4; if (x > x4) x4 = x; } } g.DrawLine(Pens.LightGray, new PointF(x4 - 2, 4 + descriptionheight), new PointF(x4 - 2, y0 + 18 * (Table.Stations.Length + 1))); // departure time t = Interface.GetInterfaceString("timetable_departuretime"); s = g.MeasureString(t, f); g.DrawString(t, f, Brushes.Black, x4, y2); float x5 = x4 + s.Width + 4; for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; if (Table.Stations[i].Terminal) { t = "00"; s = g.MeasureString(t, fs); float x = x4 + s.Width; const float c0 = 4; const float c1 = 32; g.DrawLine(Pens.Black, new PointF(x + c0, y + 6), new PointF(x + c1, y + 6)); g.DrawLine(Pens.Black, new PointF(x + c0, y + 10), new PointF(x + c1, y + 10)); x += c1 + 4; if (x > x5) x5 = x; } else { if (Table.Stations[i].Departure.Hour.Length != 0) { t = Table.Stations[i].Departure.Hour; g.DrawString(t, fs, Brushes.Black, x4, y); } else { t = "00"; } s = g.MeasureString(t, fs); float x = x4 + s.Width; if (Table.Stations[i].Departure.Minute.Length != 0 & Table.Stations[i].Departure.Second.Length != 0) { t = Table.Stations[i].Departure.Minute + ":" + Table.Stations[i].Departure.Second; } else t = ""; g.DrawString(t, f, Brushes.Black, x, y); s = g.MeasureString(t, f); x += s.Width + 4; if (x > x5) x5 = x; } } for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; g.DrawLine(Pens.LightGray, new PointF(x2 - 2, y - 1), new PointF(w - 4, y - 1)); } // border if (k == 1) { g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4), new PointF(offsetx + 4, y0a + 18 * Table.Tracks.Length - 1)); g.DrawLine(Pens.Black, new PointF(offsetx + 4, y0a + 18 * Table.Tracks.Length - 1), new PointF(x2 - 2, y0a + 18 * Table.Tracks.Length - 1)); g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4), new PointF(w - 4, 4)); g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4 + descriptionheight), new PointF(w - 4, 4 + descriptionheight)); g.DrawLine(Pens.Black, new PointF(x2 - 2, y0 + 18 * (Table.Stations.Length + 1)), new PointF(w - 4, y0 + 18 * (Table.Stations.Length + 1))); g.DrawLine(Pens.Black, new PointF(w - 4, 4), new PointF(w - 4, y0 + 18 * (Table.Stations.Length + 1))); g.DrawLine(Pens.Black, new PointF(x2 - 2, y0a + 18 * Table.Tracks.Length - 1), new PointF(x2 - 2, y0 + 18 * (Table.Stations.Length + 1))); } // measure w = (int)Math.Ceiling((double)(x5 + 1)); h = (int)Math.Ceiling((double)(y0 + 18 * (Table.Stations.Length + 1) + 4)); // description if (k == 0) { t = DefaultTimetableDescription; s = g.MeasureString(t, f, w - 16); descriptionwidth = s.Width; descriptionheight = s.Height + 2; h += (int)Math.Ceiling((double)s.Height) + 4; } // finish if (k == 0) { // measures int nw = Textures.RoundUpToPowerOfTwo(w); offsetx = nw - w; w = nw; actualheight = h; h = Textures.RoundUpToPowerOfTwo(h); } else { // create texture g.Dispose(); DefaultTimetableTexture = Textures.RegisterTexture(b); } } } // update custom timetable internal static void UpdateCustomTimetable(Textures.Texture daytime, Textures.Texture nighttime) { for (int i = 0; i < CustomObjectsUsed; i++) { for (int j = 0; j < CustomObjects[i].States.Length; j++) { for (int k = 0; k < CustomObjects[i].States[j].Object.Mesh.Materials.Length; k++) { if (daytime != null) { CustomObjects[i].States[j].Object.Mesh.Materials[k].DaytimeTexture = daytime; } if (nighttime != null) { CustomObjects[i].States[j].Object.Mesh.Materials[k].NighttimeTexture = nighttime; } } } } if (daytime != null) { CurrentCustomTimetableDaytimeTexture = daytime; } if (nighttime != null) { CurrentCustomTimetableNighttimeTexture = nighttime; } if (CurrentCustomTimetableDaytimeTexture != null | CurrentCustomTimetableNighttimeTexture != null) { CustomTimetableAvailable = true; } else { CustomTimetableAvailable = false; } } // add object for custom timetable internal static void AddObjectForCustomTimetable(ObjectManager.AnimatedObject obj) { if (CustomObjectsUsed >= CustomObjects.Length) { Array.Resize(ref CustomObjects, CustomObjects.Length << 1); } CustomObjects[CustomObjectsUsed] = obj; CustomObjectsUsed++; } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/TrackManager.cs000066400000000000000000001066361171674032100224420ustar00rootroot00000000000000using System; using OpenBveApi.Math; namespace OpenBve { internal static class TrackManager { // events internal enum EventTriggerType { None = 0, Camera = 1, FrontCarFrontAxle = 2, RearCarRearAxle = 3, OtherCarFrontAxle = 4, OtherCarRearAxle = 5, TrainFront = 6 } internal abstract class GeneralEvent { internal double TrackPositionDelta; internal bool DontTriggerAnymore; internal abstract void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex); } internal static void TryTriggerEvent(GeneralEvent Event, int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (!Event.DontTriggerAnymore) { Event.Trigger(Direction, TriggerType, Train, CarIndex); } } // background change internal class BackgroundChangeEvent : GeneralEvent { internal World.Background PreviousBackground; internal World.Background NextBackground; internal BackgroundChangeEvent(double TrackPositionDelta, World.Background PreviousBackground, World.Background NextBackground) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; this.PreviousBackground = PreviousBackground; this.NextBackground = NextBackground; } override internal void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.Camera) { if (Direction < 0) { World.TargetBackground = this.PreviousBackground; World.TargetBackgroundCountdown = World.TargetBackgroundDefaultCountdown; } else if (Direction > 0) { World.TargetBackground = this.NextBackground; World.TargetBackgroundCountdown = World.TargetBackgroundDefaultCountdown; } } } } // fog change internal class FogChangeEvent : GeneralEvent { internal Game.Fog PreviousFog; internal Game.Fog CurrentFog; internal Game.Fog NextFog; internal FogChangeEvent(double TrackPositionDelta, Game.Fog PreviousFog, Game.Fog CurrentFog, Game.Fog NextFog) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; this.PreviousFog = PreviousFog; this.CurrentFog = CurrentFog; this.NextFog = NextFog; } override internal void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.Camera) { if (Direction < 0) { Game.PreviousFog = this.PreviousFog; Game.NextFog = this.CurrentFog; } else if (Direction > 0) { Game.PreviousFog = this.CurrentFog; Game.NextFog = this.NextFog; } } } } // brightness change internal class BrightnessChangeEvent : GeneralEvent { internal float CurrentBrightness; internal float PreviousBrightness; internal double PreviousDistance; internal float NextBrightness; internal double NextDistance; internal BrightnessChangeEvent(double TrackPositionDelta, float CurrentBrightness, float PreviousBrightness, double PreviousDistance, float NextBrightness, double NextDistance) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; this.CurrentBrightness = CurrentBrightness; this.PreviousBrightness = PreviousBrightness; this.PreviousDistance = PreviousDistance; this.NextBrightness = NextBrightness; this.NextDistance = NextDistance; } override internal void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.FrontCarFrontAxle | TriggerType == EventTriggerType.OtherCarFrontAxle) { if (Direction < 0) { //Train.Cars[CarIndex].Brightness.NextBrightness = Train.Cars[CarIndex].Brightness.PreviousBrightness; Train.Cars[CarIndex].Brightness.NextBrightness = this.CurrentBrightness; Train.Cars[CarIndex].Brightness.NextTrackPosition = Train.Cars[CarIndex].FrontAxle.Follower.TrackPosition; Train.Cars[CarIndex].Brightness.PreviousBrightness = this.PreviousBrightness; Train.Cars[CarIndex].Brightness.PreviousTrackPosition = Train.Cars[CarIndex].FrontAxle.Follower.TrackPosition - this.PreviousDistance; } else if (Direction > 0) { //Train.Cars[CarIndex].Brightness.PreviousBrightness = Train.Cars[CarIndex].Brightness.NextBrightness; Train.Cars[CarIndex].Brightness.PreviousBrightness = this.CurrentBrightness; Train.Cars[CarIndex].Brightness.PreviousTrackPosition = Train.Cars[CarIndex].FrontAxle.Follower.TrackPosition; Train.Cars[CarIndex].Brightness.NextBrightness = this.NextBrightness; Train.Cars[CarIndex].Brightness.NextTrackPosition = Train.Cars[CarIndex].FrontAxle.Follower.TrackPosition + this.NextDistance; } } } } // marker start internal class MarkerStartEvent : GeneralEvent { internal Textures.Texture Texture; internal MarkerStartEvent(double trackPositionDelta, Textures.Texture texture) { this.TrackPositionDelta = trackPositionDelta; this.DontTriggerAnymore = false; this.Texture = texture; } override internal void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (Train == TrainManager.PlayerTrain & TriggerType == EventTriggerType.FrontCarFrontAxle) { if (Direction < 0) { Game.RemoveMarker(this.Texture); } else if (Direction > 0) { Game.AddMarker(this.Texture); } } } } // marker end internal class MarkerEndEvent : GeneralEvent { internal Textures.Texture Texture; internal MarkerEndEvent(double trackPositionDelta, Textures.Texture texture) { this.TrackPositionDelta = trackPositionDelta; this.DontTriggerAnymore = false; this.Texture = texture; } override internal void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (Train == TrainManager.PlayerTrain & TriggerType == EventTriggerType.FrontCarFrontAxle) { if (Direction < 0) { Game.AddMarker(this.Texture); } else if (Direction > 0) { Game.RemoveMarker(this.Texture); } } } } // station pass alarm internal class StationPassAlarmEvent : GeneralEvent { internal StationPassAlarmEvent(double TrackPositionDelta) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; } override internal void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.FrontCarFrontAxle) { if (Direction > 0) { int d = Train.DriverCar; Sounds.SoundBuffer buffer = Train.Cars[d].Sounds.Halt.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[d].Sounds.Halt.Position; if (Train.Specs.PassAlarm == TrainManager.PassAlarmType.Single) { Train.Cars[d].Sounds.Halt.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, d, false); } else if (Train.Specs.PassAlarm == TrainManager.PassAlarmType.Loop) { Train.Cars[d].Sounds.Halt.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, d, true); } } this.DontTriggerAnymore = true; } } } } // station start internal class StationStartEvent : GeneralEvent { internal int StationIndex; internal StationStartEvent(double TrackPositionDelta, int StationIndex) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; this.StationIndex = StationIndex; } override internal void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.TrainFront) { if (Direction < 0) { Train.Station = -1; Train.StationFrontCar = false; } else if (Direction > 0) { Train.Station = StationIndex; Train.StationFrontCar = true; Train.StationState = TrainManager.TrainStopState.Pending; Train.LastStation = this.StationIndex; } } else if (TriggerType == EventTriggerType.RearCarRearAxle) { if (Direction < 0) { Train.StationRearCar = false; } else { Train.StationRearCar = true; } } } } // station end internal class StationEndEvent : GeneralEvent { internal int StationIndex; internal StationEndEvent(double TrackPositionDelta, int StationIndex) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; this.StationIndex = StationIndex; } override internal void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.FrontCarFrontAxle) { if (Direction < 0) { Train.StationFrontCar = true; } else if (Direction > 0) { Train.StationFrontCar = false; if (Train == TrainManager.PlayerTrain) { Timetable.UpdateCustomTimetable(Game.Stations[this.StationIndex].TimetableDaytimeTexture, Game.Stations[this.StationIndex].TimetableNighttimeTexture); } } } else if (TriggerType == EventTriggerType.RearCarRearAxle) { if (Direction < 0) { Train.Station = this.StationIndex; Train.StationRearCar = true; Train.LastStation = this.StationIndex; } else if (Direction > 0) { if (Train.Station == StationIndex) { if (Train == TrainManager.PlayerTrain) { if (Game.PlayerStopsAtStation(StationIndex) & TrainManager.PlayerTrain.StationState == TrainManager.TrainStopState.Pending) { string s = Interface.GetInterfaceString("message_station_passed"); s = s.Replace("[name]", Game.Stations[StationIndex].Name); Game.AddMessage(s, Game.MessageDependency.None, Interface.GameMode.Normal, Game.MessageColor.Orange, Game.SecondsSinceMidnight + 10.0); } else if (Game.PlayerStopsAtStation(StationIndex) & TrainManager.PlayerTrain.StationState == TrainManager.TrainStopState.Boarding) { string s = Interface.GetInterfaceString("message_station_passed_boarding"); s = s.Replace("[name]", Game.Stations[StationIndex].Name); Game.AddMessage(s, Game.MessageDependency.None, Interface.GameMode.Normal, Game.MessageColor.Red, Game.SecondsSinceMidnight + 10.0); } } Train.Station = -1; Train.StationRearCar = false; Train.StationState = TrainManager.TrainStopState.Pending; int d = Train.DriverCar; Sounds.StopSound(Train.Cars[d].Sounds.Halt.Source); } } } } } // section change internal class SectionChangeEvent : GeneralEvent { internal int PreviousSectionIndex; internal int NextSectionIndex; internal SectionChangeEvent(double TrackPositionDelta, int PreviousSectionIndex, int NextSectionIndex) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; this.PreviousSectionIndex = PreviousSectionIndex; this.NextSectionIndex = NextSectionIndex; } internal override void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (Train != null) { if (TriggerType == EventTriggerType.FrontCarFrontAxle) { if (Direction < 0) { if (this.NextSectionIndex >= 0) { Game.Sections[this.NextSectionIndex].TrainReachedStopPoint = false; } UpdateFrontBackward(Train, true); } else if (Direction > 0) { UpdateFrontForward(Train, true, true); } } else if (TriggerType == EventTriggerType.RearCarRearAxle) { if (Direction < 0) { UpdateRearBackward(Train, true); } else if (Direction > 0) { if (this.PreviousSectionIndex >= 0) { Game.Sections[this.PreviousSectionIndex].TrainReachedStopPoint = false; } UpdateRearForward(Train, true); } } } } private void UpdateFrontBackward(TrainManager.Train Train, bool UpdateTrain) { // update sections if (this.PreviousSectionIndex >= 0) { Game.Sections[this.PreviousSectionIndex].Enter(Train); Game.UpdateSection(this.PreviousSectionIndex); } if (this.NextSectionIndex >= 0) { Game.Sections[this.NextSectionIndex].Leave(Train); Game.UpdateSection(this.NextSectionIndex); } if (UpdateTrain) { // update train if (this.PreviousSectionIndex >= 0) { if (!Game.Sections[this.PreviousSectionIndex].Invisible) { Train.CurrentSectionIndex = this.PreviousSectionIndex; } } else { Train.CurrentSectionLimit = double.PositiveInfinity; Train.CurrentSectionIndex = -1; } } } private void UpdateFrontForward(TrainManager.Train Train, bool UpdateTrain, bool UpdateSection) { if (UpdateTrain) { // update train if (this.NextSectionIndex >= 0) { if (!Game.Sections[this.NextSectionIndex].Invisible) { if (Game.Sections[this.NextSectionIndex].CurrentAspect >= 0) { Train.CurrentSectionLimit = Game.Sections[this.NextSectionIndex].Aspects[Game.Sections[this.NextSectionIndex].CurrentAspect].Speed; } else { Train.CurrentSectionLimit = double.PositiveInfinity; } Train.CurrentSectionIndex = this.NextSectionIndex; } } else { Train.CurrentSectionLimit = double.PositiveInfinity; Train.CurrentSectionIndex = -1; } // messages if (this.NextSectionIndex < 0 || !Game.Sections[this.NextSectionIndex].Invisible) { if (Train.CurrentSectionLimit == 0.0) { Game.AddMessage(Interface.GetInterfaceString("message_signal_stop"), Game.MessageDependency.SectionLimit, Interface.GameMode.Normal, Game.MessageColor.Red, double.PositiveInfinity); } else if (Train.Specs.CurrentAverageSpeed > Train.CurrentSectionLimit) { Game.AddMessage(Interface.GetInterfaceString("message_signal_overspeed"), Game.MessageDependency.SectionLimit, Interface.GameMode.Normal, Game.MessageColor.Orange, double.PositiveInfinity); } } } if (UpdateSection) { // update sections if (this.NextSectionIndex >= 0) { Game.Sections[this.NextSectionIndex].Enter(Train); Game.UpdateSection(this.NextSectionIndex); } } } private void UpdateRearBackward(TrainManager.Train Train, bool UpdateSection) { if (UpdateSection) { // update sections if (this.PreviousSectionIndex >= 0) { Game.Sections[this.PreviousSectionIndex].Enter(Train); Game.UpdateSection(this.PreviousSectionIndex); } } } private void UpdateRearForward(TrainManager.Train Train, bool UpdateSection) { if (UpdateSection) { // update sections if (this.PreviousSectionIndex >= 0) { Game.Sections[this.PreviousSectionIndex].Leave(Train); Game.UpdateSection(this.PreviousSectionIndex); } if (this.NextSectionIndex >= 0) { Game.Sections[this.NextSectionIndex].Enter(Train); Game.UpdateSection(this.NextSectionIndex); } } } } // transponder internal static class SpecialTransponderTypes { /// Marks the status of ATC. internal const int AtcTrackStatus = -16777215; /// Sets up an ATC speed limit. internal const int AtcSpeedLimit = -16777214; /// Sets up an ATS-P temporary speed limit. internal const int AtsPTemporarySpeedLimit = -16777213; /// Sets up an ATS-P permanent speed limit. internal const int AtsPPermanentSpeedLimit = -16777212; /// For internal use inside the CSV/RW parser only. internal const int InternalAtsPTemporarySpeedLimit = -16777201; } // internal enum TransponderSpecialSection { // NextRedSection = -2, // } internal class TransponderEvent : GeneralEvent { internal int Type; internal int Data; internal int SectionIndex; internal TransponderEvent(double trackPositionDelta, int type, int data, int sectionIndex) { this.TrackPositionDelta = trackPositionDelta; this.DontTriggerAnymore = false; this.Type = type; this.Data = data; this.SectionIndex = sectionIndex; } internal override void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.TrainFront) { int s = this.SectionIndex; if (s >= 0) { while (true) { if (Game.Sections[s].Exists(Train)) { s = this.SectionIndex; break; } int a = Game.Sections[s].CurrentAspect; if (a >= 0) { if (Game.Sections[s].Aspects[a].Number == 0) { break; } } s = Game.Sections[s].PreviousSection; if (s < 0) { s = this.SectionIndex; break; } } } if (Train.Plugin != null) { Train.Plugin.UpdateBeacon((int)this.Type, s, this.Data); } } } } // limit change internal class LimitChangeEvent : GeneralEvent { internal double PreviousSpeedLimit; internal double NextSpeedLimit; internal LimitChangeEvent(double TrackPositionDelta, double PreviousSpeedLimit, double NextSpeedLimit) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; this.PreviousSpeedLimit = PreviousSpeedLimit; this.NextSpeedLimit = NextSpeedLimit; } internal override void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (Direction < 0) { if (TriggerType == EventTriggerType.FrontCarFrontAxle) { int n = Train.RouteLimits.Length; if (n > 0) { Array.Resize(ref Train.RouteLimits, n - 1); Train.CurrentRouteLimit = double.PositiveInfinity; for (int i = 0; i < n - 1; i++) { if (Train.RouteLimits[i] < Train.CurrentRouteLimit) { Train.CurrentRouteLimit = Train.RouteLimits[i]; } } } } else if (TriggerType == EventTriggerType.RearCarRearAxle) { int n = Train.RouteLimits.Length; Array.Resize(ref Train.RouteLimits, n + 1); for (int i = n; i > 0; i--) { Train.RouteLimits[i] = Train.RouteLimits[i - 1]; } Train.RouteLimits[0] = this.PreviousSpeedLimit; } } else if (Direction > 0) { if (TriggerType == EventTriggerType.FrontCarFrontAxle) { int n = Train.RouteLimits.Length; Array.Resize(ref Train.RouteLimits, n + 1); Train.RouteLimits[n] = this.NextSpeedLimit; if (this.NextSpeedLimit < Train.CurrentRouteLimit) { Train.CurrentRouteLimit = this.NextSpeedLimit; } if (Train.Specs.CurrentAverageSpeed > this.NextSpeedLimit) { Game.AddMessage(Interface.GetInterfaceString("message_route_overspeed"), Game.MessageDependency.RouteLimit, Interface.GameMode.Normal, Game.MessageColor.Orange, double.PositiveInfinity); } } else if (TriggerType == EventTriggerType.RearCarRearAxle) { int n = Train.RouteLimits.Length; if (n > 0) { Train.CurrentRouteLimit = double.PositiveInfinity; for (int i = 0; i < n - 1; i++) { Train.RouteLimits[i] = Train.RouteLimits[i + 1]; if (Train.RouteLimits[i] < Train.CurrentRouteLimit) { Train.CurrentRouteLimit = Train.RouteLimits[i]; } } Array.Resize(ref Train.RouteLimits, n - 1); } } } } } // sound internal static bool SuppressSoundEvents = false; internal class SoundEvent : GeneralEvent { /// HACK: Set to a null reference to indicate the train point sound. internal Sounds.SoundBuffer SoundBuffer; internal bool PlayerTrainOnly; internal bool Once; internal bool Dynamic; internal Vector3 Position; internal double Speed; /// HACK: Set to a null reference to indicate the train point sound. internal SoundEvent(double TrackPositionDelta, Sounds.SoundBuffer SoundBuffer, bool PlayerTrainOnly, bool Once, bool Dynamic, Vector3 Position, double Speed) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; this.SoundBuffer = SoundBuffer; this.PlayerTrainOnly = PlayerTrainOnly; this.Once = Once; this.Dynamic = Dynamic; this.Position = Position; this.Speed = Speed; } internal override void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (SuppressSoundEvents) return; if (TriggerType == EventTriggerType.FrontCarFrontAxle | TriggerType == EventTriggerType.OtherCarFrontAxle | TriggerType == EventTriggerType.OtherCarRearAxle | TriggerType == EventTriggerType.RearCarRearAxle) { if (!PlayerTrainOnly | Train == TrainManager.PlayerTrain) { Vector3 p = this.Position; double pitch = 1.0; double gain = 1.0; Sounds.SoundBuffer buffer = this.SoundBuffer; if (buffer == null) { // HACK: Represents the train point sound if (TriggerType == EventTriggerType.FrontCarFrontAxle | TriggerType == EventTriggerType.OtherCarFrontAxle) { if (Train.Specs.CurrentAverageSpeed <= 0.0) return; buffer = Train.Cars[CarIndex].Sounds.PointFrontAxle.Buffer; p = Train.Cars[CarIndex].Sounds.PointFrontAxle.Position; } else { return; // HACK: Don't trigger sound for the rear axles //buffer = Train.Cars[CarIndex].Sounds.PointRearAxle.Buffer; //p = Train.Cars[CarIndex].Sounds.PointRearAxle.Position; } } if (buffer != null) { if (this.Dynamic) { double spd = Math.Abs(Train.Specs.CurrentAverageSpeed); pitch = spd / this.Speed; gain = pitch < 0.5 ? 2.0 * pitch : 1.0; if (pitch < 0.2 | gain < 0.2) { buffer = null; } } if (buffer != null) { Sounds.PlaySound(buffer, pitch, gain, p, Train, CarIndex, false); } } this.DontTriggerAnymore = this.Once; } } } internal const int SoundIndexTrainPoint = -2; } // rail sounds change internal class RailSoundsChangeEvent : GeneralEvent { internal int PreviousRunIndex; internal int PreviousFlangeIndex; internal int NextRunIndex; internal int NextFlangeIndex; internal RailSoundsChangeEvent(double TrackPositionDelta, int PreviousRunIndex, int PreviousFlangeIndex, int NextRunIndex, int NextFlangeIndex) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; this.PreviousRunIndex = PreviousRunIndex; this.PreviousFlangeIndex = PreviousFlangeIndex; this.NextRunIndex = NextRunIndex; this.NextFlangeIndex = NextFlangeIndex; } internal override void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.FrontCarFrontAxle | TriggerType == EventTriggerType.OtherCarFrontAxle) { if (Direction < 0) { Train.Cars[CarIndex].Sounds.FrontAxleRunIndex = this.PreviousRunIndex; Train.Cars[CarIndex].Sounds.FrontAxleFlangeIndex = this.PreviousFlangeIndex; } else if (Direction > 0) { Train.Cars[CarIndex].Sounds.FrontAxleRunIndex = this.NextRunIndex; Train.Cars[CarIndex].Sounds.FrontAxleFlangeIndex = this.NextFlangeIndex; } } else if (TriggerType == EventTriggerType.RearCarRearAxle | TriggerType == EventTriggerType.OtherCarRearAxle) { if (Direction < 0) { Train.Cars[CarIndex].Sounds.RearAxleRunIndex = this.PreviousRunIndex; Train.Cars[CarIndex].Sounds.RearAxleFlangeIndex = this.PreviousFlangeIndex; } else if (Direction > 0) { Train.Cars[CarIndex].Sounds.RearAxleRunIndex = this.NextRunIndex; Train.Cars[CarIndex].Sounds.RearAxleFlangeIndex = this.NextFlangeIndex; } } } } // track end internal class TrackEndEvent : GeneralEvent { internal TrackEndEvent(double TrackPositionDelta) { this.TrackPositionDelta = TrackPositionDelta; this.DontTriggerAnymore = false; } internal override void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.RearCarRearAxle & Train != TrainManager.PlayerTrain) { TrainManager.DisposeTrain(Train); } else if (Train == TrainManager.PlayerTrain) { Train.Cars[CarIndex].Derailed = true; } } } // ================================ // track element internal struct TrackElement { internal double StartingTrackPosition; internal double CurveRadius; internal double CurveCant; internal double CurveCantTangent; internal double AdhesionMultiplier; internal double CsvRwAccuracyLevel; internal Vector3 WorldPosition; internal Vector3 WorldDirection; internal Vector3 WorldUp; internal Vector3 WorldSide; internal GeneralEvent[] Events; internal TrackElement(double StartingTrackPosition) { this.StartingTrackPosition = StartingTrackPosition; this.CurveRadius = 0.0; this.CurveCant = 0.0; this.CurveCantTangent = 0.0; this.AdhesionMultiplier = 1.0; this.CsvRwAccuracyLevel = 2.0; this.WorldPosition = new Vector3(0.0, 0.0, 0.0); this.WorldDirection = new Vector3(0.0, 0.0, 1.0); this.WorldUp = new Vector3(0.0, 1.0, 0.0); this.WorldSide = new Vector3(1.0, 0.0, 0.0); this.Events = new GeneralEvent[] { }; } } // track internal struct Track { internal TrackElement[] Elements; } internal static Track CurrentTrack; // track follower internal struct TrackFollower { internal int LastTrackElement; internal double TrackPosition; internal Vector3 WorldPosition; internal Vector3 WorldDirection; internal Vector3 WorldUp; internal Vector3 WorldSide; internal double CurveRadius; internal double CurveCant; internal double CantDueToInaccuracy; internal double AdhesionMultiplier; internal EventTriggerType TriggerType; internal TrainManager.Train Train; internal int CarIndex; internal void UpdateWorldCoordinates(bool AddTrackInaccuracy) { UpdateTrackFollower(ref this, this.TrackPosition, true, AddTrackInaccuracy); } } internal static void UpdateTrackFollower(ref TrackFollower Follower, double NewTrackPosition, bool UpdateWorldCoordinates, bool AddTrackInaccurary) { if (CurrentTrack.Elements.Length == 0) return; int i = Follower.LastTrackElement; while (i >= 0 && NewTrackPosition < CurrentTrack.Elements[i].StartingTrackPosition) { double ta = Follower.TrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; double tb = -0.01; CheckEvents(ref Follower, i, -1, ta, tb); i--; } if (i >= 0) { while (i < CurrentTrack.Elements.Length - 1) { if (NewTrackPosition < CurrentTrack.Elements[i + 1].StartingTrackPosition) break; double ta = Follower.TrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; double tb = CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition + 0.01; CheckEvents(ref Follower, i, 1, ta, tb); i++; } } else { i = 0; } double da = Follower.TrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; double db = NewTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition; // track if (UpdateWorldCoordinates) { if (db != 0.0) { if (CurrentTrack.Elements[i].CurveRadius != 0.0) { // curve double r = CurrentTrack.Elements[i].CurveRadius; double p = CurrentTrack.Elements[i].WorldDirection.Y / Math.Sqrt(CurrentTrack.Elements[i].WorldDirection.X * CurrentTrack.Elements[i].WorldDirection.X + CurrentTrack.Elements[i].WorldDirection.Z * CurrentTrack.Elements[i].WorldDirection.Z); double s = db / Math.Sqrt(1.0 + p * p); double h = s * p; double b = s / Math.Abs(r); double f = 2.0 * r * r * (1.0 - Math.Cos(b)); double c = (double)Math.Sign(db) * Math.Sqrt(f >= 0.0 ? f : 0.0); double a = 0.5 * (double)Math.Sign(r) * b; Vector3 D = new Vector3(CurrentTrack.Elements[i].WorldDirection.X, 0.0, CurrentTrack.Elements[i].WorldDirection.Z); World.Normalize(ref D.X, ref D.Y, ref D.Z); double cosa = Math.Cos(a); double sina = Math.Sin(a); World.Rotate(ref D.X, ref D.Y, ref D.Z, 0.0, 1.0, 0.0, cosa, sina); Follower.WorldPosition.X = CurrentTrack.Elements[i].WorldPosition.X + c * D.X; Follower.WorldPosition.Y = CurrentTrack.Elements[i].WorldPosition.Y + h; Follower.WorldPosition.Z = CurrentTrack.Elements[i].WorldPosition.Z + c * D.Z; World.Rotate(ref D.X, ref D.Y, ref D.Z, 0.0, 1.0, 0.0, cosa, sina); Follower.WorldDirection.X = D.X; Follower.WorldDirection.Y = p; Follower.WorldDirection.Z = D.Z; World.Normalize(ref Follower.WorldDirection.X, ref Follower.WorldDirection.Y, ref Follower.WorldDirection.Z); double cos2a = Math.Cos(2.0 * a); double sin2a = Math.Sin(2.0 * a); Follower.WorldSide = CurrentTrack.Elements[i].WorldSide; World.Rotate(ref Follower.WorldSide.X, ref Follower.WorldSide.Y, ref Follower.WorldSide.Z, 0.0, 1.0, 0.0, cos2a, sin2a); World.Cross(Follower.WorldDirection.X, Follower.WorldDirection.Y, Follower.WorldDirection.Z, Follower.WorldSide.X, Follower.WorldSide.Y, Follower.WorldSide.Z, out Follower.WorldUp.X, out Follower.WorldUp.Y, out Follower.WorldUp.Z); Follower.CurveRadius = CurrentTrack.Elements[i].CurveRadius; } else { // straight Follower.WorldPosition.X = CurrentTrack.Elements[i].WorldPosition.X + db * CurrentTrack.Elements[i].WorldDirection.X; Follower.WorldPosition.Y = CurrentTrack.Elements[i].WorldPosition.Y + db * CurrentTrack.Elements[i].WorldDirection.Y; Follower.WorldPosition.Z = CurrentTrack.Elements[i].WorldPosition.Z + db * CurrentTrack.Elements[i].WorldDirection.Z; Follower.WorldDirection = CurrentTrack.Elements[i].WorldDirection; Follower.WorldUp = CurrentTrack.Elements[i].WorldUp; Follower.WorldSide = CurrentTrack.Elements[i].WorldSide; Follower.CurveRadius = 0.0; } // cant if (i < CurrentTrack.Elements.Length - 1) { double t = db / (CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition); if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } double t2 = t * t; double t3 = t2 * t; Follower.CurveCant = (2.0 * t3 - 3.0 * t2 + 1.0) * CurrentTrack.Elements[i].CurveCant + (t3 - 2.0 * t2 + t) * CurrentTrack.Elements[i].CurveCantTangent + (-2.0 * t3 + 3.0 * t2) * CurrentTrack.Elements[i + 1].CurveCant + (t3 - t2) * CurrentTrack.Elements[i + 1].CurveCantTangent; } else { Follower.CurveCant = CurrentTrack.Elements[i].CurveCant; } } else { Follower.WorldPosition = CurrentTrack.Elements[i].WorldPosition; Follower.WorldDirection = CurrentTrack.Elements[i].WorldDirection; Follower.WorldUp = CurrentTrack.Elements[i].WorldUp; Follower.WorldSide = CurrentTrack.Elements[i].WorldSide; Follower.CurveRadius = CurrentTrack.Elements[i].CurveRadius; Follower.CurveCant = CurrentTrack.Elements[i].CurveCant; } } else { if (db != 0.0) { if (CurrentTrack.Elements[i].CurveRadius != 0.0) { Follower.CurveRadius = CurrentTrack.Elements[i].CurveRadius; } else { Follower.CurveRadius = 0.0; } if (i < CurrentTrack.Elements.Length - 1) { double t = db / (CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition); if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } double t2 = t * t; double t3 = t2 * t; Follower.CurveCant = (2.0 * t3 - 3.0 * t2 + 1.0) * CurrentTrack.Elements[i].CurveCant + (t3 - 2.0 * t2 + t) * CurrentTrack.Elements[i].CurveCantTangent + (-2.0 * t3 + 3.0 * t2) * CurrentTrack.Elements[i + 1].CurveCant + (t3 - t2) * CurrentTrack.Elements[i + 1].CurveCantTangent; } else { Follower.CurveCant = CurrentTrack.Elements[i].CurveCant; } } else { Follower.CurveRadius = CurrentTrack.Elements[i].CurveRadius; Follower.CurveCant = CurrentTrack.Elements[i].CurveCant; } } Follower.AdhesionMultiplier = CurrentTrack.Elements[i].AdhesionMultiplier; // inaccuracy if (AddTrackInaccurary) { double x, y, c; if (i < CurrentTrack.Elements.Length - 1) { double t = db / (CurrentTrack.Elements[i + 1].StartingTrackPosition - CurrentTrack.Elements[i].StartingTrackPosition); if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } double x1, y1, c1; double x2, y2, c2; GetInaccuracies(NewTrackPosition, CurrentTrack.Elements[i].CsvRwAccuracyLevel, out x1, out y1, out c1); GetInaccuracies(NewTrackPosition, CurrentTrack.Elements[i + 1].CsvRwAccuracyLevel, out x2, out y2, out c2); x = (1.0 - t) * x1 + t * x2; y = (1.0 - t) * y1 + t * y2; c = (1.0 - t) * c1 + t * c2; } else { GetInaccuracies(NewTrackPosition, CurrentTrack.Elements[i].CsvRwAccuracyLevel, out x, out y, out c); } Follower.WorldPosition.X += x * Follower.WorldSide.X + y * Follower.WorldUp.X; Follower.WorldPosition.Y += x * Follower.WorldSide.Y + y * Follower.WorldUp.Y; Follower.WorldPosition.Z += x * Follower.WorldSide.Z + y * Follower.WorldUp.Z; Follower.CurveCant += c; Follower.CantDueToInaccuracy = c; } else { Follower.CantDueToInaccuracy = 0.0; } // events CheckEvents(ref Follower, i, Math.Sign(db - da), da, db); // finish Follower.TrackPosition = NewTrackPosition; Follower.LastTrackElement = i; } // get inaccuracies private static void GetInaccuracies(double position, double inaccuracy, out double x, out double y, out double c) { if (inaccuracy <= 0.0) { x = 0.0; y = 0.0; c = 0.0; } else { double z = Math.Pow(0.25 * inaccuracy, 1.2) * position; x = 0.14 * Math.Sin(0.5843 * z) + 0.82 * Math.Sin(0.2246 * z) + 0.55 * Math.Sin(0.1974 * z); x *= 0.0035 * Game.RouteRailGauge * inaccuracy; y = 0.18 * Math.Sin(0.5172 * z) + 0.37 * Math.Sin(0.3251 * z) + 0.91 * Math.Sin(0.3773 * z); y *= 0.0020 * Game.RouteRailGauge * inaccuracy; c = 0.23 * Math.Sin(0.3131 * z) + 0.54 * Math.Sin(0.5807 * z) + 0.81 * Math.Sin(0.3621 * z); c *= 0.0025 * Game.RouteRailGauge * inaccuracy; } } // check events private static void CheckEvents(ref TrackFollower Follower, int ElementIndex, int Direction, double OldDelta, double NewDelta) { if (Follower.TriggerType != EventTriggerType.None) { if (Direction < 0) { for (int j = CurrentTrack.Elements[ElementIndex].Events.Length - 1; j >= 0; j--) { if (OldDelta > CurrentTrack.Elements[ElementIndex].Events[j].TrackPositionDelta & NewDelta <= CurrentTrack.Elements[ElementIndex].Events[j].TrackPositionDelta) { TryTriggerEvent(CurrentTrack.Elements[ElementIndex].Events[j], -1, Follower.TriggerType, Follower.Train, Follower.CarIndex); } } } else if (Direction > 0) { for (int j = 0; j < CurrentTrack.Elements[ElementIndex].Events.Length; j++) { if (OldDelta < CurrentTrack.Elements[ElementIndex].Events[j].TrackPositionDelta & NewDelta >= CurrentTrack.Elements[ElementIndex].Events[j].TrackPositionDelta) { TryTriggerEvent(CurrentTrack.Elements[ElementIndex].Events[j], 1, Follower.TriggerType, Follower.Train, Follower.CarIndex); } } } } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/TrainManager.cs000066400000000000000000005414221171674032100224470ustar00rootroot00000000000000using System; using OpenBveApi.Math; namespace OpenBve { internal static class TrainManager { // axle internal struct Axle { internal TrackManager.TrackFollower Follower; internal bool CurrentWheelSlip; } // coupler internal struct Coupler { internal double MinimumDistanceBetweenCars; internal double MaximumDistanceBetweenCars; } // sections internal struct CarSection { internal ObjectManager.AnimatedObject[] Elements; internal bool Overlay; } // cars internal struct Door { /// A value of -1 (left) or 1 (right). internal int Direction; /// A value between 0 (closed) and 1 (opened). internal double State; /// The value of the state at which a door lock simulation is scheduled. internal double DoorLockState; /// The duration of the scheduled door lock simulation. internal double DoorLockDuration; } internal struct AccelerationCurve { internal double StageZeroAcceleration; internal double StageOneSpeed; internal double StageOneAcceleration; internal double StageTwoSpeed; internal double StageTwoExponent; } internal enum CarBrakeType { ElectromagneticStraightAirBrake = 0, ElectricCommandBrake = 1, AutomaticAirBrake = 2 } internal enum EletropneumaticBrakeType { None = 0, ClosingElectromagneticValve = 1, DelayFillingControl = 2 } internal enum AirBrakeHandleState { Invalid = -1, Release = 0, Lap = 1, Service = 2, } internal struct AirBrakeHandle { internal AirBrakeHandleState Driver; internal AirBrakeHandleState Safety; internal AirBrakeHandleState Actual; internal AirBrakeHandleState DelayedValue; internal double DelayedTime; } internal enum AirBrakeType { Main, Auxillary } internal struct CarAirBrake { internal AirBrakeType Type; internal bool AirCompressorEnabled; internal double AirCompressorMinimumPressure; internal double AirCompressorMaximumPressure; internal double AirCompressorRate; internal double MainReservoirCurrentPressure; internal double MainReservoirEqualizingReservoirCoefficient; internal double MainReservoirBrakePipeCoefficient; internal double EqualizingReservoirCurrentPressure; internal double EqualizingReservoirNormalPressure; internal double EqualizingReservoirServiceRate; internal double EqualizingReservoirEmergencyRate; internal double EqualizingReservoirChargeRate; internal double BrakePipeCurrentPressure; internal double BrakePipeNormalPressure; internal double BrakePipeFlowSpeed; internal double BrakePipeChargeRate; internal double BrakePipeServiceRate; internal double BrakePipeEmergencyRate; internal double AuxillaryReservoirCurrentPressure; internal double AuxillaryReservoirMaximumPressure; internal double AuxillaryReservoirChargeRate; internal double AuxillaryReservoirBrakePipeCoefficient; internal double AuxillaryReservoirBrakeCylinderCoefficient; internal double BrakeCylinderCurrentPressure; internal double BrakeCylinderEmergencyMaximumPressure; internal double BrakeCylinderServiceMaximumPressure; internal double BrakeCylinderEmergencyChargeRate; internal double BrakeCylinderServiceChargeRate; internal double BrakeCylinderReleaseRate; internal double BrakeCylinderSoundPlayedForPressure; internal double StraightAirPipeCurrentPressure; internal double StraightAirPipeReleaseRate; internal double StraightAirPipeServiceRate; internal double StraightAirPipeEmergencyRate; } internal struct CarHoldBrake { internal double CurrentAccelerationOutput; internal double NextUpdateTime; internal double UpdateInterval; } internal struct CarConstSpeed { internal double CurrentAccelerationOutput; internal double NextUpdateTime; internal double UpdateInterval; } internal struct CarReAdhesionDevice { internal double UpdateInterval; internal double MaximumAccelerationOutput; internal double ApplicationFactor; internal double ReleaseInterval; internal double ReleaseFactor; internal double NextUpdateTime; internal double TimeStable; } internal struct CarSpecs { /// motor internal bool IsMotorCar; internal AccelerationCurve[] AccelerationCurves; internal double AccelerationCurvesMultiplier; internal double AccelerationCurveMaximum; internal double JerkPowerUp; internal double JerkPowerDown; internal double JerkBrakeUp; internal double JerkBrakeDown; /// brake internal double BrakeDecelerationAtServiceMaximumPressure; internal double BrakeControlSpeed; internal double MotorDeceleration; /// physical properties internal double MassEmpty; internal double MassCurrent; internal double ExposedFrontalArea; internal double UnexposedFrontalArea; internal double CoefficientOfStaticFriction; internal double CoefficientOfRollingResistance; internal double AerodynamicDragCoefficient; internal double CenterOfGravityHeight; internal double CriticalTopplingAngle; /// current data internal double CurrentSpeed; internal double CurrentPerceivedSpeed; internal double CurrentPerceivedTraveledDistance; internal double CurrentAcceleration; /// The acceleration generated by the motor. Is positive for power and negative for brake, regardless of the train's direction. internal double CurrentAccelerationOutput; internal double CurrentRollDueToTopplingAngle; internal double CurrentRollDueToCantAngle; internal double CurrentRollDueToShakingAngle; internal double CurrentRollDueToShakingAngularSpeed; internal double CurrentRollShakeDirection; internal double CurrentPitchDueToAccelerationAngle; internal double CurrentPitchDueToAccelerationAngularSpeed; internal double CurrentPitchDueToAccelerationTargetAngle; internal double CurrentPitchDueToAccelerationFastValue; internal double CurrentPitchDueToAccelerationMediumValue; internal double CurrentPitchDueToAccelerationSlowValue; /// systems internal CarHoldBrake HoldBrake; internal CarConstSpeed ConstSpeed; internal CarReAdhesionDevice ReAdhesionDevice; internal CarBrakeType BrakeType; internal EletropneumaticBrakeType ElectropneumaticType; internal CarAirBrake AirBrake; /// doors internal Door[] Doors; internal double DoorOpenFrequency; internal double DoorCloseFrequency; internal double DoorOpenPitch; internal double DoorClosePitch; internal bool AnticipatedLeftDoorsOpened; internal bool AnticipatedRightDoorsOpened; } internal struct CarBrightness { internal float PreviousBrightness; internal double PreviousTrackPosition; internal float NextBrightness; internal double NextTrackPosition; } internal struct Horn { internal CarSound Sound; internal bool Loop; private Horn(CarSound sound, bool loop) { this.Sound = sound; this.Loop = loop; } internal static readonly Horn Empty = new Horn(CarSound.Empty, false); } internal struct CarSound { internal Sounds.SoundBuffer Buffer; internal Sounds.SoundSource Source; internal Vector3 Position; private CarSound(Sounds.SoundBuffer buffer, Sounds.SoundSource source, Vector3 position) { this.Buffer = buffer; this.Source = source; this.Position = position; } internal static readonly CarSound Empty = new CarSound(null, null, new Vector3(0.0, 0.0, 0.0)); } internal struct MotorSoundTableEntry { internal Sounds.SoundBuffer Buffer; internal int SoundIndex; internal float Pitch; internal float Gain; } internal struct MotorSoundTable { internal MotorSoundTableEntry[] Entries; internal Sounds.SoundBuffer Buffer; internal Sounds.SoundSource Source; } internal struct MotorSound { internal MotorSoundTable[] Tables; internal Vector3 Position; internal double SpeedConversionFactor; internal int CurrentAccelerationDirection; internal const int MotorP1 = 0; internal const int MotorP2 = 1; internal const int MotorB1 = 2; internal const int MotorB2 = 3; } internal struct CarSounds { internal MotorSound Motor; internal CarSound Adjust; internal CarSound Air; internal CarSound AirHigh; internal CarSound AirZero; internal CarSound Brake; internal CarSound BrakeHandleApply; internal CarSound BrakeHandleRelease; internal CarSound BrakeHandleMin; internal CarSound BrakeHandleMax; internal CarSound BreakerResume; internal CarSound BreakerResumeOrInterrupt; internal bool BreakerResumed; internal CarSound CpEnd; internal CarSound CpLoop; internal bool CpLoopStarted; internal CarSound CpStart; internal double CpStartTimeStarted; internal CarSound DoorCloseL; internal CarSound DoorCloseR; internal CarSound DoorOpenL; internal CarSound DoorOpenR; internal CarSound EmrBrake; internal CarSound[] Flange; internal double[] FlangeVolume; internal CarSound Halt; internal Horn[] Horns; internal CarSound Loop; internal CarSound MasterControllerUp; internal CarSound MasterControllerDown; internal CarSound MasterControllerMin; internal CarSound MasterControllerMax; internal CarSound PilotLampOn; internal CarSound PilotLampOff; internal CarSound PointFrontAxle; internal CarSound PointRearAxle; internal CarSound Rub; internal CarSound ReverserOn; internal CarSound ReverserOff; internal CarSound[] Run; internal double[] RunVolume; internal double RunNextReasynchronizationPosition; internal CarSound SpringL; internal CarSound SpringR; internal CarSound[] Plugin; internal int FrontAxleRunIndex; internal int RearAxleRunIndex; internal int FrontAxleFlangeIndex; internal int RearAxleFlangeIndex; internal double FlangePitch; internal double SpringPlayedAngle; } internal struct Car { internal double Width; internal double Height; internal double Length; internal Axle FrontAxle; internal Axle RearAxle; internal double FrontAxlePosition; internal double RearAxlePosition; internal Vector3 Up; internal CarSection[] CarSections; internal int CurrentCarSection; internal double DriverX; internal double DriverY; internal double DriverZ; internal double DriverYaw; internal double DriverPitch; internal CarSpecs Specs; internal CarSounds Sounds; internal bool CurrentlyVisible; internal bool Derailed; internal bool Topples; internal CarBrightness Brightness; internal double BeaconReceiverPosition; internal TrackManager.TrackFollower BeaconReceiver; } // train internal struct HandleChange { internal int Value; internal double Time; } internal struct PowerHandle { internal int Driver; internal int Safety; internal int Actual; internal HandleChange[] DelayedChanges; internal void AddChange(Train Train, int Value, double Delay) { int n = DelayedChanges.Length; Array.Resize(ref DelayedChanges, n + 1); DelayedChanges[n].Value = Value; DelayedChanges[n].Time = Game.SecondsSinceMidnight + Delay; } internal void RemoveChanges(int Count) { int n = DelayedChanges.Length; for (int i = 0; i < n - Count; i++) { DelayedChanges[i] = DelayedChanges[i + Count]; } Array.Resize(ref DelayedChanges, n - Count); } } internal struct BrakeHandle { internal int Driver; internal int Safety; internal int Actual; internal HandleChange[] DelayedChanges; internal void AddChange(Train Train, int Value, double Delay) { int n = DelayedChanges.Length; Array.Resize(ref DelayedChanges, n + 1); DelayedChanges[n].Value = Value; DelayedChanges[n].Time = Game.SecondsSinceMidnight + Delay; } internal void RemoveChanges(int Count) { int n = DelayedChanges.Length; for (int i = 0; i < n - Count; i++) { DelayedChanges[i] = DelayedChanges[i + Count]; } Array.Resize(ref DelayedChanges, n - Count); } } internal struct EmergencyHandle { internal bool Driver; internal bool Safety; internal bool Actual; internal double ApplicationTime; } internal struct ReverserHandle { internal int Driver; internal int Actual; } internal struct HoldBrakeHandle { internal bool Driver; internal bool Actual; } // train specs internal enum PassAlarmType { None = 0, Single = 1, Loop = 2 } internal struct TrainAirBrake { internal AirBrakeHandle Handle; } internal enum DoorMode { AutomaticManualOverride = 0, Automatic = 1, Manual = 2 } internal enum DefaultSafetySystems { AtsSn = 1, AtsP = 2, Atc = 4, Eb = 8 } internal struct TrainSpecs { internal double TotalMass; internal ReverserHandle CurrentReverser; internal double CurrentAverageSpeed; internal double CurrentAverageAcceleration; internal double CurrentAverageJerk; internal double CurrentAirPressure; internal double CurrentAirDensity; internal double CurrentAirTemperature; internal double CurrentElevation; internal bool SingleHandle; internal int PowerNotchReduceSteps; internal int MaximumPowerNotch; internal PowerHandle CurrentPowerNotch; internal int MaximumBrakeNotch; internal BrakeHandle CurrentBrakeNotch; internal EmergencyHandle CurrentEmergencyBrake; internal bool HasHoldBrake; internal HoldBrakeHandle CurrentHoldBrake; internal DefaultSafetySystems DefaultSafetySystems; internal bool HasConstSpeed; internal bool CurrentConstSpeed; internal TrainAirBrake AirBrake; internal double DelayPowerUp; internal double DelayPowerDown; internal double DelayBrakeUp; internal double DelayBrakeDown; internal PassAlarmType PassAlarm; internal DoorMode DoorOpenMode; internal DoorMode DoorCloseMode; } // passengers internal struct TrainPassengers { internal double PassengerRatio; internal double CurrentAcceleration; internal double CurrentSpeedDifference; internal bool FallenOver; } // train internal enum TrainState { Pending = 0, Available = 1, Disposed = 2, Bogus = 3 } internal enum TrainStopState { Pending = 0, Boarding = 1, Completed = 2 } internal class Train { /// The plugin used by this train. internal PluginManager.Plugin Plugin; internal int TrainIndex; internal TrainState State; internal Car[] Cars; internal Coupler[] Couplers; internal int DriverCar; internal TrainSpecs Specs; internal TrainPassengers Passengers; internal int LastStation; internal int Station; internal bool StationFrontCar; internal bool StationRearCar; internal TrainStopState StationState; internal double StationArrivalTime; internal double StationDepartureTime; internal bool StationDepartureSoundPlayed; internal bool StationAdjust; internal double StationDistanceToStopPoint; internal double[] RouteLimits; internal double CurrentRouteLimit; internal double CurrentSectionLimit; internal int CurrentSectionIndex; internal double TimetableDelta; internal Game.GeneralAI AI; internal double InternalTimerTimeElapsed; } // trains /// The list of trains available in the simulation. internal static Train[] Trains = new Train[] { }; /// A reference to the train of the Trains element that corresponds to the player's train. internal static Train PlayerTrain = null; // ================================ // parse panel config internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { string File = OpenBveApi.Path.CombineFile(TrainPath, "panel.animated"); if (System.IO.File.Exists(File)) { ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(File, Encoding, ObjectManager.ObjectLoadMode.DontAllowUnloadOfTextures); for (int i = 0; i < a.Objects.Length; i++) { a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject(); } Train.Cars[Train.DriverCar].CarSections[0].Elements = a.Objects; World.CameraRestriction = World.CameraRestrictionMode.NotAvailable; } else { File = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg"); if (System.IO.File.Exists(File)) { Panel2CfgParser.ParsePanel2Config(TrainPath, Encoding, Train); World.CameraRestriction = World.CameraRestrictionMode.On; } else { File = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg"); if (System.IO.File.Exists(File)) { PanelCfgParser.ParsePanelConfig(TrainPath, Encoding, Train); World.CameraRestriction = World.CameraRestrictionMode.On; } else { World.CameraRestriction = World.CameraRestrictionMode.NotAvailable; } } } } // ================================ // move car internal static void MoveCar(Train Train, int CarIndex, double Delta, double TimeElapsed) { if (Train.State != TrainState.Disposed) { TrackManager.UpdateTrackFollower(ref Train.Cars[CarIndex].FrontAxle.Follower, Train.Cars[CarIndex].FrontAxle.Follower.TrackPosition + Delta, true, true); if (Train.State != TrainState.Disposed) { TrackManager.UpdateTrackFollower(ref Train.Cars[CarIndex].RearAxle.Follower, Train.Cars[CarIndex].RearAxle.Follower.TrackPosition + Delta, true, true); if (Train.State != TrainState.Disposed) { TrackManager.UpdateTrackFollower(ref Train.Cars[CarIndex].BeaconReceiver, Train.Cars[CarIndex].BeaconReceiver.TrackPosition + Delta, true, true); } } } } // update atmospheric constants internal static void UpdateAtmosphericConstants(Train Train) { double h = 0.0; for (int i = 0; i < Train.Cars.Length; i++) { h += Train.Cars[i].FrontAxle.Follower.WorldPosition.Y + Train.Cars[i].RearAxle.Follower.WorldPosition.Y; } Train.Specs.CurrentElevation = Game.RouteInitialElevation + h / (2.0 * (double)Train.Cars.Length); Train.Specs.CurrentAirTemperature = Game.GetAirTemperature(Train.Specs.CurrentElevation); Train.Specs.CurrentAirPressure = Game.GetAirPressure(Train.Specs.CurrentElevation, Train.Specs.CurrentAirTemperature); Train.Specs.CurrentAirDensity = Game.GetAirDensity(Train.Specs.CurrentAirPressure, Train.Specs.CurrentAirTemperature); } // get acceleration output internal static double GetAccelerationOutput(Train Train, int CarIndex, int CurveIndex, double Speed) { if (CurveIndex < Train.Cars[CarIndex].Specs.AccelerationCurves.Length) { double a0 = Train.Cars[CarIndex].Specs.AccelerationCurves[CurveIndex].StageZeroAcceleration; double s1 = Train.Cars[CarIndex].Specs.AccelerationCurves[CurveIndex].StageOneSpeed; double a1 = Train.Cars[CarIndex].Specs.AccelerationCurves[CurveIndex].StageOneAcceleration; double s2 = Train.Cars[CarIndex].Specs.AccelerationCurves[CurveIndex].StageTwoSpeed; double e2 = Train.Cars[CarIndex].Specs.AccelerationCurves[CurveIndex].StageTwoExponent; double f = Train.Cars[CarIndex].Specs.AccelerationCurvesMultiplier; if (Speed <= 0.0) { return f * a0; } else if (Speed < s1) { double t = Speed / s1; return f * (a0 * (1.0 - t) + a1 * t); } else if (Speed < s2) { return f * s1 * a1 / Speed; } else { return f * s1 * a1 * Math.Pow(s2, e2 - 1.0) * Math.Pow(Speed, -e2); } } else { return 0.0; } } // get resistance private static double GetResistance(Train Train, int CarIndex, ref Axle Axle, double Speed) { double t; if (CarIndex == 0 & Train.Cars[CarIndex].Specs.CurrentSpeed >= 0.0 || CarIndex == Train.Cars.Length - 1 & Train.Cars[CarIndex].Specs.CurrentSpeed <= 0.0) { t = Train.Cars[CarIndex].Specs.ExposedFrontalArea; } else { t = Train.Cars[CarIndex].Specs.UnexposedFrontalArea; } double f = t * Train.Cars[CarIndex].Specs.AerodynamicDragCoefficient * Train.Specs.CurrentAirDensity / (2.0 * Train.Cars[CarIndex].Specs.MassCurrent); double a = Game.RouteAccelerationDueToGravity * Train.Cars[CarIndex].Specs.CoefficientOfRollingResistance + f * Speed * Speed; return a; } // get critical wheelslip acceleration private static double GetCriticalWheelSlipAccelerationForElectricMotor(Train Train, int CarIndex, double AdhesionMultiplier, double UpY, double Speed) { double NormalForceAcceleration = UpY * Game.RouteAccelerationDueToGravity; // TODO: Implement formula that depends on speed here. double coefficient = Train.Cars[CarIndex].Specs.CoefficientOfStaticFriction; return coefficient * AdhesionMultiplier * NormalForceAcceleration; } private static double GetCriticalWheelSlipAccelerationForFrictionBrake(Train Train, int CarIndex, double AdhesionMultiplier, double UpY, double Speed) { double NormalForceAcceleration = UpY * Game.RouteAccelerationDueToGravity; // TODO: Implement formula that depends on speed here. double coefficient = Train.Cars[CarIndex].Specs.CoefficientOfStaticFriction; return coefficient * AdhesionMultiplier * NormalForceAcceleration; } // update toppling, cant and spring internal static void UpdateTopplingCantAndSpring(Train Train, int CarIndex, double TimeElapsed) { // get direction, up and side vectors double dx, dy, dz; double ux, uy, uz; double sx, sy, sz; { dx = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X; dy = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y; dz = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z; double t = 1.0 / Math.Sqrt(dx * dx + dy * dy + dz * dz); dx *= t; dy *= t; dz *= t; t = 1.0 / Math.Sqrt(dx * dx + dz * dz); double ex = dx * t; double ez = dz * t; sx = ez; sy = 0.0; sz = -ex; World.Cross(dx, dy, dz, sx, sy, sz, out ux, out uy, out uz); } // cant and radius double c; { double ca = Train.Cars[CarIndex].FrontAxle.Follower.CurveCant; double cb = Train.Cars[CarIndex].RearAxle.Follower.CurveCant; c = Math.Tan(0.5 * (Math.Atan(ca) + Math.Atan(cb))); } double r, rs; if (Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius != 0.0 & Train.Cars[CarIndex].RearAxle.Follower.CurveRadius != 0.0) { r = Math.Sqrt(Math.Abs(Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius * Train.Cars[CarIndex].RearAxle.Follower.CurveRadius)); rs = (double)Math.Sign(Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius + Train.Cars[CarIndex].RearAxle.Follower.CurveRadius); } else if (Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius != 0.0) { r = Math.Abs(Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius); rs = (double)Math.Sign(Train.Cars[CarIndex].FrontAxle.Follower.CurveRadius); } else if (Train.Cars[CarIndex].RearAxle.Follower.CurveRadius != 0.0) { r = Math.Abs(Train.Cars[CarIndex].RearAxle.Follower.CurveRadius); rs = (double)Math.Sign(Train.Cars[CarIndex].RearAxle.Follower.CurveRadius); } else { r = 0.0; rs = 0.0; } // roll due to shaking { double a0 = Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngle; double a1; if (Train.Cars[CarIndex].Specs.CurrentRollShakeDirection != 0.0) { const double c0 = 0.03; const double c1 = 0.15; a1 = c1 * Math.Atan(c0 * Train.Cars[CarIndex].Specs.CurrentRollShakeDirection); double d = 0.5 + Train.Cars[CarIndex].Specs.CurrentRollShakeDirection * Train.Cars[CarIndex].Specs.CurrentRollShakeDirection; if (Train.Cars[CarIndex].Specs.CurrentRollShakeDirection < 0.0) { Train.Cars[CarIndex].Specs.CurrentRollShakeDirection += d * TimeElapsed; if (Train.Cars[CarIndex].Specs.CurrentRollShakeDirection > 0.0) Train.Cars[CarIndex].Specs.CurrentRollShakeDirection = 0.0; } else { Train.Cars[CarIndex].Specs.CurrentRollShakeDirection -= d * TimeElapsed; if (Train.Cars[CarIndex].Specs.CurrentRollShakeDirection < 0.0) Train.Cars[CarIndex].Specs.CurrentRollShakeDirection = 0.0; } } else { a1 = 0.0; } double SpringAcceleration; if (!Train.Cars[CarIndex].Derailed) { SpringAcceleration = 15.0 * Math.Abs(a1 - a0); } else { SpringAcceleration = 1.5 * Math.Abs(a1 - a0); } double SpringDeceleration = 0.25 * SpringAcceleration; Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed += (double)Math.Sign(a1 - a0) * SpringAcceleration * TimeElapsed; double x = (double)Math.Sign(Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed) * SpringDeceleration * TimeElapsed; if (Math.Abs(x) < Math.Abs(Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed)) { Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed -= x; } else { Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed = 0.0; } a0 += Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngularSpeed * TimeElapsed; Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngle = a0; } // roll due to cant (incorporates shaking) { double cantAngle = Math.Atan(c / Game.RouteRailGauge); Train.Cars[CarIndex].Specs.CurrentRollDueToCantAngle = cantAngle + Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngle; } // pitch due to acceleration { for (int i = 0; i < 3; i++) { double a, v, j; if (i == 0) { a = Train.Cars[CarIndex].Specs.CurrentAcceleration; v = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationFastValue; j = 1.8; } else if (i == 1) { a = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationFastValue; v = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationMediumValue; j = 1.2; } else { a = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationFastValue; v = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationSlowValue; j = 1.0; } double d = a - v; if (d < 0.0) { v -= j * TimeElapsed; if (v < a) v = a; } else { v += j * TimeElapsed; if (v > a) v = a; } if (i == 0) { Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationFastValue = v; } else if (i == 1) { Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationMediumValue = v; } else { Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationSlowValue = v; } } { double d = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationSlowValue - Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationFastValue; Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationTargetAngle = 0.03 * Math.Atan(d); } { double a = 3.0 * (double)Math.Sign(Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationTargetAngle - Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngle); Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngularSpeed += a * TimeElapsed; double s = Math.Abs(Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationTargetAngle - Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngle); if (Math.Abs(Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngularSpeed) > s) { Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngularSpeed = s * (double)Math.Sign(Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngularSpeed); } Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngle += Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngularSpeed * TimeElapsed; } } // derailment if (Interface.CurrentOptions.Derailments & !Train.Cars[CarIndex].Derailed) { double a = Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle + Train.Cars[CarIndex].Specs.CurrentRollDueToCantAngle; double sa = (double)Math.Sign(a); double tc = Train.Cars[CarIndex].Specs.CriticalTopplingAngle; if (a * sa > tc) { Train.Cars[CarIndex].Derailed = true; } } // toppling roll if (Interface.CurrentOptions.Toppling | Train.Cars[CarIndex].Derailed) { double a = Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle; double ab = Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle + Train.Cars[CarIndex].Specs.CurrentRollDueToCantAngle; double h = Train.Cars[CarIndex].Specs.CenterOfGravityHeight; double s = Math.Abs(Train.Cars[CarIndex].Specs.CurrentSpeed); double rmax = 2.0 * h * s * s / (Game.RouteAccelerationDueToGravity * Game.RouteRailGauge); double ta; Train.Cars[CarIndex].Topples = false; if (Train.Cars[CarIndex].Derailed) { double sab = (double)Math.Sign(ab); ta = 0.5 * Math.PI * (sab == 0.0 ? Program.RandomNumberGenerator.NextDouble() < 0.5 ? -1.0 : 1.0 : sab); } else { if (r != 0.0) { if (r < rmax) { double s0 = Math.Sqrt(r * Game.RouteAccelerationDueToGravity * Game.RouteRailGauge / (2.0 * h)); const double fac = 0.25; /// arbitrary coefficient ta = -fac * (s - s0) * rs; Train.Cars[CarIndex].Topples = true; } else { ta = 0.0; } } else { ta = 0.0; } } double td; if (Train.Cars[CarIndex].Derailed) { td = Math.Abs(ab); if (td < 0.1) td = 0.1; } else { td = 1.0; } if (a > ta) { double d = a - ta; if (td > d) td = d; a -= td * TimeElapsed; } else if (a < ta) { double d = ta - a; if (td > d) td = d; a += td * TimeElapsed; } Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle = a; } else { Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle = 0.0; } // apply position due to cant/toppling { double a = Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle + Train.Cars[CarIndex].Specs.CurrentRollDueToCantAngle; double x = Math.Sign(a) * 0.5 * Game.RouteRailGauge * (1.0 - Math.Cos(a)); double y = Math.Abs(0.5 * Game.RouteRailGauge * Math.Sin(a)); double cx = sx * x + ux * y; double cy = sy * x + uy * y; double cz = sz * x + uz * y; Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X += cx; Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y += cy; Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z += cz; Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X += cx; Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y += cy; Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z += cz; } // apply rolling { double a = -Train.Cars[CarIndex].Specs.CurrentRollDueToTopplingAngle - Train.Cars[CarIndex].Specs.CurrentRollDueToCantAngle; double cosa = Math.Cos(a); double sina = Math.Sin(a); World.Rotate(ref sx, ref sy, ref sz, dx, dy, dz, cosa, sina); World.Rotate(ref ux, ref uy, ref uz, dx, dy, dz, cosa, sina); Train.Cars[CarIndex].Up.X = ux; Train.Cars[CarIndex].Up.Y = uy; Train.Cars[CarIndex].Up.Z = uz; } // apply pitching if (Train.Cars[CarIndex].CurrentCarSection >= 0 && Train.Cars[CarIndex].CarSections[Train.Cars[CarIndex].CurrentCarSection].Overlay) { double a = Train.Cars[CarIndex].Specs.CurrentPitchDueToAccelerationAngle; double cosa = Math.Cos(a); double sina = Math.Sin(a); World.Rotate(ref dx, ref dy, ref dz, sx, sy, sz, cosa, sina); World.Rotate(ref ux, ref uy, ref uz, sx, sy, sz, cosa, sina); double cx = 0.5 * (Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X + Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X); double cy = 0.5 * (Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y + Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y); double cz = 0.5 * (Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z + Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z); Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X -= cx; Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y -= cy; Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z -= cz; Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X -= cx; Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y -= cy; Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z -= cz; World.Rotate(ref Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X, ref Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y, ref Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z, sx, sy, sz, cosa, sina); World.Rotate(ref Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X, ref Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y, ref Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z, sx, sy, sz, cosa, sina); Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X += cx; Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y += cy; Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z += cz; Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X += cx; Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y += cy; Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z += cz; Train.Cars[CarIndex].Up.X = ux; Train.Cars[CarIndex].Up.Y = uy; Train.Cars[CarIndex].Up.Z = uz; } // spring sound { double a = Train.Cars[CarIndex].Specs.CurrentRollDueToShakingAngle; double diff = a - Train.Cars[CarIndex].Sounds.SpringPlayedAngle; const double angleTolerance = 0.001; if (diff < -angleTolerance) { Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.SpringL.Buffer; if (buffer != null) { if (!Sounds.IsPlaying(Train.Cars[CarIndex].Sounds.SpringL.Source)) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.SpringL.Position; Train.Cars[CarIndex].Sounds.SpringL.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, CarIndex, false); } } Train.Cars[CarIndex].Sounds.SpringPlayedAngle = a; } else if (diff > angleTolerance) { Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.SpringR.Buffer; if (buffer != null) { if (!Sounds.IsPlaying(Train.Cars[CarIndex].Sounds.SpringR.Source)) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.SpringR.Position; Train.Cars[CarIndex].Sounds.SpringR.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, CarIndex, false); } } Train.Cars[CarIndex].Sounds.SpringPlayedAngle = a; } } // flange sound { /* * This determines the amount of flange noise as a result of the angle at which the * line that forms between the axles hits the rail, i.e. the less perpendicular that * line is to the rails, the more flange noise there will be. * */ Vector3 d = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition; World.Normalize(ref d.X, ref d.Y, ref d.Z); double b0 = d.X * Train.Cars[CarIndex].RearAxle.Follower.WorldSide.X + d.Y * Train.Cars[CarIndex].RearAxle.Follower.WorldSide.Y + d.Z * Train.Cars[CarIndex].RearAxle.Follower.WorldSide.Z; double b1 = d.X * Train.Cars[CarIndex].FrontAxle.Follower.WorldSide.X + d.Y * Train.Cars[CarIndex].FrontAxle.Follower.WorldSide.Y + d.Z * Train.Cars[CarIndex].FrontAxle.Follower.WorldSide.Z; double spd = Math.Abs(Train.Cars[CarIndex].Specs.CurrentSpeed); double pitch = 0.5 + 0.04 * spd; double b2 = Math.Abs(b0) + Math.Abs(b1); double basegain = 0.8 * b2 * b2 * spd * spd; /* * This determines additional flange noise as a resul of the roll angle of the car * compared to the roll angle of the rails, i.e. if the car bounces due to inaccuracies, * there will be additional flange noise. * */ double cdti = Math.Abs(Train.Cars[CarIndex].FrontAxle.Follower.CantDueToInaccuracy) + Math.Abs(Train.Cars[CarIndex].RearAxle.Follower.CantDueToInaccuracy); basegain += 15.0 * spd * cdti * cdti; /* * This applies the settings. * */ if (basegain < 0.0) basegain = 0.0; if (basegain > 0.75) basegain = 0.75; if (pitch > Train.Cars[CarIndex].Sounds.FlangePitch) { Train.Cars[CarIndex].Sounds.FlangePitch += TimeElapsed; if (Train.Cars[CarIndex].Sounds.FlangePitch > pitch) Train.Cars[CarIndex].Sounds.FlangePitch = pitch; } else { Train.Cars[CarIndex].Sounds.FlangePitch -= TimeElapsed; if (Train.Cars[CarIndex].Sounds.FlangePitch < pitch) Train.Cars[CarIndex].Sounds.FlangePitch = pitch; } pitch = Train.Cars[CarIndex].Sounds.FlangePitch; for (int i = 0; i < Train.Cars[CarIndex].Sounds.Flange.Length; i++) { if (i == Train.Cars[CarIndex].Sounds.FrontAxleFlangeIndex | i == Train.Cars[CarIndex].Sounds.RearAxleFlangeIndex) { Train.Cars[CarIndex].Sounds.FlangeVolume[i] += TimeElapsed; if (Train.Cars[CarIndex].Sounds.FlangeVolume[i] > 1.0) Train.Cars[CarIndex].Sounds.FlangeVolume[i] = 1.0; } else { Train.Cars[CarIndex].Sounds.FlangeVolume[i] -= TimeElapsed; if (Train.Cars[CarIndex].Sounds.FlangeVolume[i] < 0.0) Train.Cars[CarIndex].Sounds.FlangeVolume[i] = 0.0; } double gain = basegain * Train.Cars[CarIndex].Sounds.FlangeVolume[i]; if (Sounds.IsPlaying(Train.Cars[CarIndex].Sounds.Flange[i].Source)) { if (pitch > 0.01 & gain > 0.0001) { Train.Cars[CarIndex].Sounds.Flange[i].Source.Pitch = pitch; Train.Cars[CarIndex].Sounds.Flange[i].Source.Volume = gain; } else { Train.Cars[CarIndex].Sounds.Flange[i].Source.Stop(); } } else if (pitch > 0.02 & gain > 0.01) { Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.Flange[i].Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.Flange[i].Position; Train.Cars[CarIndex].Sounds.Flange[i].Source = Sounds.PlaySound(buffer, pitch, gain, pos, Train, CarIndex, true); } } } } } // update camera internal static void UpdateCamera(Train Train) { int i = Train.DriverCar; double dx = Train.Cars[i].FrontAxle.Follower.WorldPosition.X - Train.Cars[i].RearAxle.Follower.WorldPosition.X; double dy = Train.Cars[i].FrontAxle.Follower.WorldPosition.Y - Train.Cars[i].RearAxle.Follower.WorldPosition.Y; double dz = Train.Cars[i].FrontAxle.Follower.WorldPosition.Z - Train.Cars[i].RearAxle.Follower.WorldPosition.Z; double t = 1.0 / Math.Sqrt(dx * dx + dy * dy + dz * dz); dx *= t; dy *= t; dz *= t; double ux = Train.Cars[i].Up.X; double uy = Train.Cars[i].Up.Y; double uz = Train.Cars[i].Up.Z; double sx = dz * uy - dy * uz; double sy = dx * uz - dz * ux; double sz = dy * ux - dx * uy; double rx = 0.5 * (Train.Cars[i].FrontAxle.Follower.WorldPosition.X + Train.Cars[i].RearAxle.Follower.WorldPosition.X); double ry = 0.5 * (Train.Cars[i].FrontAxle.Follower.WorldPosition.Y + Train.Cars[i].RearAxle.Follower.WorldPosition.Y); double rz = 0.5 * (Train.Cars[i].FrontAxle.Follower.WorldPosition.Z + Train.Cars[i].RearAxle.Follower.WorldPosition.Z); double cx = rx + sx * Train.Cars[i].DriverX + ux * Train.Cars[i].DriverY + dx * Train.Cars[i].DriverZ; double cy = ry + sy * Train.Cars[i].DriverX + uy * Train.Cars[i].DriverY + dy * Train.Cars[i].DriverZ; double cz = rz + sz * Train.Cars[i].DriverX + uz * Train.Cars[i].DriverY + dz * Train.Cars[i].DriverZ; World.CameraTrackFollower.WorldPosition = new Vector3(cx, cy, cz); World.CameraTrackFollower.WorldDirection = new Vector3(dx, dy, dz); World.CameraTrackFollower.WorldUp = new Vector3(ux, uy, uz); World.CameraTrackFollower.WorldSide = new Vector3(sx, sy, sz); double f = (Train.Cars[i].DriverZ - Train.Cars[i].RearAxlePosition) / (Train.Cars[i].FrontAxlePosition - Train.Cars[i].RearAxlePosition); double tp = (1.0 - f) * Train.Cars[i].RearAxle.Follower.TrackPosition + f * Train.Cars[i].FrontAxle.Follower.TrackPosition; TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, tp, false, false); } // create world coordinates internal static void CreateWorldCoordinates(Train Train, int CarIndex, double CarX, double CarY, double CarZ, out double PositionX, out double PositionY, out double PositionZ, out double DirectionX, out double DirectionY, out double DirectionZ) { DirectionX = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X; DirectionY = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y; DirectionZ = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z - Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z; double t = DirectionX * DirectionX + DirectionY * DirectionY + DirectionZ * DirectionZ; if (t != 0.0) { t = 1.0 / Math.Sqrt(t); DirectionX *= t; DirectionY *= t; DirectionZ *= t; double ux = Train.Cars[CarIndex].Up.X; double uy = Train.Cars[CarIndex].Up.Y; double uz = Train.Cars[CarIndex].Up.Z; double sx = DirectionZ * uy - DirectionY * uz; double sy = DirectionX * uz - DirectionZ * ux; double sz = DirectionY * ux - DirectionX * uy; double rx = 0.5 * (Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X + Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.X); double ry = 0.5 * (Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y + Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Y); double rz = 0.5 * (Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z + Train.Cars[CarIndex].RearAxle.Follower.WorldPosition.Z); PositionX = rx + sx * CarX + ux * CarY + DirectionX * CarZ; PositionY = ry + sy * CarX + uy * CarY + DirectionY * CarZ; PositionZ = rz + sz * CarX + uz * CarY + DirectionZ * CarZ; } else { PositionX = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.X; PositionY = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Y; PositionZ = Train.Cars[CarIndex].FrontAxle.Follower.WorldPosition.Z; DirectionX = 0.0; DirectionY = 1.0; DirectionZ = 0.0; } } // initialize train internal static void InitializeTrain(Train Train) { for (int i = 0; i < Train.Cars.Length; i++) { InitializeCar(Train, i); } UpdateAtmosphericConstants(Train); UpdateTrain(Train, 0.0); } // initialize car private static void InitializeCar(Train Train, int CarIndex) { int c = CarIndex; for (int i = 0; i < Train.Cars[c].CarSections.Length; i++) { InitializeCarSection(Train, c, i); } Train.Cars[c].Brightness.PreviousBrightness = 1.0f; Train.Cars[c].Brightness.NextBrightness = 1.0f; } // initialize car section internal static void InitializeCarSection(Train Train, int CarIndex, int SectionIndex) { int c = CarIndex; int s = SectionIndex; for (int j = 0; j < Train.Cars[c].CarSections[s].Elements.Length; j++) { for (int k = 0; k < Train.Cars[c].CarSections[s].Elements[j].States.Length; k++) { InitializeCarSectionElement(Train, c, s, j, k); } } } // initialize car section element internal static void InitializeCarSectionElement(Train Train, int CarIndex, int SectionIndex, int ElementIndex, int StateIndex) { ObjectManager.InitializeAnimatedObject(ref Train.Cars[CarIndex].CarSections[SectionIndex].Elements[ElementIndex], StateIndex, Train.Cars[CarIndex].CarSections[SectionIndex].Overlay, Train.Cars[CarIndex].CurrentlyVisible); } // update train objects internal static void UpdateTrainObjects(double TimeElapsed, bool ForceUpdate) { for (int i = 0; i < Trains.Length; i++) { UpdateTrainObjects(Trains[i], TimeElapsed, ForceUpdate); } } private static void UpdateTrainObjects(Train Train, double TimeElapsed, bool ForceUpdate) { if (!Game.MinimalisticSimulation) { for (int i = 0; i < Train.Cars.Length; i++) { UpdateTrainObjects(Train, i, TimeElapsed, ForceUpdate); } } } private static void UpdateTrainObjects(Train Train, int CarIndex, double TimeElapsed, bool ForceUpdate) { // calculate positions and directions for section element update int c = CarIndex; double dx = Train.Cars[c].FrontAxle.Follower.WorldPosition.X - Train.Cars[c].RearAxle.Follower.WorldPosition.X; double dy = Train.Cars[c].FrontAxle.Follower.WorldPosition.Y - Train.Cars[c].RearAxle.Follower.WorldPosition.Y; double dz = Train.Cars[c].FrontAxle.Follower.WorldPosition.Z - Train.Cars[c].RearAxle.Follower.WorldPosition.Z; double t = dx * dx + dy * dy + dz * dz; double ux, uy, uz, sx, sy, sz; if (t != 0.0) { t = 1.0 / Math.Sqrt(t); dx *= t; dy *= t; dz *= t; ux = Train.Cars[c].Up.X; uy = Train.Cars[c].Up.Y; uz = Train.Cars[c].Up.Z; sx = dz * uy - dy * uz; sy = dx * uz - dz * ux; sz = dy * ux - dx * uy; } else { ux = 0.0; uy = 1.0; uz = 0.0; sx = 1.0; sy = 0.0; sz = 0.0; } double px = 0.5 * (Train.Cars[c].FrontAxle.Follower.WorldPosition.X + Train.Cars[c].RearAxle.Follower.WorldPosition.X); double py = 0.5 * (Train.Cars[c].FrontAxle.Follower.WorldPosition.Y + Train.Cars[c].RearAxle.Follower.WorldPosition.Y); double pz = 0.5 * (Train.Cars[c].FrontAxle.Follower.WorldPosition.Z + Train.Cars[c].RearAxle.Follower.WorldPosition.Z); double d = 0.5 * (Train.Cars[c].FrontAxlePosition + Train.Cars[c].RearAxlePosition); px -= dx * d; py -= dy * d; pz -= dz * d; // determine visibility double cdx = px - World.AbsoluteCameraPosition.X; double cdy = py - World.AbsoluteCameraPosition.Y; double cdz = pz - World.AbsoluteCameraPosition.Z; double dist = cdx * cdx + cdy * cdy + cdz * cdz; double bid = Interface.CurrentOptions.ViewingDistance + Train.Cars[c].Length; Train.Cars[c].CurrentlyVisible = dist < bid * bid; // brightness byte dnb; { float Brightness = (float)(Train.Cars[c].Brightness.NextTrackPosition - Train.Cars[c].Brightness.PreviousTrackPosition); if (Brightness != 0.0f) { Brightness = (float)(Train.Cars[c].FrontAxle.Follower.TrackPosition - Train.Cars[c].Brightness.PreviousTrackPosition) / Brightness; if (Brightness < 0.0f) Brightness = 0.0f; if (Brightness > 1.0f) Brightness = 1.0f; Brightness = Train.Cars[c].Brightness.PreviousBrightness * (1.0f - Brightness) + Train.Cars[c].Brightness.NextBrightness * Brightness; } else { Brightness = Train.Cars[c].Brightness.PreviousBrightness; } dnb = (byte)Math.Round(255.0 * (double)(1.0 - Brightness)); } // update current section int s = Train.Cars[c].CurrentCarSection; if (s >= 0) { for (int i = 0; i < Train.Cars[c].CarSections[s].Elements.Length; i++) { UpdateCarSectionElement(Train, CarIndex, s, i, new Vector3(px, py, pz), new Vector3(dx, dy, dz), new Vector3(ux, uy, uz), new Vector3(sx, sy, sz), Train.Cars[c].CurrentlyVisible, TimeElapsed, ForceUpdate); // brightness change int o = Train.Cars[c].CarSections[s].Elements[i].ObjectIndex; if (ObjectManager.Objects[o] != null) { for (int j = 0; j < ObjectManager.Objects[o].Mesh.Materials.Length; j++) { ObjectManager.Objects[o].Mesh.Materials[j].DaytimeNighttimeBlend = dnb; } } } } } // change car section internal static void ChangeCarSection(Train Train, int CarIndex, int SectionIndex) { for (int i = 0; i < Train.Cars[CarIndex].CarSections.Length; i++) { for (int j = 0; j < Train.Cars[CarIndex].CarSections[i].Elements.Length; j++) { int o = Train.Cars[CarIndex].CarSections[i].Elements[j].ObjectIndex; Renderer.HideObject(o); } } if (SectionIndex >= 0) { InitializeCarSection(Train, CarIndex, SectionIndex); for (int j = 0; j < Train.Cars[CarIndex].CarSections[SectionIndex].Elements.Length; j++) { int o = Train.Cars[CarIndex].CarSections[SectionIndex].Elements[j].ObjectIndex; if (Train.Cars[CarIndex].CarSections[SectionIndex].Overlay) { Renderer.ShowObject(o, Renderer.ObjectType.Overlay); } else { Renderer.ShowObject(o, Renderer.ObjectType.Dynamic); } } } Train.Cars[CarIndex].CurrentCarSection = SectionIndex; } // update car section element private static void UpdateCarSectionElement(Train Train, int CarIndex, int SectionIndex, int ElementIndex, Vector3 Position, Vector3 Direction, Vector3 Up, Vector3 Side, bool Show, double TimeElapsed, bool ForceUpdate) { Vector3 p; if (Train.Cars[CarIndex].CarSections[SectionIndex].Overlay & World.CameraRestriction != World.CameraRestrictionMode.NotAvailable) { p = new Vector3(Train.Cars[CarIndex].DriverX, Train.Cars[CarIndex].DriverY, Train.Cars[CarIndex].DriverZ); } else { p = Position; } double timeDelta; bool updatefunctions; if (Train.Cars[CarIndex].CarSections[SectionIndex].Elements[ElementIndex].RefreshRate != 0.0) { if (Train.Cars[CarIndex].CarSections[SectionIndex].Elements[ElementIndex].SecondsSinceLastUpdate >= Train.Cars[CarIndex].CarSections[SectionIndex].Elements[ElementIndex].RefreshRate) { timeDelta = Train.Cars[CarIndex].CarSections[SectionIndex].Elements[ElementIndex].SecondsSinceLastUpdate; Train.Cars[CarIndex].CarSections[SectionIndex].Elements[ElementIndex].SecondsSinceLastUpdate = TimeElapsed; updatefunctions = true; } else { timeDelta = TimeElapsed; Train.Cars[CarIndex].CarSections[SectionIndex].Elements[ElementIndex].SecondsSinceLastUpdate += TimeElapsed; updatefunctions = false; } } else { timeDelta = Train.Cars[CarIndex].CarSections[SectionIndex].Elements[ElementIndex].SecondsSinceLastUpdate; Train.Cars[CarIndex].CarSections[SectionIndex].Elements[ElementIndex].SecondsSinceLastUpdate = TimeElapsed; updatefunctions = true; } if (ForceUpdate) { updatefunctions = true; } ObjectManager.UpdateAnimatedObject(ref Train.Cars[CarIndex].CarSections[SectionIndex].Elements[ElementIndex], true, Train, CarIndex, Train.Cars[CarIndex].CurrentCarSection, Train.Cars[CarIndex].FrontAxle.Follower.TrackPosition - Train.Cars[CarIndex].FrontAxlePosition, p, Direction, Up, Side, Train.Cars[CarIndex].CarSections[SectionIndex].Overlay, updatefunctions, Show, timeDelta); } // update train internal static void UpdateTrain(Train Train, double TimeElapsed) { if (Train.State == TrainState.Pending) { // pending train bool forceIntroduction = Train == PlayerTrain && !Game.MinimalisticSimulation; double time = 0.0; if (!forceIntroduction) { for (int i = 0; i < Game.Stations.Length; i++) { if (Game.Stations[i].StopMode == Game.StationStopMode.AllStop | Game.Stations[i].StopMode == Game.StationStopMode.PlayerPass) { if (Game.Stations[i].ArrivalTime >= 0.0) { time = Game.Stations[i].ArrivalTime; } else if (Game.Stations[i].DepartureTime >= 0.0) { time = Game.Stations[i].DepartureTime - Game.Stations[i].StopTime; } break; } } time -= Train.TimetableDelta; } if (Game.SecondsSinceMidnight >= time | forceIntroduction) { bool introduce = true; if (!forceIntroduction) { if (Train.CurrentSectionIndex >= 0) { if (!Game.Sections[Train.CurrentSectionIndex].IsFree()) { introduce = false; } } } if (introduce) { // train is introduced Train.State = TrainState.Available; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].CarSections.Length != 0) { TrainManager.ChangeCarSection(Train, j, j <= Train.DriverCar | Train != PlayerTrain ? 0 : -1); } if (Train.Cars[j].Specs.IsMotorCar) { if (Train.Cars[j].Sounds.Loop.Buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[j].Sounds.Loop.Position; Train.Cars[j].Sounds.Loop.Source = Sounds.PlaySound(Train.Cars[j].Sounds.Loop.Buffer, 1.0, 1.0, pos, Train, j, true); } } } } } } else if (Train.State == TrainState.Available) { // available train UpdateTrainPhysicsAndControls(Train, TimeElapsed); if (Interface.CurrentOptions.GameMode == Interface.GameMode.Arcade) { if (Train.Specs.CurrentAverageSpeed > Train.CurrentRouteLimit) { Game.AddMessage(Interface.GetInterfaceString("message_route_overspeed"), Game.MessageDependency.RouteLimit, Interface.GameMode.Arcade, Game.MessageColor.Orange, double.PositiveInfinity); } if (Train.CurrentSectionLimit == 0.0) { Game.AddMessage(Interface.GetInterfaceString("message_signal_stop"), Game.MessageDependency.SectionLimit, Interface.GameMode.Normal, Game.MessageColor.Red, double.PositiveInfinity); } else if (Train.Specs.CurrentAverageSpeed > Train.CurrentSectionLimit) { Game.AddMessage(Interface.GetInterfaceString("message_signal_overspeed"), Game.MessageDependency.SectionLimit, Interface.GameMode.Normal, Game.MessageColor.Orange, double.PositiveInfinity); } } if (Train.AI != null) { Train.AI.Trigger(Train, TimeElapsed); } } else if (Train.State == TrainState.Bogus) { // bogus train if (Train.AI != null) { Train.AI.Trigger(Train, TimeElapsed); } } } // update trains internal static void UpdateTrains(double TimeElapsed) { // individual trains (without objects) for (int i = 0; i < Trains.Length; i++) { UpdateTrain(Trains[i], TimeElapsed); } // detect collision if (!Game.MinimalisticSimulation & Interface.CurrentOptions.Collisions) { for (int i = 0; i < Trains.Length; i++) { // with other trains if (Trains[i].State == TrainState.Available) { double a = Trains[i].Cars[0].FrontAxle.Follower.TrackPosition - Trains[i].Cars[0].FrontAxlePosition + 0.5 * Trains[i].Cars[0].Length; double b = Trains[i].Cars[Trains[i].Cars.Length - 1].RearAxle.Follower.TrackPosition - Trains[i].Cars[Trains[i].Cars.Length - 1].RearAxlePosition - 0.5 * Trains[i].Cars[0].Length; for (int j = i + 1; j < Trains.Length; j++) { if (Trains[j].State == TrainState.Available) { double c = Trains[j].Cars[0].FrontAxle.Follower.TrackPosition - Trains[j].Cars[0].FrontAxlePosition + 0.5 * Trains[j].Cars[0].Length; double d = Trains[j].Cars[Trains[j].Cars.Length - 1].RearAxle.Follower.TrackPosition - Trains[j].Cars[Trains[j].Cars.Length - 1].RearAxlePosition - 0.5 * Trains[j].Cars[0].Length; if (a > d & b < c) { if (a > c) { // i > j int k = Trains[i].Cars.Length - 1; if (Trains[i].Cars[k].Specs.CurrentSpeed < Trains[j].Cars[0].Specs.CurrentSpeed) { double v = Trains[j].Cars[0].Specs.CurrentSpeed - Trains[i].Cars[k].Specs.CurrentSpeed; double s = (Trains[i].Cars[k].Specs.CurrentSpeed * Trains[i].Cars[k].Specs.MassCurrent + Trains[j].Cars[0].Specs.CurrentSpeed * Trains[j].Cars[0].Specs.MassCurrent) / (Trains[i].Cars[k].Specs.MassCurrent + Trains[j].Cars[0].Specs.MassCurrent); Trains[i].Cars[k].Specs.CurrentSpeed = s; Trains[j].Cars[0].Specs.CurrentSpeed = s; double e = 0.5 * (c - b) + 0.0001; TrackManager.UpdateTrackFollower(ref Trains[i].Cars[k].FrontAxle.Follower, Trains[i].Cars[k].FrontAxle.Follower.TrackPosition + e, false, false); TrackManager.UpdateTrackFollower(ref Trains[i].Cars[k].RearAxle.Follower, Trains[i].Cars[k].RearAxle.Follower.TrackPosition + e, false, false); TrackManager.UpdateTrackFollower(ref Trains[j].Cars[0].FrontAxle.Follower, Trains[j].Cars[0].FrontAxle.Follower.TrackPosition - e, false, false); TrackManager.UpdateTrackFollower(ref Trains[j].Cars[0].RearAxle.Follower, Trains[j].Cars[0].RearAxle.Follower.TrackPosition - e, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[i].Cars[k].Specs.MassCurrent + Trains[j].Cars[0].Specs.MassCurrent); double fi = Trains[j].Cars[0].Specs.MassCurrent * f; double fj = Trains[i].Cars[k].Specs.MassCurrent * f; double vi = v * fi; double vj = v * fj; if (vi > Game.CriticalCollisionSpeedDifference) Trains[i].Cars[k].Derailed = true; if (vj > Game.CriticalCollisionSpeedDifference) Trains[j].Cars[i].Derailed = true; } // adjust cars for train i for (int h = Trains[i].Cars.Length - 2; h >= 0; h--) { a = Trains[i].Cars[h + 1].FrontAxle.Follower.TrackPosition - Trains[i].Cars[h + 1].FrontAxlePosition + 0.5 * Trains[i].Cars[h + 1].Length; b = Trains[i].Cars[h].RearAxle.Follower.TrackPosition - Trains[i].Cars[h].RearAxlePosition - 0.5 * Trains[i].Cars[h].Length; d = b - a - Trains[i].Couplers[h].MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; TrackManager.UpdateTrackFollower(ref Trains[i].Cars[h].FrontAxle.Follower, Trains[i].Cars[h].FrontAxle.Follower.TrackPosition - d, false, false); TrackManager.UpdateTrackFollower(ref Trains[i].Cars[h].RearAxle.Follower, Trains[i].Cars[h].RearAxle.Follower.TrackPosition - d, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[i].Cars[h + 1].Specs.MassCurrent + Trains[i].Cars[h].Specs.MassCurrent); double fi = Trains[i].Cars[h + 1].Specs.MassCurrent * f; double fj = Trains[i].Cars[h].Specs.MassCurrent * f; double vi = v * fi; double vj = v * fj; if (vi > Game.CriticalCollisionSpeedDifference) Trains[i].Cars[h + 1].Derailed = true; if (vj > Game.CriticalCollisionSpeedDifference) Trains[i].Cars[h].Derailed = true; } Trains[i].Cars[h].Specs.CurrentSpeed = Trains[i].Cars[h + 1].Specs.CurrentSpeed; } } // adjust cars for train j for (int h = 1; h < Trains[j].Cars.Length; h++) { a = Trains[j].Cars[h - 1].RearAxle.Follower.TrackPosition - Trains[j].Cars[h - 1].RearAxlePosition - 0.5 * Trains[j].Cars[h - 1].Length; b = Trains[j].Cars[h].FrontAxle.Follower.TrackPosition - Trains[j].Cars[h].FrontAxlePosition + 0.5 * Trains[j].Cars[h].Length; d = a - b - Trains[j].Couplers[h - 1].MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; TrackManager.UpdateTrackFollower(ref Trains[j].Cars[h].FrontAxle.Follower, Trains[j].Cars[h].FrontAxle.Follower.TrackPosition + d, false, false); TrackManager.UpdateTrackFollower(ref Trains[j].Cars[h].RearAxle.Follower, Trains[j].Cars[h].RearAxle.Follower.TrackPosition + d, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[j].Cars[h - 1].Specs.MassCurrent + Trains[j].Cars[h].Specs.MassCurrent); double fi = Trains[j].Cars[h - 1].Specs.MassCurrent * f; double fj = Trains[j].Cars[h].Specs.MassCurrent * f; double vi = v * fi; double vj = v * fj; if (vi > Game.CriticalCollisionSpeedDifference) Trains[j].Cars[h - 1].Derailed = true; if (vj > Game.CriticalCollisionSpeedDifference) Trains[j].Cars[h].Derailed = true; } Trains[j].Cars[h].Specs.CurrentSpeed = Trains[j].Cars[h - 1].Specs.CurrentSpeed; } } } } else { // i < j int k = Trains[j].Cars.Length - 1; if (Trains[i].Cars[0].Specs.CurrentSpeed > Trains[j].Cars[k].Specs.CurrentSpeed) { double v = Trains[i].Cars[0].Specs.CurrentSpeed - Trains[j].Cars[k].Specs.CurrentSpeed; double s = (Trains[i].Cars[0].Specs.CurrentSpeed * Trains[i].Cars[0].Specs.MassCurrent + Trains[j].Cars[k].Specs.CurrentSpeed * Trains[j].Cars[k].Specs.MassCurrent) / (Trains[i].Cars[0].Specs.MassCurrent + Trains[j].Cars[k].Specs.MassCurrent); Trains[i].Cars[0].Specs.CurrentSpeed = s; Trains[j].Cars[k].Specs.CurrentSpeed = s; double e = 0.5 * (a - d) + 0.0001; TrackManager.UpdateTrackFollower(ref Trains[i].Cars[0].FrontAxle.Follower, Trains[i].Cars[0].FrontAxle.Follower.TrackPosition - e, false, false); TrackManager.UpdateTrackFollower(ref Trains[i].Cars[0].RearAxle.Follower, Trains[i].Cars[0].RearAxle.Follower.TrackPosition - e, false, false); TrackManager.UpdateTrackFollower(ref Trains[j].Cars[k].FrontAxle.Follower, Trains[j].Cars[k].FrontAxle.Follower.TrackPosition + e, false, false); TrackManager.UpdateTrackFollower(ref Trains[j].Cars[k].RearAxle.Follower, Trains[j].Cars[k].RearAxle.Follower.TrackPosition + e, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[i].Cars[0].Specs.MassCurrent + Trains[j].Cars[k].Specs.MassCurrent); double fi = Trains[j].Cars[k].Specs.MassCurrent * f; double fj = Trains[i].Cars[0].Specs.MassCurrent * f; double vi = v * fi; double vj = v * fj; if (vi > Game.CriticalCollisionSpeedDifference) Trains[i].Cars[0].Derailed = true; if (vj > Game.CriticalCollisionSpeedDifference) Trains[j].Cars[k].Derailed = true; } // adjust cars for train i for (int h = 1; h < Trains[i].Cars.Length; h++) { a = Trains[i].Cars[h - 1].RearAxle.Follower.TrackPosition - Trains[i].Cars[h - 1].RearAxlePosition - 0.5 * Trains[i].Cars[h - 1].Length; b = Trains[i].Cars[h].FrontAxle.Follower.TrackPosition - Trains[i].Cars[h].FrontAxlePosition + 0.5 * Trains[i].Cars[h].Length; d = a - b - Trains[i].Couplers[h - 1].MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; TrackManager.UpdateTrackFollower(ref Trains[i].Cars[h].FrontAxle.Follower, Trains[i].Cars[h].FrontAxle.Follower.TrackPosition + d, false, false); TrackManager.UpdateTrackFollower(ref Trains[i].Cars[h].RearAxle.Follower, Trains[i].Cars[h].RearAxle.Follower.TrackPosition + d, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[i].Cars[h - 1].Specs.MassCurrent + Trains[i].Cars[h].Specs.MassCurrent); double fi = Trains[i].Cars[h - 1].Specs.MassCurrent * f; double fj = Trains[i].Cars[h].Specs.MassCurrent * f; double vi = v * fi; double vj = v * fj; if (vi > Game.CriticalCollisionSpeedDifference) Trains[i].Cars[h - 1].Derailed = true; if (vj > Game.CriticalCollisionSpeedDifference) Trains[i].Cars[h].Derailed = true; } Trains[i].Cars[h].Specs.CurrentSpeed = Trains[i].Cars[h - 1].Specs.CurrentSpeed; } } // adjust cars for train j for (int h = Trains[j].Cars.Length - 2; h >= 0; h--) { a = Trains[j].Cars[h + 1].FrontAxle.Follower.TrackPosition - Trains[j].Cars[h + 1].FrontAxlePosition + 0.5 * Trains[j].Cars[h + 1].Length; b = Trains[j].Cars[h].RearAxle.Follower.TrackPosition - Trains[j].Cars[h].RearAxlePosition - 0.5 * Trains[j].Cars[h].Length; d = b - a - Trains[j].Couplers[h].MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; TrackManager.UpdateTrackFollower(ref Trains[j].Cars[h].FrontAxle.Follower, Trains[j].Cars[h].FrontAxle.Follower.TrackPosition - d, false, false); TrackManager.UpdateTrackFollower(ref Trains[j].Cars[h].RearAxle.Follower, Trains[j].Cars[h].RearAxle.Follower.TrackPosition - d, false, false); if (Interface.CurrentOptions.Derailments) { double f = 2.0 / (Trains[j].Cars[h + 1].Specs.MassCurrent + Trains[j].Cars[h].Specs.MassCurrent); double fi = Trains[j].Cars[h + 1].Specs.MassCurrent * f; double fj = Trains[j].Cars[h].Specs.MassCurrent * f; double vi = v * fi; double vj = v * fj; if (vi > Game.CriticalCollisionSpeedDifference) Trains[j].Cars[h + 1].Derailed = true; if (vj > Game.CriticalCollisionSpeedDifference) Trains[j].Cars[h].Derailed = true; } Trains[j].Cars[h].Specs.CurrentSpeed = Trains[j].Cars[h + 1].Specs.CurrentSpeed; } } } } } } } } // with buffers if (i == PlayerTrain.TrainIndex) { double a = Trains[i].Cars[0].FrontAxle.Follower.TrackPosition - Trains[i].Cars[0].FrontAxlePosition + 0.5 * Trains[i].Cars[0].Length; double b = Trains[i].Cars[Trains[i].Cars.Length - 1].RearAxle.Follower.TrackPosition - Trains[i].Cars[Trains[i].Cars.Length - 1].RearAxlePosition - 0.5 * Trains[i].Cars[0].Length; for (int j = 0; j < Game.BufferTrackPositions.Length; j++) { if (a > Game.BufferTrackPositions[j] & b < Game.BufferTrackPositions[j]) { a += 0.0001; b -= 0.0001; double da = a - Game.BufferTrackPositions[j]; double db = Game.BufferTrackPositions[j] - b; if (da < db) { // front TrackManager.UpdateTrackFollower(ref Trains[i].Cars[0].FrontAxle.Follower, Trains[i].Cars[0].FrontAxle.Follower.TrackPosition - da, false, false); TrackManager.UpdateTrackFollower(ref Trains[i].Cars[0].RearAxle.Follower, Trains[i].Cars[0].RearAxle.Follower.TrackPosition - da, false, false); if (Interface.CurrentOptions.Derailments && Math.Abs(Trains[i].Cars[0].Specs.CurrentSpeed) > Game.CriticalCollisionSpeedDifference) { Trains[i].Cars[0].Derailed = true; } Trains[i].Cars[0].Specs.CurrentSpeed = 0.0; for (int h = 1; h < Trains[i].Cars.Length; h++) { a = Trains[i].Cars[h - 1].RearAxle.Follower.TrackPosition - Trains[i].Cars[h - 1].RearAxlePosition - 0.5 * Trains[i].Cars[h - 1].Length; b = Trains[i].Cars[h].FrontAxle.Follower.TrackPosition - Trains[i].Cars[h].FrontAxlePosition + 0.5 * Trains[i].Cars[h].Length; double d = a - b - Trains[i].Couplers[h - 1].MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; TrackManager.UpdateTrackFollower(ref Trains[i].Cars[h].FrontAxle.Follower, Trains[i].Cars[h].FrontAxle.Follower.TrackPosition + d, false, false); TrackManager.UpdateTrackFollower(ref Trains[i].Cars[h].RearAxle.Follower, Trains[i].Cars[h].RearAxle.Follower.TrackPosition + d, false, false); if (Interface.CurrentOptions.Derailments && Math.Abs(Trains[i].Cars[h].Specs.CurrentSpeed) > Game.CriticalCollisionSpeedDifference) { Trains[i].Cars[h].Derailed = true; } Trains[i].Cars[h].Specs.CurrentSpeed = 0.0; } } } else { // rear int c = Trains[i].Cars.Length - 1; TrackManager.UpdateTrackFollower(ref Trains[i].Cars[c].FrontAxle.Follower, Trains[i].Cars[c].FrontAxle.Follower.TrackPosition + db, false, false); TrackManager.UpdateTrackFollower(ref Trains[i].Cars[c].RearAxle.Follower, Trains[i].Cars[c].RearAxle.Follower.TrackPosition + db, false, false); if (Interface.CurrentOptions.Derailments && Math.Abs(Trains[i].Cars[c].Specs.CurrentSpeed) > Game.CriticalCollisionSpeedDifference) { Trains[i].Cars[c].Derailed = true; } Trains[i].Cars[c].Specs.CurrentSpeed = 0.0; for (int h = Trains[i].Cars.Length - 2; h >= 0; h--) { a = Trains[i].Cars[h + 1].FrontAxle.Follower.TrackPosition - Trains[i].Cars[h + 1].FrontAxlePosition + 0.5 * Trains[i].Cars[h + 1].Length; b = Trains[i].Cars[h].RearAxle.Follower.TrackPosition - Trains[i].Cars[h].RearAxlePosition - 0.5 * Trains[i].Cars[h].Length; double d = b - a - Trains[i].Couplers[h].MinimumDistanceBetweenCars; if (d < 0.0) { d -= 0.0001; TrackManager.UpdateTrackFollower(ref Trains[i].Cars[h].FrontAxle.Follower, Trains[i].Cars[h].FrontAxle.Follower.TrackPosition - d, false, false); TrackManager.UpdateTrackFollower(ref Trains[i].Cars[h].RearAxle.Follower, Trains[i].Cars[h].RearAxle.Follower.TrackPosition - d, false, false); if (Interface.CurrentOptions.Derailments && Math.Abs(Trains[i].Cars[h].Specs.CurrentSpeed) > Game.CriticalCollisionSpeedDifference) { Trains[i].Cars[h].Derailed = true; } Trains[i].Cars[h].Specs.CurrentSpeed = 0.0; } } } } } } } } // compute final angles and positions for (int i = 0; i < Trains.Length; i++) { if (Trains[i].State != TrainState.Disposed & Trains[i].State != TrainManager.TrainState.Bogus) { for (int j = 0; j < Trains[i].Cars.Length; j++) { Trains[i].Cars[j].FrontAxle.Follower.UpdateWorldCoordinates(true); Trains[i].Cars[j].RearAxle.Follower.UpdateWorldCoordinates(true); UpdateTopplingCantAndSpring(Trains[i], j, TimeElapsed); } } } } // dispose train internal static void DisposeTrain(Train Train) { Train.State = TrainState.Disposed; for (int i = 0; i < Train.Cars.Length; i++) { int s = Train.Cars[i].CurrentCarSection; if (s >= 0) { for (int j = 0; j < Train.Cars[i].CarSections[s].Elements.Length; j++) { Renderer.HideObject(Train.Cars[i].CarSections[s].Elements[j].ObjectIndex); } } } Sounds.StopAllSounds(Train); for (int i = 0; i < Game.Sections.Length; i++) { Game.Sections[i].Leave(Train); } if (Game.Sections.Length != 0) { Game.UpdateSection(Game.Sections.Length - 1); } } // synchronize train private static void SynchronizeTrain(Train Train) { for (int i = 0; i < Train.Cars.Length; i++) { double s = 0.5 * (Train.Cars[i].FrontAxle.Follower.TrackPosition + Train.Cars[i].RearAxle.Follower.TrackPosition); double d = 0.5 * (Train.Cars[i].FrontAxle.Follower.TrackPosition - Train.Cars[i].RearAxle.Follower.TrackPosition); TrackManager.UpdateTrackFollower(ref Train.Cars[i].FrontAxle.Follower, s + d, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[i].RearAxle.Follower, s - d, false, false); double b = Train.Cars[i].FrontAxle.Follower.TrackPosition - Train.Cars[i].FrontAxlePosition + Train.Cars[i].BeaconReceiverPosition; TrackManager.UpdateTrackFollower(ref Train.Cars[i].BeaconReceiver, b , false, false); } } // update train station private static void UpdateTrainStation(Train Train, double TimeElapsed) { if (Train.Station >= 0) { int i = Train.Station; int n = Game.GetStopIndex(Train.Station, Train.Cars.Length); double tf, tb; if (n >= 0) { double p0 = Train.Cars[0].FrontAxle.Follower.TrackPosition - Train.Cars[0].FrontAxlePosition + 0.5 * Train.Cars[0].Length; double p1 = Game.Stations[i].Stops[n].TrackPosition; tf = Game.Stations[i].Stops[n].ForwardTolerance; tb = Game.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; // automatically open doors if (Train.Specs.DoorOpenMode != DoorMode.Manual) { if ((GetDoorsState(Train, Game.Stations[i].OpenLeftDoors, Game.Stations[i].OpenRightDoors) & TrainDoorState.AllOpened) == 0) { if (Math.Abs(Train.Specs.CurrentAverageSpeed) < 0.1 / 3.6 & Math.Abs(Train.Specs.CurrentAverageAcceleration) < 0.1 / 3.6) { if (Train.StationDistanceToStopPoint < tb & -Train.StationDistanceToStopPoint < tf) { OpenTrainDoors(Train, Game.Stations[i].OpenLeftDoors, Game.Stations[i].OpenRightDoors); } } } } // detect arrival if (Train.Specs.CurrentAverageSpeed > -0.277777777777778 & Train.Specs.CurrentAverageSpeed < 0.277777777777778) { bool left, right; if (Game.Stations[i].OpenLeftDoors) { left = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Specs.AnticipatedLeftDoorsOpened) { left = true; break; } } } else { left = true; } if (Game.Stations[i].OpenRightDoors) { right = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Specs.AnticipatedRightDoorsOpened) { right = true; break; } } } else { right = true; } if (left & right) { // arrival Train.StationState = TrainStopState.Boarding; Train.StationAdjust = false; Sounds.StopSound(Train.Cars[Train.DriverCar].Sounds.Halt.Source); Sounds.SoundBuffer buffer = Game.Stations[i].ArrivalSoundBuffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Game.Stations[i].SoundOrigin; Sounds.PlaySound(buffer, 1.0, 1.0, pos, false); } Train.StationArrivalTime = Game.SecondsSinceMidnight; Train.StationDepartureTime = Game.Stations[i].DepartureTime - Train.TimetableDelta; if (Train.StationDepartureTime - Game.SecondsSinceMidnight < Game.Stations[i].StopTime) { Train.StationDepartureTime = Game.SecondsSinceMidnight + Game.Stations[i].StopTime; } Train.Passengers.PassengerRatio = Game.Stations[i].PassengerRatio; UpdateTrainMassFromPassengerRatio(Train); if (Train == PlayerTrain) { double early = 0.0; if (Game.Stations[i].ArrivalTime >= 0.0) { early = (Game.Stations[i].ArrivalTime - Train.TimetableDelta) - Train.StationArrivalTime; } else { early = 0.0; } string s; if (early < -1.0) { s = Interface.GetInterfaceString("message_station_arrival_late"); } else if (early > 1.0) { s = Interface.GetInterfaceString("message_station_arrival_early"); } else { s = Interface.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 += Interface.GetInterfaceString("message_delimiter") + Interface.GetInterfaceString("message_station_overrun"); } else if (Train.StationDistanceToStopPoint > 0.1) { s += Interface.GetInterfaceString("message_delimiter") + Interface.GetInterfaceString("message_station_underrun"); } double d = Math.Abs(Train.StationDistanceToStopPoint); string c = d.ToString("0.0", Culture); if (Game.Stations[i].StationType == Game.StationType.Terminal) { s += Interface.GetInterfaceString("message_delimiter") + Interface.GetInterfaceString("message_station_terminal"); } s = s.Replace("[name]", Game.Stations[i].Name); s = s.Replace("[time]", b); s = s.Replace("[difference]", c); Game.AddMessage(s, Game.MessageDependency.None, Interface.GameMode.Normal, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 10.0); if (Game.Stations[i].StationType == Game.StationType.Normal) { s = Interface.GetInterfaceString("message_station_deadline"); Game.AddMessage(s, Game.MessageDependency.Station, Interface.GameMode.Normal, Game.MessageColor.Blue, double.PositiveInfinity); } Timetable.UpdateCustomTimetable(Game.Stations[i].TimetableDaytimeTexture, Game.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].Specs.Doors.Length; k++) { Train.Cars[j].Specs.Doors[k].DoorLockDuration = 0.0; if (Game.Stations[i].OpenLeftDoors & Train.Cars[j].Specs.Doors[k].Direction == -1 | Game.Stations[i].OpenRightDoors & Train.Cars[j].Specs.Doors[k].Direction == 1) { double p = 0.005 * Game.Stations[i].PassengerRatio * Game.Stations[i].PassengerRatio * Game.Stations[i].PassengerRatio * Game.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].Specs.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].Specs.Doors[k].DoorLockDuration = (50.0 - 10.0 * p) / (17.0 - 16.0 * p); } } } } } else if (Train.Specs.CurrentAverageSpeed > -0.277777777777778 & Train.Specs.CurrentAverageSpeed < 0.277777777777778) { // correct stop position if (!Train.StationAdjust & (Train.StationDistanceToStopPoint > tb | Train.StationDistanceToStopPoint < -tf)) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.Adjust.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.Adjust.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } if (Train == TrainManager.PlayerTrain) { Game.AddMessage(Interface.GetInterfaceString("message_station_correct"), Game.MessageDependency.None, Interface.GameMode.Normal, Game.MessageColor.Orange, Game.SecondsSinceMidnight + 5.0); } Train.StationAdjust = true; } } else { Train.StationAdjust = false; } } } } else if (Train.StationState == TrainStopState.Boarding) { // automatically close doors if (Train.Specs.DoorCloseMode != DoorMode.Manual & Game.Stations[i].StationType == Game.StationType.Normal) { if (Game.SecondsSinceMidnight >= Train.StationDepartureTime - 1.0 / Train.Cars[Train.DriverCar].Specs.DoorCloseFrequency) { if ((GetDoorsState(Train, true, true) & TrainDoorState.AllClosed) == 0) { CloseTrainDoors(Train, true, true); } } } // detect departure bool left, right; if (!Game.Stations[i].OpenLeftDoors & !Game.Stations[i].OpenRightDoors) { left = true; right = true; } else { if (Game.Stations[i].OpenLeftDoors) { left = false; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].State != 0.0) { left = true; break; } } if (left) break; } } else { left = false; } if (Game.Stations[i].OpenRightDoors) { right = false; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].State != 0.0) { right = true; break; } } if (right) break; } } else { right = false; } } if (left | right) { // departure message if (Game.SecondsSinceMidnight > Train.StationDepartureTime) { Train.StationState = TrainStopState.Completed; if (Train == PlayerTrain & Game.Stations[i].StationType == Game.StationType.Normal) { if (!Game.Stations[i].OpenLeftDoors & !Game.Stations[i].OpenRightDoors | Train.Specs.DoorCloseMode != DoorMode.Manual) { Game.AddMessage(Interface.GetInterfaceString("message_station_depart"), Game.MessageDependency.None, Interface.GameMode.Normal, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 5.0); } else { Game.AddMessage(Interface.GetInterfaceString("message_station_depart_closedoors"), Game.MessageDependency.None, Interface.GameMode.Normal, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 5.0); } } else if (Game.Stations[i].StationType == Game.StationType.ChangeEnds) { //Game.AddMessage("CHANGE ENDS", Game.MessageDependency.None, Interface.GameMode.Expert, Game.MessageColor.Magenta, Game.SecondsSinceMidnight + 5.0); JumpTrain(Train, i + 1); } } // passengers boarding for (int j = 0; j < Train.Cars.Length; j++) { double r = 2.0 * Game.Stations[i].PassengerRatio * TimeElapsed; if (r >= Program.RandomNumberGenerator.NextDouble()) { int d = (int)Math.Floor(Program.RandomNumberGenerator.NextDouble() * (double)Train.Cars[j].Specs.Doors.Length); if (Train.Cars[j].Specs.Doors[d].State == 1.0) { Train.Cars[j].Specs.CurrentRollShakeDirection += (double)Train.Cars[j].Specs.Doors[d].Direction; } } } } else { Train.StationState = TrainStopState.Completed; if (Train == PlayerTrain & Game.Stations[i].StationType == Game.StationType.Normal) { Game.AddMessage(Interface.GetInterfaceString("message_station_depart"), Game.MessageDependency.None, Interface.GameMode.Normal, Game.MessageColor.Blue, Game.SecondsSinceMidnight + 5.0); } } // departure sound if (!Train.StationDepartureSoundPlayed) { Sounds.SoundBuffer buffer = Game.Stations[i].DepartureSoundBuffer; if (buffer != null) { double dur = Sounds.GetDuration(buffer); if (Game.SecondsSinceMidnight >= Train.StationDepartureTime - dur) { Sounds.PlaySound(buffer, 1.0, 1.0, Game.Stations[i].SoundOrigin, false); Train.StationDepartureSoundPlayed = true; } } } } } else { Train.StationState = TrainStopState.Pending; } // automatically close doors if (Train.Specs.DoorCloseMode == DoorMode.Automatic) { if (Train.Station == -1 | Train.StationState == TrainStopState.Completed) { if ((GetDoorsState(Train, true, true) & TrainDoorState.AllClosed) == 0) { CloseTrainDoors(Train, true, true); } } } } // update train doors private static void UpdateTrainDoors(Train Train, double TimeElapsed) { OpenBveApi.Runtime.DoorStates oldState = OpenBveApi.Runtime.DoorStates.None; OpenBveApi.Runtime.DoorStates newState = OpenBveApi.Runtime.DoorStates.None; for (int i = 0; i < Train.Cars.Length; i++) { bool ld = Train.Cars[i].Specs.AnticipatedLeftDoorsOpened; bool rd = Train.Cars[i].Specs.AnticipatedRightDoorsOpened; double os = Train.Cars[i].Specs.DoorOpenFrequency; double cs = Train.Cars[i].Specs.DoorCloseFrequency; for (int j = 0; j < Train.Cars[i].Specs.Doors.Length; j++) { if (Train.Cars[i].Specs.Doors[j].Direction == -1 | Train.Cars[i].Specs.Doors[j].Direction == 1) { bool shouldBeOpen = Train.Cars[i].Specs.Doors[j].Direction == -1 ? ld : rd; if (Train.Cars[i].Specs.Doors[j].State > 0.0) { if (Train.Cars[i].Specs.Doors[j].Direction == -1) { oldState |= OpenBveApi.Runtime.DoorStates.Left; } else { oldState |= OpenBveApi.Runtime.DoorStates.Right; } } if (shouldBeOpen) { // open Train.Cars[i].Specs.Doors[j].State += os * TimeElapsed; if (Train.Cars[i].Specs.Doors[j].State > 1.0) { Train.Cars[i].Specs.Doors[j].State = 1.0; } } else { // close if (Train.Cars[i].Specs.Doors[j].DoorLockDuration > 0.0) { if (Train.Cars[i].Specs.Doors[j].State > Train.Cars[i].Specs.Doors[j].DoorLockState) { Train.Cars[i].Specs.Doors[j].State -= cs * TimeElapsed; } if (Train.Cars[i].Specs.Doors[j].State < Train.Cars[i].Specs.Doors[j].DoorLockState) { Train.Cars[i].Specs.Doors[j].State = Train.Cars[i].Specs.Doors[j].DoorLockState; } Train.Cars[i].Specs.Doors[j].DoorLockDuration -= TimeElapsed; if (Train.Cars[i].Specs.Doors[j].DoorLockDuration < 0.0) { Train.Cars[i].Specs.Doors[j].DoorLockDuration = 0.0; } } else { Train.Cars[i].Specs.Doors[j].State -= cs * TimeElapsed; } if (Train.Cars[i].Specs.Doors[j].State < 0.0) { Train.Cars[i].Specs.Doors[j].State = 0.0; } } if (Train.Cars[i].Specs.Doors[j].State > 0.0) { if (Train.Cars[i].Specs.Doors[j].Direction == -1) { newState |= OpenBveApi.Runtime.DoorStates.Left; } else { newState |= OpenBveApi.Runtime.DoorStates.Right; } } } } } // door changed if (oldState != OpenBveApi.Runtime.DoorStates.None & newState == OpenBveApi.Runtime.DoorStates.None) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.PilotLampOn.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.PilotLampOn.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } else if (oldState == OpenBveApi.Runtime.DoorStates.None & newState != OpenBveApi.Runtime.DoorStates.None) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.PilotLampOff.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.PilotLampOff.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } if (oldState != newState) { if (Train.Plugin != null) { Train.Plugin.DoorChange(oldState, newState); } } } // train doors internal static void OpenTrainDoors(Train Train, bool Left, bool Right) { bool sl = false, sr = false; for (int i = 0; i < Train.Cars.Length; i++) { if (Left & !Train.Cars[i].Specs.AnticipatedLeftDoorsOpened) { Train.Cars[i].Specs.AnticipatedLeftDoorsOpened = true; sl = true; } if (Right & !Train.Cars[i].Specs.AnticipatedRightDoorsOpened) { Train.Cars[i].Specs.AnticipatedRightDoorsOpened = true; sr = true; } } if (sl) { for (int i = 0; i < Train.Cars.Length; i++) { Sounds.SoundBuffer buffer = Train.Cars[i].Sounds.DoorOpenL.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Sounds.DoorOpenL.Position; Sounds.PlaySound(buffer, Train.Cars[i].Specs.DoorOpenPitch, 1.0, pos, Train, i, false); } for (int j = 0; j < Train.Cars[i].Specs.Doors.Length; j++) { if (Train.Cars[i].Specs.Doors[j].Direction == -1) { Train.Cars[i].Specs.Doors[j].DoorLockDuration = 0.0; } } } } if (sr) { for (int i = 0; i < Train.Cars.Length; i++) { Sounds.SoundBuffer buffer = Train.Cars[i].Sounds.DoorOpenR.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Sounds.DoorOpenR.Position; Sounds.PlaySound(buffer, Train.Cars[i].Specs.DoorClosePitch, 1.0, pos, Train, i, false); } for (int j = 0; j < Train.Cars[i].Specs.Doors.Length; j++) { if (Train.Cars[i].Specs.Doors[j].Direction == 1) { Train.Cars[i].Specs.Doors[j].DoorLockDuration = 0.0; } } } } } internal static void CloseTrainDoors(Train Train, bool Left, bool Right) { bool sl = false, sr = false; for (int i = 0; i < Train.Cars.Length; i++) { if (Left & Train.Cars[i].Specs.AnticipatedLeftDoorsOpened) { Train.Cars[i].Specs.AnticipatedLeftDoorsOpened = false; sl = true; } if (Right & Train.Cars[i].Specs.AnticipatedRightDoorsOpened) { Train.Cars[i].Specs.AnticipatedRightDoorsOpened = false; sr = true; } } if (sl) { for (int i = 0; i < Train.Cars.Length; i++) { Sounds.SoundBuffer buffer = Train.Cars[i].Sounds.DoorCloseL.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Sounds.DoorCloseL.Position; Sounds.PlaySound(buffer, Train.Cars[i].Specs.DoorClosePitch, 1.0, pos, Train, i, false); } } } if (sr) { for (int i = 0; i < Train.Cars.Length; i++) { Sounds.SoundBuffer buffer = Train.Cars[i].Sounds.DoorCloseR.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Sounds.DoorCloseR.Position; Sounds.PlaySound(buffer, Train.Cars[i].Specs.DoorClosePitch, 1.0, pos, Train, i, false); } } } } /// Specifies enumerated constants that can be combined to represent doors states. internal enum TrainDoorState { None = 0, /// Fully closed doors are present in the train. Closed = 1, /// Fully opened doors are present in the train. Opened = 2, /// Doors are present in the train which are neither fully closed nor fully opened. Mixed = 4, /// All doors in the train are fully closed. AllClosed = 8, /// All doors in the train are fully opened. AllOpened = 16, /// All doors in the train are neither fully closed nor fully opened. AllMixed = 32 } /// Returns the combination of door states encountered in a train. /// The train to consider. /// Whether to include left doors. /// Whether to include right doors. /// A bit mask combining encountered door states. internal static TrainDoorState GetDoorsState(Train Train, bool Left, bool Right) { bool opened = false, closed = false, mixed = false; for (int i = 0; i < Train.Cars.Length; i++) { for (int j = 0; j < Train.Cars[i].Specs.Doors.Length; j++) { if (Left & Train.Cars[i].Specs.Doors[j].Direction == -1 | Right & Train.Cars[i].Specs.Doors[j].Direction == 1) { if (Train.Cars[i].Specs.Doors[j].State == 0.0) { closed = true; } else if (Train.Cars[i].Specs.Doors[j].State == 1.0) { opened = true; } else { mixed = true; } } } } TrainDoorState Result = TrainDoorState.None; if (opened) Result |= TrainDoorState.Opened; if (closed) Result |= TrainDoorState.Closed; if (mixed) Result |= TrainDoorState.Mixed; if (opened & !closed & !mixed) Result |= TrainDoorState.AllOpened; if (!opened & closed & !mixed) Result |= TrainDoorState.AllClosed; if (!opened & !closed & mixed) Result |= TrainDoorState.AllMixed; return Result; } // update safety system private static void UpdateSafetySystem(Train Train, double TimeElapsed) { Game.UpdatePluginSections(Train); if (Train.Plugin != null) { Train.Plugin.LastSection = Train.CurrentSectionIndex; Train.Plugin.UpdatePlugin(); } } // update brake system private static void UpdateBrakeSystem(Train Train, double TimeElapsed, out double[] DecelerationDueToBrake, out double[] DecelerationDueToMotor) { // individual brake systems DecelerationDueToBrake = new double[Train.Cars.Length]; DecelerationDueToMotor = new double[Train.Cars.Length]; for (int i = 0; i < Train.Cars.Length; i++) { UpdateBrakeSystem(Train, i, TimeElapsed, out DecelerationDueToBrake[i], out DecelerationDueToMotor[i]); } // brake pipe pressure distribution dummy (just averages) double TotalPressure = 0.0; for (int i = 0; i < Train.Cars.Length; i++) { if (i > 0) { if (Train.Cars[i - 1].Derailed | Train.Cars[i].Derailed) { Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure -= Game.BrakePipeLeakRate * TimeElapsed; if (Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure < 0.0) Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure = 0.0; } } if (i < Train.Cars.Length - 1) { if (Train.Cars[i].Derailed | Train.Cars[i + 1].Derailed) { Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure -= Game.BrakePipeLeakRate * TimeElapsed; if (Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure < 0.0) Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure = 0.0; } } TotalPressure += Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure; } double AveragePressure = TotalPressure / (double)Train.Cars.Length; for (int i = 0; i < Train.Cars.Length; i++) { Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure = AveragePressure; } } private static void UpdateBrakeSystem(Train Train, int CarIndex, double TimeElapsed, out double DecelerationDueToBrake, out double DecelerationDueToMotor) { // air compressor if (Train.Cars[CarIndex].Specs.AirBrake.Type == AirBrakeType.Main) { if (Train.Cars[CarIndex].Specs.AirBrake.AirCompressorEnabled) { if (Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure > Train.Cars[CarIndex].Specs.AirBrake.AirCompressorMaximumPressure) { Train.Cars[CarIndex].Specs.AirBrake.AirCompressorEnabled = false; Train.Cars[CarIndex].Sounds.CpLoopStarted = false; Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.CpEnd.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.CpEnd.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, CarIndex, false); } buffer = Train.Cars[CarIndex].Sounds.CpLoop.Buffer; if (buffer != null) { Sounds.StopSound(Train.Cars[CarIndex].Sounds.CpLoop.Source); } } else { Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure += Train.Cars[CarIndex].Specs.AirBrake.AirCompressorRate * TimeElapsed; if (!Train.Cars[CarIndex].Sounds.CpLoopStarted && Game.SecondsSinceMidnight > Train.Cars[CarIndex].Sounds.CpStartTimeStarted + 5.0) { Train.Cars[CarIndex].Sounds.CpLoopStarted = true; Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.CpLoop.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.CpLoop.Position; Train.Cars[CarIndex].Sounds.CpLoop.Source = Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, CarIndex, true); } } } } else { if (Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure < Train.Cars[CarIndex].Specs.AirBrake.AirCompressorMinimumPressure) { Train.Cars[CarIndex].Specs.AirBrake.AirCompressorEnabled = true; Train.Cars[CarIndex].Sounds.CpStartTimeStarted = Game.SecondsSinceMidnight; Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.CpStart.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.CpStart.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, CarIndex, false); } } } } // initialize const double Tolerance = 5000.0; int airsound = -1; // equalizing reservoir if (Train.Cars[CarIndex].Specs.AirBrake.Type == AirBrakeType.Main) { if (Train.Specs.CurrentEmergencyBrake.Actual) { double r = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirEmergencyRate; double d = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirNormalPressure; r = GetRate(d / m, r * TimeElapsed); if (r > Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure) r = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure -= r; } else { if (Train.Cars[CarIndex].Specs.BrakeType == CarBrakeType.AutomaticAirBrake) { /// automatic air brake if (Train.Specs.AirBrake.Handle.Actual == AirBrakeHandleState.Service) { double r = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirServiceRate; double d = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirNormalPressure; r = GetRate(d / m, r * TimeElapsed); if (r > Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure) r = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure -= r; } else if (Train.Specs.AirBrake.Handle.Actual == AirBrakeHandleState.Release) { double r = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirChargeRate; double d = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirNormalPressure - Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirNormalPressure; r = GetRate(d / m, r * TimeElapsed); if (r > d) r = d; d = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; if (r > d) r = d; double f = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirEqualizingReservoirCoefficient; double s = r * f * TimeElapsed; if (s > Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure) { r *= Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure / s; s = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure; } Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure += 0.5 * r; Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure -= 0.5 * s; } } else if (Train.Cars[CarIndex].Specs.BrakeType == CarBrakeType.ElectromagneticStraightAirBrake) { /// electromagnetic straight air brake double r = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirChargeRate; double d = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirNormalPressure - Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirNormalPressure; r = GetRate(d / m, r * TimeElapsed); if (r > d) r = d; d = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; if (r > d) r = d; double f = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirEqualizingReservoirCoefficient; double s = r * f * TimeElapsed; if (s > Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure) { r *= Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure / s; s = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure; } Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure += 0.5 * r; Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure -= 0.5 * s; } } } // brake pipe (main reservoir) if ((Train.Cars[CarIndex].Specs.BrakeType == CarBrakeType.AutomaticAirBrake | Train.Cars[CarIndex].Specs.BrakeType == CarBrakeType.ElectromagneticStraightAirBrake) & Train.Cars[CarIndex].Specs.AirBrake.Type == AirBrakeType.Main) { if (Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure > Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure + Tolerance) { /// brake pipe exhaust valve double r = Train.Specs.CurrentEmergencyBrake.Actual ? Train.Cars[CarIndex].Specs.AirBrake.BrakePipeEmergencyRate : Train.Cars[CarIndex].Specs.AirBrake.BrakePipeServiceRate; double d = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirNormalPressure; r = (0.5 + 1.5 * d / m) * r * TimeElapsed; if (r > d) r = d; Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure -= r; } else if (Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure + Tolerance < Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure) { /// fill brake pipe from main reservoir double r = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeChargeRate; double d = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirNormalPressure; r = (0.5 + 1.5 * d / m) * r * TimeElapsed; if (r > d) r = d; d = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeNormalPressure - Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure; if (r > d) r = d; double f = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirBrakePipeCoefficient; double s = r * f; if (s > Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure) { r *= Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure / s; s = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure; } Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure += 0.5 * r; Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure -= 0.5 * s; } } // triple valve (auxillary reservoir, brake pipe, brake cylinder) if (Train.Cars[CarIndex].Specs.BrakeType == CarBrakeType.AutomaticAirBrake) { if (Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure + Tolerance < Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure) { if (Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure + Tolerance < Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure) { /// back-flow from brake cylinder to auxillary reservoir double u = (Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure - Tolerance) / Tolerance; if (u > 1.0) u = 1.0; double f = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirBrakeCylinderCoefficient; double r = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceChargeRate * f; double d = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirMaximumPressure; r = GetRate(d * u / m, r * TimeElapsed); if (Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure + r > m) { r = m - Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure; } if (r > d) r = d; double s = r / f; if (s > d) { r *= d / s; s = d; } if (s > Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure) { r *= Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure / s; s = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; } Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure += 0.5 * r; Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure -= 0.5 * s; } else if (Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure > Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure + Tolerance) { /// refill brake cylinder from auxillary reservoir double u = (Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure - Tolerance) / Tolerance; if (u > 1.0) u = 1.0; double f = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirBrakeCylinderCoefficient; double r = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceChargeRate * f; double d = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirMaximumPressure; r = GetRate(d * u / m, r * TimeElapsed); if (r > Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure) { r = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure; } if (r > d) r = d; double s = r / f; if (s > d) { r *= d / s; s = d; } d = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure - Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; if (s > d) { r *= d / s; s = d; } Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure -= 0.5 * r; Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure += 0.5 * s; } /// air sound Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; } else if (Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure > Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure + Tolerance) { double u = (Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure - Tolerance) / Tolerance; if (u > 1.0) u = 1.0; { /// refill auxillary reservoir from brake pipe double r = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirChargeRate; double d = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirMaximumPressure; r = GetRate(d * u / m, r * TimeElapsed); if (r > Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure) { r = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure; } if (r > d) r = d; d = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirMaximumPressure - Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure; if (r > d) r = d; double f = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirBrakePipeCoefficient; double s = r / f; if (s > Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure) { r *= Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure / s; s = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure; } if (s > d) { r *= d / s; s = d; } Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure += 0.5 * r; Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure -= 0.5 * s; } { /// brake cylinder release double r = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderReleaseRate; double d = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; r = GetRate(d * u / m, r * TimeElapsed); if (r > Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure) r = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure -= r; /// air sound if (r > 0.0 & Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure < Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure) { double p = 0.8 * Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure - 0.2 * Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; if (p < 0.0) p = 0.0; Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure = p; airsound = p < Tolerance ? 0 : Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure > m - Tolerance ? 2 : 1; } } } else { /// air sound Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; } } // solenoid valve for electromagnetic straight air brake (auxillary reservoir, electric command, brake cylinder) if (Train.Cars[CarIndex].Specs.BrakeType == CarBrakeType.ElectromagneticStraightAirBrake) { /// refill auxillary reservoir from brake pipe if (Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure > Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure + Tolerance) { double r = 2.0 * Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirChargeRate; double d = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirMaximumPressure; r = GetRate(d / m, r * TimeElapsed); if (r > Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure) { r = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure; } if (r > d) r = d; d = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirMaximumPressure - Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure; if (r > d) r = d; double f = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirBrakePipeCoefficient; double s = r / f; if (s > Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure) { r *= Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure / s; s = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure; } if (s > d) { r *= d / s; s = d; } Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure += 0.5 * r; Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure -= 0.5 * s; } { /// electric command bool emergency; if (Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure + Tolerance < Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure) { emergency = true; } else { emergency = Train.Specs.CurrentEmergencyBrake.Actual; } double p; if (emergency) { p = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; } else { p = (double)Train.Specs.CurrentBrakeNotch.Actual / (double)Train.Specs.MaximumBrakeNotch; p *= Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; } if (Train.Cars[CarIndex].Specs.IsMotorCar & !Train.Specs.CurrentEmergencyBrake.Actual & Train.Specs.CurrentReverser.Actual != 0) { /// brake control system if (Math.Abs(Train.Cars[CarIndex].Specs.CurrentSpeed) > Train.Cars[CarIndex].Specs.BrakeControlSpeed) { if (Train.Cars[CarIndex].Specs.ElectropneumaticType == EletropneumaticBrakeType.ClosingElectromagneticValve) { /// closing electromagnetic valve (lock-out valve) p = 0.0; } else if (Train.Cars[CarIndex].Specs.ElectropneumaticType == EletropneumaticBrakeType.DelayFillingControl) { /// delay-filling control double f = (double)Train.Specs.CurrentBrakeNotch.Actual / (double)Train.Specs.MaximumBrakeNotch; double a = Train.Cars[CarIndex].Specs.MotorDeceleration; double pr = p / Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; double b = pr * Train.Cars[CarIndex].Specs.BrakeDecelerationAtServiceMaximumPressure; double d = b - a; if (d > 0.0) { p = d / Train.Cars[CarIndex].Specs.BrakeDecelerationAtServiceMaximumPressure; if (p > 1.0) p = 1.0; p *= Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; } else { p = 0.0; } } } } if (Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure > p + Tolerance) { /// brake cylinder release double r = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderReleaseRate; double d = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure - p; double m = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; r = GetRate(d / m, r * TimeElapsed); if (r > Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure) r = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; if (r > d) r = d; /// air sound if (r > 0.0 & Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure < Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure) { Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure = p; airsound = p < Tolerance ? 0 : Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure > m - Tolerance ? 2 : 1; } /// pressure change Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure -= r; } else if (Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure + Tolerance < p) { /// refill brake cylinder from auxillary reservoir double f = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirBrakeCylinderCoefficient; double r; if (emergency) { r = 2.0 * Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyChargeRate * f; } else { r = 2.0 * Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceChargeRate * f; } double d = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure - Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; r = GetRate(d / m, r * TimeElapsed); if (r > Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure) { r = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure; } if (r > d) r = d; double s = r / f; if (s > d) { r *= d / s; s = d; } d = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure - Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; if (s > d) { r *= d / s; s = d; } Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirCurrentPressure -= 0.5 * r; Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure += 0.5 * s; /// air sound Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; } else { /// air sound Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; } } } // valves for electric command brake (main reservoir, electric command, brake cylinder) if (Train.Cars[CarIndex].Specs.BrakeType == CarBrakeType.ElectricCommandBrake) { double p; if (Train.Specs.CurrentEmergencyBrake.Actual) { p = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; } else { p = (double)Train.Specs.CurrentBrakeNotch.Actual / (double)Train.Specs.MaximumBrakeNotch; p *= Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; } if (!Train.Specs.CurrentEmergencyBrake.Actual & Train.Specs.CurrentReverser.Actual != 0) { /// brake control system if (Train.Cars[CarIndex].Specs.IsMotorCar & Math.Abs(Train.Cars[CarIndex].Specs.CurrentSpeed) > Train.Cars[CarIndex].Specs.BrakeControlSpeed) { if (Train.Cars[CarIndex].Specs.ElectropneumaticType == EletropneumaticBrakeType.ClosingElectromagneticValve) { /// closing electromagnetic valve (lock-out valve) p = 0.0; } else if (Train.Cars[CarIndex].Specs.ElectropneumaticType == EletropneumaticBrakeType.DelayFillingControl) { /// delay-filling control double f = (double)Train.Specs.CurrentBrakeNotch.Actual / (double)Train.Specs.MaximumBrakeNotch; double a = Train.Cars[CarIndex].Specs.MotorDeceleration; double pr = p / Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; double b = pr * Train.Cars[CarIndex].Specs.BrakeDecelerationAtServiceMaximumPressure; double d = b - a; if (d > 0.0) { p = d / Train.Cars[CarIndex].Specs.BrakeDecelerationAtServiceMaximumPressure; if (p > 1.0) p = 1.0; p *= Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; } else { p = 0.0; } } } } if (Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure > p + Tolerance | p == 0.0) { /// brake cylinder exhaust valve double r = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderReleaseRate; double d = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; r = GetRate(d / m, r * TimeElapsed); if (r > d) r = d; /// air sound if (r > 0.0 & Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure < Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure) { Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure = p; airsound = p < Tolerance ? 0 : Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure > m - Tolerance ? 2 : 1; } /// pressure change Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure -= r; } else if ((Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure + Tolerance < p | p == Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure) & Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure + Tolerance < Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure) { /// fill brake cylinder from main reservoir double r; if (Train.Specs.CurrentEmergencyBrake.Actual) { r = 2.0 * Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyChargeRate; } else { r = 2.0 * Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceChargeRate; } double pm = p < Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure ? p : Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure; double d = pm - Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; r = GetRate(d / m, r * TimeElapsed); if (r > d) r = d; double f1 = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirBrakeCylinderCoefficient; double f2 = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirBrakePipeCoefficient; double f3 = Train.Cars[CarIndex].Specs.AirBrake.AuxillaryReservoirBrakePipeCoefficient; double f = f1 * f2 / f3; /// MainReservoirBrakeCylinderCoefficient double s = r * f; if (s > Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure) { r *= Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure / s; s = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure; } Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure += 0.5 * r; Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure -= 0.5 * s; /// air sound Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; } else { /// air sound Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; } } // straight air pipe (for compatibility needle only) if (Train.Cars[CarIndex].Specs.BrakeType == CarBrakeType.ElectromagneticStraightAirBrake & Train.Cars[CarIndex].Specs.AirBrake.Type == AirBrakeType.Main) { double p; if (Train.Specs.CurrentEmergencyBrake.Actual) { p = 0.0; } else { p = (double)Train.Specs.CurrentBrakeNotch.Actual / (double)Train.Specs.MaximumBrakeNotch; p *= Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; } if (p + Tolerance < Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeCurrentPressure) { double r; if (Train.Specs.CurrentEmergencyBrake.Actual) { r = Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeEmergencyRate; } else { r = Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeReleaseRate; } double d = Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeCurrentPressure - p; double m = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; r = GetRate(d / m, r * TimeElapsed); if (r > d) r = d; Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeCurrentPressure -= r; } else if (p > Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeCurrentPressure + Tolerance) { double r = Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeServiceRate; double d = p - Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeCurrentPressure; double m = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; r = GetRate(d / m, r * TimeElapsed); if (r > d) r = d; Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeCurrentPressure += r; } } else if (Train.Cars[CarIndex].Specs.BrakeType == CarBrakeType.ElectricCommandBrake) { double p; if (Train.Specs.CurrentEmergencyBrake.Actual) { p = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; } else { p = (double)Train.Specs.CurrentBrakeNotch.Actual / (double)Train.Specs.MaximumBrakeNotch; p *= Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; } Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeCurrentPressure = p; } // air sound if (airsound == 0) { /// air zero Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.AirZero.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.AirZero.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, CarIndex, false); } } else if (airsound == 1) { /// air Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.Air.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.Air.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, CarIndex, false); } } else if (airsound == 2) { /// air high Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.AirHigh.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.AirHigh.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, CarIndex, false); } } // deceleration provided by brake double pressureratio = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure / Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; DecelerationDueToBrake = pressureratio * Train.Cars[CarIndex].Specs.BrakeDecelerationAtServiceMaximumPressure; // deceleration provided by motor if (Train.Cars[CarIndex].Specs.BrakeType != CarBrakeType.AutomaticAirBrake && Math.Abs(Train.Cars[CarIndex].Specs.CurrentSpeed) >= Train.Cars[CarIndex].Specs.BrakeControlSpeed & Train.Specs.CurrentReverser.Actual != 0 & !Train.Specs.CurrentEmergencyBrake.Actual) { double f = (double)Train.Specs.CurrentBrakeNotch.Actual / (double)Train.Specs.MaximumBrakeNotch; double a = Train.Cars[CarIndex].Specs.MotorDeceleration; DecelerationDueToMotor = f * a; } else { DecelerationDueToMotor = 0.0; } // hold brake if (Train.Specs.CurrentHoldBrake.Actual & DecelerationDueToMotor == 0.0) { if (Game.SecondsSinceMidnight >= Train.Cars[CarIndex].Specs.HoldBrake.NextUpdateTime) { Train.Cars[CarIndex].Specs.HoldBrake.NextUpdateTime = Game.SecondsSinceMidnight + Train.Cars[CarIndex].Specs.HoldBrake.UpdateInterval; Train.Cars[CarIndex].Specs.HoldBrake.CurrentAccelerationOutput += 0.8 * Train.Cars[CarIndex].Specs.CurrentAcceleration * (double)Math.Sign(Train.Cars[CarIndex].Specs.CurrentPerceivedSpeed); if (Train.Cars[CarIndex].Specs.HoldBrake.CurrentAccelerationOutput < 0.0) Train.Cars[CarIndex].Specs.HoldBrake.CurrentAccelerationOutput = 0.0; double a = Train.Cars[CarIndex].Specs.MotorDeceleration; if (Train.Cars[CarIndex].Specs.HoldBrake.CurrentAccelerationOutput > a) Train.Cars[CarIndex].Specs.HoldBrake.CurrentAccelerationOutput = a; } DecelerationDueToMotor = Train.Cars[CarIndex].Specs.HoldBrake.CurrentAccelerationOutput; } else { Train.Cars[CarIndex].Specs.HoldBrake.CurrentAccelerationOutput = 0.0; } { // rub sound Sounds.SoundBuffer buffer = Train.Cars[CarIndex].Sounds.Rub.Buffer; if (buffer != null) { double spd = Math.Abs(Train.Cars[CarIndex].Specs.CurrentSpeed); double pitch = 1.0 / (spd + 1.0) + 1.0; double gain = Train.Cars[CarIndex].Derailed ? 0.0 : pressureratio; if (spd < 1.38888888888889) { double t = spd * spd; gain *= 1.5552 * t - 0.746496 * spd * t; } else if (spd > 12.5) { double t = spd - 12.5; const double fadefactor = 0.1; gain *= 1.0 / (fadefactor * t * t + 1.0); } if (Sounds.IsPlaying(Train.Cars[CarIndex].Sounds.Rub.Source)) { if (pitch > 0.01 & gain > 0.001) { Train.Cars[CarIndex].Sounds.Rub.Source.Pitch = pitch; Train.Cars[CarIndex].Sounds.Rub.Source.Volume = gain; } else { Sounds.StopSound(Train.Cars[CarIndex].Sounds.Rub.Source); } } else if (pitch > 0.02 & gain > 0.01) { OpenBveApi.Math.Vector3 pos = Train.Cars[CarIndex].Sounds.Rub.Position; Train.Cars[CarIndex].Sounds.Rub.Source = Sounds.PlaySound(buffer, pitch, gain, pos, Train, CarIndex, true); } } } } private static double GetRate(double Ratio, double Factor) { Ratio = Ratio < 0.0 ? 0.0 : Ratio > 1.0 ? 1.0 : Ratio; Ratio = 1.0 - Ratio; return 1.5 * Factor * (1.01 - Ratio * Ratio); } // apply emergency brake internal static void ApplyEmergencyBrake(Train Train) { // sound if (!Train.Specs.CurrentEmergencyBrake.Driver) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.BrakeHandleMax.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.BrakeHandleMax.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } for (int i = 0; i < Train.Cars.Length; i++) { buffer = Train.Cars[i].Sounds.EmrBrake.Buffer; buffer = Train.Cars[Train.DriverCar].Sounds.EmrBrake.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Sounds.EmrBrake.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } } // apply if (Train.Specs.SingleHandle) { ApplyNotch(Train, 0, false, Train.Specs.MaximumBrakeNotch, true); } else { ApplyNotch(Train, 0, true, Train.Specs.MaximumBrakeNotch, true); } ApplyAirBrakeHandle(Train, AirBrakeHandleState.Service); Train.Specs.CurrentEmergencyBrake.Driver = true; Train.Specs.CurrentHoldBrake.Driver = false; Train.Specs.CurrentConstSpeed = false; // plugin if (Train.Plugin != null) { Train.Plugin.UpdatePower(); Train.Plugin.UpdateBrake(); } } // unapply emergency brake internal static void UnapplyEmergencyBrake(Train Train) { if (Train.Specs.CurrentEmergencyBrake.Driver) { // sound Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.BrakeHandleRelease.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.BrakeHandleRelease.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } // apply if (Train.Specs.SingleHandle) { ApplyNotch(Train, 0, false, Train.Specs.MaximumBrakeNotch, true); } else { ApplyNotch(Train, 0, true, Train.Specs.MaximumBrakeNotch, true); } ApplyAirBrakeHandle(Train, AirBrakeHandleState.Service); Train.Specs.CurrentEmergencyBrake.Driver = false; // plugin if (Train.Plugin != null) { Train.Plugin.UpdatePower(); Train.Plugin.UpdateBrake(); } } } // apply hold brake internal static void ApplyHoldBrake(Train Train, bool Value) { Train.Specs.CurrentHoldBrake.Driver = Value; if (Train.Plugin != null) { Train.Plugin.UpdatePower(); Train.Plugin.UpdateBrake(); } } // apply reverser internal static void ApplyReverser(Train Train, int Value, bool Relative) { int a = Train.Specs.CurrentReverser.Driver; int r = Relative ? a + Value : Value; if (r < -1) r = -1; if (r > 1) r = 1; if (a != r) { Train.Specs.CurrentReverser.Driver = r; if (Train.Plugin != null) { Train.Plugin.UpdateReverser(); } Game.AddBlackBoxEntry(Game.BlackBoxEventToken.None); // sound if (a == 0 & r != 0) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.ReverserOn.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.ReverserOn.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } else if (a != 0 & r == 0) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.ReverserOff.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.ReverserOff.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } } } // apply notch internal static void ApplyNotch(Train Train, int PowerValue, bool PowerRelative, int BrakeValue, bool BrakeRelative) { // determine notch int p = PowerRelative ? PowerValue + Train.Specs.CurrentPowerNotch.Driver : PowerValue; if (p < 0) { p = 0; } else if (p > Train.Specs.MaximumPowerNotch) { p = Train.Specs.MaximumPowerNotch; } int b = BrakeRelative ? BrakeValue + Train.Specs.CurrentBrakeNotch.Driver : BrakeValue; if (b < 0) { b = 0; } else if (b > Train.Specs.MaximumBrakeNotch) { b = Train.Specs.MaximumBrakeNotch; } // power sound if (p < Train.Specs.CurrentPowerNotch.Driver) { if (p > 0) { // down (not min) Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.MasterControllerDown.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.MasterControllerDown.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } else { // min Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.MasterControllerMin.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.MasterControllerMin.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } } else if (p > Train.Specs.CurrentPowerNotch.Driver) { if (p < Train.Specs.MaximumPowerNotch) { // up (not max) Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.MasterControllerUp.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.MasterControllerUp.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } else { // max Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.MasterControllerMax.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.MasterControllerMax.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } } // brake sound if (b < Train.Specs.CurrentBrakeNotch.Driver) { // brake release Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.Brake.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.Brake.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } if (b > 0) { // brake release (not min) buffer = Train.Cars[Train.DriverCar].Sounds.BrakeHandleRelease.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.BrakeHandleRelease.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } else { // brake min buffer = Train.Cars[Train.DriverCar].Sounds.BrakeHandleMin.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.BrakeHandleMin.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } } else if (b > Train.Specs.CurrentBrakeNotch.Driver) { // brake Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.BrakeHandleApply.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.BrakeHandleApply.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } // apply notch if (Train.Specs.SingleHandle) { if (b != 0) p = 0; } Train.Specs.CurrentPowerNotch.Driver = p; Train.Specs.CurrentBrakeNotch.Driver = b; Game.AddBlackBoxEntry(Game.BlackBoxEventToken.None); // plugin if (Train.Plugin != null) { Train.Plugin.UpdatePower(); Train.Plugin.UpdateBrake(); } } // apply air brake handle internal static void ApplyAirBrakeHandle(Train Train, int RelativeDirection) { if (Train.Cars[Train.DriverCar].Specs.BrakeType == CarBrakeType.AutomaticAirBrake) { if (RelativeDirection == -1) { if (Train.Specs.AirBrake.Handle.Driver == AirBrakeHandleState.Service) { ApplyAirBrakeHandle(Train, AirBrakeHandleState.Lap); } else { ApplyAirBrakeHandle(Train, AirBrakeHandleState.Release); } } else if (RelativeDirection == 1) { if (Train.Specs.AirBrake.Handle.Driver == AirBrakeHandleState.Release) { ApplyAirBrakeHandle(Train, AirBrakeHandleState.Lap); } else { ApplyAirBrakeHandle(Train, AirBrakeHandleState.Service); } } Game.AddBlackBoxEntry(Game.BlackBoxEventToken.None); } } internal static void ApplyAirBrakeHandle(Train Train, AirBrakeHandleState State) { if (Train.Cars[Train.DriverCar].Specs.BrakeType == CarBrakeType.AutomaticAirBrake) { if (State != Train.Specs.AirBrake.Handle.Driver) { // sound when moved to service if (State == AirBrakeHandleState.Service) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.Brake.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.Brake.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } // sound if ((int)State < (int)Train.Specs.AirBrake.Handle.Driver) { // brake release if ((int)State > 0) { // brake release (not min) Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.BrakeHandleRelease.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.BrakeHandleRelease.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } else { // brake min Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.BrakeHandleMin.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.BrakeHandleMin.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } } else if ((int)State > (int)Train.Specs.AirBrake.Handle.Driver) { // brake Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.BrakeHandleApply.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.BrakeHandleApply.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } } // apply Train.Specs.AirBrake.Handle.Driver = State; Game.AddBlackBoxEntry(Game.BlackBoxEventToken.None); // plugin if (Train.Plugin != null) { Train.Plugin.UpdatePower(); Train.Plugin.UpdateBrake(); } } } } // update train passengers private static void UpdateTrainPassengers(Train Train, double TimeElapsed) { double accelerationDifference = Train.Specs.CurrentAverageAcceleration - Train.Passengers.CurrentAcceleration; double jerk = 0.25 + 0.10 * Math.Abs(accelerationDifference); double accelerationQuanta = jerk * TimeElapsed; if (Math.Abs(accelerationDifference) < accelerationQuanta) { Train.Passengers.CurrentAcceleration = Train.Specs.CurrentAverageAcceleration; accelerationDifference = 0.0; } else { Train.Passengers.CurrentAcceleration += (double)Math.Sign(accelerationDifference) * accelerationQuanta; accelerationDifference = Train.Specs.CurrentAverageAcceleration - Train.Passengers.CurrentAcceleration; } Train.Passengers.CurrentSpeedDifference += accelerationDifference * TimeElapsed; double acceleration = 0.10 + 0.35 * Math.Abs(Train.Passengers.CurrentSpeedDifference); double speedQuanta = acceleration * TimeElapsed; if (Math.Abs(Train.Passengers.CurrentSpeedDifference) < speedQuanta) { Train.Passengers.CurrentSpeedDifference = 0.0; } else { Train.Passengers.CurrentSpeedDifference -= (double)Math.Sign(Train.Passengers.CurrentSpeedDifference) * speedQuanta; } if (Train.Passengers.PassengerRatio > 0.0) { double threshold = 1.0 / Train.Passengers.PassengerRatio; if (Math.Abs(Train.Passengers.CurrentSpeedDifference) > threshold) { Train.Passengers.FallenOver = true; } else { Train.Passengers.FallenOver = false; } } else { Train.Passengers.FallenOver = false; } } // update speeds private static void UpdateSpeeds(Train Train, double TimeElapsed) { if (Game.MinimalisticSimulation & Train == PlayerTrain) { // hold the position of the player's train during startup for (int i = 0; i < Train.Cars.Length; i++) { Train.Cars[i].Specs.CurrentSpeed = 0.0; Train.Cars[i].Specs.CurrentAccelerationOutput = 0.0; } return; } // update brake system double[] DecelerationDueToBrake, DecelerationDueToMotor; UpdateBrakeSystem(Train, TimeElapsed, out DecelerationDueToBrake, out DecelerationDueToMotor); // calculate new car speeds double[] NewSpeeds = new double[Train.Cars.Length]; for (int i = 0; i < Train.Cars.Length; i++) { double PowerRollingCouplerAcceleration; // rolling on an incline { double a = Train.Cars[i].FrontAxle.Follower.WorldDirection.Y; double b = Train.Cars[i].RearAxle.Follower.WorldDirection.Y; PowerRollingCouplerAcceleration = -0.5 * (a + b) * Game.RouteAccelerationDueToGravity; } // friction double FrictionBrakeAcceleration; { double v = Math.Abs(Train.Cars[i].Specs.CurrentSpeed); double a = GetResistance(Train, i, ref Train.Cars[i].FrontAxle, v); double b = GetResistance(Train, i, ref Train.Cars[i].RearAxle, v); FrictionBrakeAcceleration = 0.5 * (a + b); } // power double wheelspin = 0.0; double wheelSlipAccelerationMotorFront = GetCriticalWheelSlipAccelerationForElectricMotor(Train, i, Train.Cars[i].FrontAxle.Follower.AdhesionMultiplier, Train.Cars[i].FrontAxle.Follower.WorldUp.Y, Train.Cars[i].Specs.CurrentSpeed); double wheelSlipAccelerationMotorRear = GetCriticalWheelSlipAccelerationForElectricMotor(Train, i, Train.Cars[i].RearAxle.Follower.AdhesionMultiplier, Train.Cars[i].RearAxle.Follower.WorldUp.Y, Train.Cars[i].Specs.CurrentSpeed); double wheelSlipAccelerationBrakeFront = GetCriticalWheelSlipAccelerationForFrictionBrake(Train, i, Train.Cars[i].FrontAxle.Follower.AdhesionMultiplier, Train.Cars[i].FrontAxle.Follower.WorldUp.Y, Train.Cars[i].Specs.CurrentSpeed); double wheelSlipAccelerationBrakeRear = GetCriticalWheelSlipAccelerationForFrictionBrake(Train, i, Train.Cars[i].RearAxle.Follower.AdhesionMultiplier, Train.Cars[i].RearAxle.Follower.WorldUp.Y, Train.Cars[i].Specs.CurrentSpeed); if (Train.Cars[i].Derailed) { wheelSlipAccelerationMotorFront = 0.0; wheelSlipAccelerationBrakeFront = 0.0; } if (Train.Cars[i].Derailed) { wheelSlipAccelerationMotorRear = 0.0; wheelSlipAccelerationBrakeRear = 0.0; } if (DecelerationDueToMotor[i] == 0.0) { double a; if (Train.Cars[i].Specs.IsMotorCar) { if (DecelerationDueToMotor[i] == 0.0) { if (Train.Specs.CurrentReverser.Actual != 0 & Train.Specs.CurrentPowerNotch.Actual > 0 & !Train.Specs.CurrentHoldBrake.Actual & !Train.Specs.CurrentEmergencyBrake.Actual) { // target acceleration a = GetAccelerationOutput(Train, i, Train.Specs.CurrentPowerNotch.Actual - 1, (double)Train.Specs.CurrentReverser.Actual * Train.Cars[i].Specs.CurrentSpeed); // readhesion device if (a > Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput) { a = Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput; } // wheel slip if (a < wheelSlipAccelerationMotorFront) { Train.Cars[i].FrontAxle.CurrentWheelSlip = false; } else { Train.Cars[i].FrontAxle.CurrentWheelSlip = true; wheelspin += (double)Train.Specs.CurrentReverser.Actual * a * Train.Cars[i].Specs.MassCurrent; } if (a < wheelSlipAccelerationMotorRear) { Train.Cars[i].RearAxle.CurrentWheelSlip = false; } else { Train.Cars[i].RearAxle.CurrentWheelSlip = true; wheelspin += (double)Train.Specs.CurrentReverser.Actual * a * Train.Cars[i].Specs.MassCurrent; } // readhesion device { if (Game.SecondsSinceMidnight >= Train.Cars[i].Specs.ReAdhesionDevice.NextUpdateTime) { double d = Train.Cars[i].Specs.ReAdhesionDevice.UpdateInterval; double f = Train.Cars[i].Specs.ReAdhesionDevice.ApplicationFactor; double t = Train.Cars[i].Specs.ReAdhesionDevice.ReleaseInterval; double r = Train.Cars[i].Specs.ReAdhesionDevice.ReleaseFactor; Train.Cars[i].Specs.ReAdhesionDevice.NextUpdateTime = Game.SecondsSinceMidnight + d; if (Train.Cars[i].FrontAxle.CurrentWheelSlip | Train.Cars[i].RearAxle.CurrentWheelSlip) { Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput = a * f; Train.Cars[i].Specs.ReAdhesionDevice.TimeStable = 0.0; } else { Train.Cars[i].Specs.ReAdhesionDevice.TimeStable += d; if (Train.Cars[i].Specs.ReAdhesionDevice.TimeStable >= t) { Train.Cars[i].Specs.ReAdhesionDevice.TimeStable -= t; if (r != 0.0 & Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput <= a + 1.0) { if (Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput < 0.025) { Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput = 0.025; } else { Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput *= r; } } else { Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput = double.PositiveInfinity; } } } } } // const speed if (Train.Specs.CurrentConstSpeed) { if (Game.SecondsSinceMidnight >= Train.Cars[i].Specs.ConstSpeed.NextUpdateTime) { Train.Cars[i].Specs.ConstSpeed.NextUpdateTime = Game.SecondsSinceMidnight + Train.Cars[i].Specs.ConstSpeed.UpdateInterval; Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput -= 0.8 * Train.Cars[i].Specs.CurrentAcceleration * (double)Train.Specs.CurrentReverser.Actual; if (Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput < 0.0) Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput = 0.0; } if (a > Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput) a = Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput; if (a < 0.0) a = 0.0; } else { Train.Cars[i].Specs.ConstSpeed.CurrentAccelerationOutput = a; } // finalize if (wheelspin != 0.0) a = 0.0; } else { a = 0.0; Train.Cars[i].FrontAxle.CurrentWheelSlip = false; Train.Cars[i].RearAxle.CurrentWheelSlip = false; } } else { a = 0.0; Train.Cars[i].FrontAxle.CurrentWheelSlip = false; Train.Cars[i].RearAxle.CurrentWheelSlip = false; } } else { a = 0.0; Train.Cars[i].FrontAxle.CurrentWheelSlip = false; Train.Cars[i].RearAxle.CurrentWheelSlip = false; } if (!Train.Cars[i].Derailed) { if (Train.Cars[i].Specs.CurrentAccelerationOutput < a) { if (Train.Cars[i].Specs.CurrentAccelerationOutput < 0.0) { Train.Cars[i].Specs.CurrentAccelerationOutput += Train.Cars[i].Specs.JerkBrakeDown * TimeElapsed; } else { Train.Cars[i].Specs.CurrentAccelerationOutput += Train.Cars[i].Specs.JerkPowerUp * TimeElapsed; } if (Train.Cars[i].Specs.CurrentAccelerationOutput > a) { Train.Cars[i].Specs.CurrentAccelerationOutput = a; } } else { Train.Cars[i].Specs.CurrentAccelerationOutput -= Train.Cars[i].Specs.JerkPowerDown * TimeElapsed; if (Train.Cars[i].Specs.CurrentAccelerationOutput < a) { Train.Cars[i].Specs.CurrentAccelerationOutput = a; } } } else { Train.Cars[i].Specs.CurrentAccelerationOutput = 0.0; } } // brake bool wheellock = false; if (wheelspin == 0.0 & Train.Cars[i].Derailed) wheellock = true; if (!Train.Cars[i].Derailed & wheelspin == 0.0) { double a; // motor if (Train.Cars[i].Specs.IsMotorCar & DecelerationDueToMotor[i] != 0.0) { a = -DecelerationDueToMotor[i]; if (Train.Cars[i].Specs.CurrentAccelerationOutput > a) { if (Train.Cars[i].Specs.CurrentAccelerationOutput > 0.0) { Train.Cars[i].Specs.CurrentAccelerationOutput -= Train.Cars[i].Specs.JerkPowerDown * TimeElapsed; } else { Train.Cars[i].Specs.CurrentAccelerationOutput -= Train.Cars[i].Specs.JerkBrakeUp * TimeElapsed; } if (Train.Cars[i].Specs.CurrentAccelerationOutput < a) { Train.Cars[i].Specs.CurrentAccelerationOutput = a; } } else { Train.Cars[i].Specs.CurrentAccelerationOutput += Train.Cars[i].Specs.JerkBrakeDown * TimeElapsed; if (Train.Cars[i].Specs.CurrentAccelerationOutput > a) { Train.Cars[i].Specs.CurrentAccelerationOutput = a; } } } // brake a = DecelerationDueToBrake[i]; if (Train.Cars[i].Specs.CurrentSpeed >= -0.01 & Train.Cars[i].Specs.CurrentSpeed <= 0.01) { double rf = Train.Cars[i].FrontAxle.Follower.WorldDirection.Y; double rr = Train.Cars[i].RearAxle.Follower.WorldDirection.Y; double ra = Math.Abs(0.5 * (rf + rr) * Game.RouteAccelerationDueToGravity); if (a > ra) a = ra; } double factor = Train.Cars[i].Specs.MassEmpty / Train.Cars[i].Specs.MassCurrent; if (a >= wheelSlipAccelerationBrakeFront) { wheellock = true; } else { FrictionBrakeAcceleration += 0.5 * a * factor; } if (a >= wheelSlipAccelerationBrakeRear) { wheellock = true; } else { FrictionBrakeAcceleration += 0.5 * a * factor; } } else if (Train.Cars[i].Derailed) { FrictionBrakeAcceleration += Game.CoefficientOfGroundFriction * Game.RouteAccelerationDueToGravity; } // motor if (Train.Specs.CurrentReverser.Actual != 0) { double factor = Train.Cars[i].Specs.MassEmpty / Train.Cars[i].Specs.MassCurrent; if (Train.Cars[i].Specs.CurrentAccelerationOutput > 0.0) { PowerRollingCouplerAcceleration += (double)Train.Specs.CurrentReverser.Actual * Train.Cars[i].Specs.CurrentAccelerationOutput * factor; } else { double a = -Train.Cars[i].Specs.CurrentAccelerationOutput; if (a >= wheelSlipAccelerationMotorFront) { Train.Cars[i].FrontAxle.CurrentWheelSlip = true; } else if (!Train.Cars[i].Derailed) { FrictionBrakeAcceleration += 0.5 * a * factor; } if (a >= wheelSlipAccelerationMotorRear) { Train.Cars[i].RearAxle.CurrentWheelSlip = true; } else { FrictionBrakeAcceleration += 0.5 * a * factor; } } } else { Train.Cars[i].Specs.CurrentAccelerationOutput = 0.0; } // perceived speed { double target; if (wheellock) { target = 0.0; } else if (wheelspin == 0.0) { target = Train.Cars[i].Specs.CurrentSpeed; } else { target = Train.Cars[i].Specs.CurrentSpeed + wheelspin / 2500.0; } double diff = target - Train.Cars[i].Specs.CurrentPerceivedSpeed; double rate = (diff < 0.0 ? 5.0 : 1.0) * Game.RouteAccelerationDueToGravity * TimeElapsed; rate *= 1.0 - 0.7 / (diff * diff + 1.0); double factor = rate * rate; factor = 1.0 - factor / (factor + 1000.0); rate *= factor; if (diff >= -rate & diff <= rate) { Train.Cars[i].Specs.CurrentPerceivedSpeed = target; } else { Train.Cars[i].Specs.CurrentPerceivedSpeed += rate * (double)Math.Sign(diff); } } // perceived traveled distance Train.Cars[i].Specs.CurrentPerceivedTraveledDistance += Math.Abs(Train.Cars[i].Specs.CurrentPerceivedSpeed) * TimeElapsed; // calculate new speed { int d = Math.Sign(Train.Cars[i].Specs.CurrentSpeed); double a = PowerRollingCouplerAcceleration; double b = FrictionBrakeAcceleration; if (Math.Abs(a) < b) { if (Math.Sign(a) == d) { if (d == 0) { NewSpeeds[i] = 0.0; } else { double c = (b - Math.Abs(a)) * TimeElapsed; if (Math.Abs(Train.Cars[i].Specs.CurrentSpeed) > c) { NewSpeeds[i] = Train.Cars[i].Specs.CurrentSpeed - (double)d * c; } else { NewSpeeds[i] = 0.0; } } } else { double c = (Math.Abs(a) + b) * TimeElapsed; if (Math.Abs(Train.Cars[i].Specs.CurrentSpeed) > c) { NewSpeeds[i] = Train.Cars[i].Specs.CurrentSpeed - (double)d * c; } else { NewSpeeds[i] = 0.0; } } } else { NewSpeeds[i] = Train.Cars[i].Specs.CurrentSpeed + (a - b * (double)d) * TimeElapsed; } } } // calculate center of mass position double[] CenterOfCarPositions = new double[Train.Cars.Length]; double CenterOfMassPosition = 0.0; double TrainMass = 0.0; for (int i = 0; i < Train.Cars.Length; i++) { double pr = Train.Cars[i].RearAxle.Follower.TrackPosition - Train.Cars[i].RearAxlePosition; double pf = Train.Cars[i].FrontAxle.Follower.TrackPosition - Train.Cars[i].FrontAxlePosition; CenterOfCarPositions[i] = 0.5 * (pr + pf); CenterOfMassPosition += CenterOfCarPositions[i] * Train.Cars[i].Specs.MassCurrent; TrainMass += Train.Cars[i].Specs.MassCurrent; } if (TrainMass != 0.0) { CenterOfMassPosition /= TrainMass; } { // coupler // determine closest cars int p = -1; // primary car index int s = -1; // secondary car index { double PrimaryDistance = double.MaxValue; for (int i = 0; i < Train.Cars.Length; i++) { double d = Math.Abs(CenterOfCarPositions[i] - CenterOfMassPosition); if (d < PrimaryDistance) { PrimaryDistance = d; p = i; } } double SecondDistance = double.MaxValue; for (int i = p - 1; i <= p + 1; i++) { if (i >= 0 & i < Train.Cars.Length & i != p) { double d = Math.Abs(CenterOfCarPositions[i] - CenterOfMassPosition); if (d < SecondDistance) { SecondDistance = d; s = i; } } } if (s >= 0 && PrimaryDistance <= 0.25 * (PrimaryDistance + SecondDistance)) { s = -1; } } // coupler bool[] CouplerCollision = new bool[Train.Couplers.Length]; int cf, cr; if (s >= 0) { // use two cars as center of mass if (p > s) { int t = p; p = s; s = t; } double min = Train.Couplers[p].MinimumDistanceBetweenCars; double max = Train.Couplers[p].MaximumDistanceBetweenCars; double d = CenterOfCarPositions[p] - CenterOfCarPositions[s] - 0.5 * (Train.Cars[p].Length + Train.Cars[s].Length); if (d < min) { double t = (min - d) / (Train.Cars[p].Specs.MassCurrent + Train.Cars[s].Specs.MassCurrent); double tp = t * Train.Cars[s].Specs.MassCurrent; double ts = t * Train.Cars[p].Specs.MassCurrent; TrackManager.UpdateTrackFollower(ref Train.Cars[p].FrontAxle.Follower, Train.Cars[p].FrontAxle.Follower.TrackPosition + tp, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[p].RearAxle.Follower, Train.Cars[p].RearAxle.Follower.TrackPosition + tp, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[s].FrontAxle.Follower, Train.Cars[s].FrontAxle.Follower.TrackPosition - ts, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[s].RearAxle.Follower, Train.Cars[s].RearAxle.Follower.TrackPosition - ts, false, false); CenterOfCarPositions[p] += tp; CenterOfCarPositions[s] -= ts; CouplerCollision[p] = true; } else if (d > max & !Train.Cars[p].Derailed & !Train.Cars[s].Derailed) { double t = (d - max) / (Train.Cars[p].Specs.MassCurrent + Train.Cars[s].Specs.MassCurrent); double tp = t * Train.Cars[s].Specs.MassCurrent; double ts = t * Train.Cars[p].Specs.MassCurrent; TrackManager.UpdateTrackFollower(ref Train.Cars[p].FrontAxle.Follower, Train.Cars[p].FrontAxle.Follower.TrackPosition - tp, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[p].RearAxle.Follower, Train.Cars[p].RearAxle.Follower.TrackPosition - tp, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[s].FrontAxle.Follower, Train.Cars[s].FrontAxle.Follower.TrackPosition + ts, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[s].RearAxle.Follower, Train.Cars[s].RearAxle.Follower.TrackPosition + ts, false, false); CenterOfCarPositions[p] -= tp; CenterOfCarPositions[s] += ts; CouplerCollision[p] = true; } cf = p; cr = s; } else { // use one car as center of mass cf = p; cr = p; } // front cars for (int i = cf - 1; i >= 0; i--) { double min = Train.Couplers[i].MinimumDistanceBetweenCars; double max = Train.Couplers[i].MaximumDistanceBetweenCars; double d = CenterOfCarPositions[i] - CenterOfCarPositions[i + 1] - 0.5 * (Train.Cars[i].Length + Train.Cars[i + 1].Length); if (d < min) { double t = min - d + 0.0001; TrackManager.UpdateTrackFollower(ref Train.Cars[i].FrontAxle.Follower, Train.Cars[i].FrontAxle.Follower.TrackPosition + t, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[i].RearAxle.Follower, Train.Cars[i].RearAxle.Follower.TrackPosition + t, false, false); CenterOfCarPositions[i] += t; CouplerCollision[i] = true; } else if (d > max & !Train.Cars[i].Derailed & !Train.Cars[i + 1].Derailed) { double t = d - max + 0.0001; TrackManager.UpdateTrackFollower(ref Train.Cars[i].FrontAxle.Follower, Train.Cars[i].FrontAxle.Follower.TrackPosition - t, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[i].RearAxle.Follower, Train.Cars[i].RearAxle.Follower.TrackPosition - t, false, false); CenterOfCarPositions[i] -= t; CouplerCollision[i] = true; } } // rear cars for (int i = cr + 1; i < Train.Cars.Length; i++) { double min = Train.Couplers[i - 1].MinimumDistanceBetweenCars; double max = Train.Couplers[i - 1].MaximumDistanceBetweenCars; double d = CenterOfCarPositions[i - 1] - CenterOfCarPositions[i] - 0.5 * (Train.Cars[i].Length + Train.Cars[i - 1].Length); if (d < min) { double t = min - d + 0.0001; TrackManager.UpdateTrackFollower(ref Train.Cars[i].FrontAxle.Follower, Train.Cars[i].FrontAxle.Follower.TrackPosition - t, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[i].RearAxle.Follower, Train.Cars[i].RearAxle.Follower.TrackPosition - t, false, false); CenterOfCarPositions[i] -= t; CouplerCollision[i - 1] = true; } else if (d > max & !Train.Cars[i].Derailed & !Train.Cars[i - 1].Derailed) { double t = d - max + 0.0001; TrackManager.UpdateTrackFollower(ref Train.Cars[i].FrontAxle.Follower, Train.Cars[i].FrontAxle.Follower.TrackPosition + t, false, false); TrackManager.UpdateTrackFollower(ref Train.Cars[i].RearAxle.Follower, Train.Cars[i].RearAxle.Follower.TrackPosition + t, false, false); CenterOfCarPositions[i] += t; CouplerCollision[i - 1] = true; } } // update speeds for (int i = 0; i < Train.Couplers.Length; i++) { if (CouplerCollision[i]) { int j; for (j = i + 1; j < Train.Couplers.Length; j++) { if (!CouplerCollision[j]) { break; } } double v = 0.0; double m = 0.0; for (int k = i; k <= j; k++) { v += NewSpeeds[k] * Train.Cars[k].Specs.MassCurrent; m += Train.Cars[k].Specs.MassCurrent; } if (m != 0.0) { v /= m; } for (int k = i; k <= j; k++) { if (Interface.CurrentOptions.Derailments && Math.Abs(v - NewSpeeds[k]) > 0.5 * Game.CriticalCollisionSpeedDifference) { Train.Cars[k].Derailed = true; } NewSpeeds[k] = v; } i = j - 1; } } } // update average data Train.Specs.CurrentAverageSpeed = 0.0; Train.Specs.CurrentAverageAcceleration = 0.0; Train.Specs.CurrentAverageJerk = 0.0; double invtime = TimeElapsed != 0.0 ? 1.0 / TimeElapsed : 1.0; for (int i = 0; i < Train.Cars.Length; i++) { Train.Cars[i].Specs.CurrentAcceleration = (NewSpeeds[i] - Train.Cars[i].Specs.CurrentSpeed) * invtime; Train.Cars[i].Specs.CurrentSpeed = NewSpeeds[i]; Train.Specs.CurrentAverageSpeed += NewSpeeds[i]; Train.Specs.CurrentAverageAcceleration += Train.Cars[i].Specs.CurrentAcceleration; } double invcarlen = 1.0 / (double)Train.Cars.Length; Train.Specs.CurrentAverageSpeed *= invcarlen; Train.Specs.CurrentAverageAcceleration *= invcarlen; } // update train mass private static void UpdateTrainMassFromPassengerRatio(Train Train) { for (int i = 0; i < Train.Cars.Length; i++) { double area = Train.Cars[i].Width * Train.Cars[i].Length; const double passengersPerArea = 1.0; double randomFactor = 0.9 + 0.2 * Program.RandomNumberGenerator.NextDouble(); double passengers = Math.Round(randomFactor * Train.Passengers.PassengerRatio * passengersPerArea * area); const double massPerPassenger = 70.0; double passengerMass = passengers * massPerPassenger; Train.Cars[i].Specs.MassCurrent = Train.Cars[i].Specs.MassEmpty + passengerMass; } } // un-derail train internal static void UnderailTrains() { for (int i = 0; i < Trains.Length; i++) { UnderailTrain(Trains[i]); } } internal static void UnderailTrain(Train Train) { for (int i = 0; i < Train.Cars.Length; i++) { Train.Cars[i].Specs.CurrentRollDueToTopplingAngle = 0.0; Train.Cars[i].Derailed = false; } } // jump train internal static void JumpTrain(Train train, int stationIndex) { int stopIndex = Game.GetStopIndex(stationIndex, train.Cars.Length); if (stopIndex >= 0) { if (train == PlayerTrain) { if (train.Plugin != null) { train.Plugin.BeginJump((OpenBveApi.Runtime.InitializationModes)Game.TrainStart); } } for (int h = 0; h < train.Cars.Length; h++) { train.Cars[h].Specs.CurrentSpeed = 0.0; } double d = Game.Stations[stationIndex].Stops[stopIndex].TrackPosition - train.Cars[0].FrontAxle.Follower.TrackPosition + train.Cars[0].FrontAxlePosition - 0.5 * train.Cars[0].Length; if (train == PlayerTrain) { TrackManager.SuppressSoundEvents = true; } while (d != 0.0) { double x; if (Math.Abs(d) > 1.0) { x = (double)Math.Sign(d); } else { x = d; } for (int h = 0; h < train.Cars.Length; h++) { TrainManager.MoveCar(train, h, x, 0.0); } if (Math.Abs(d) >= 1.0) { d -= x; } else { break; } } if (train == PlayerTrain) { TrainManager.UnderailTrains(); TrackManager.SuppressSoundEvents = false; } if (train.Specs.CurrentEmergencyBrake.Driver) { TrainManager.ApplyNotch(train, 0, false, 0, true); } else { TrainManager.ApplyNotch(train, 0, false, train.Specs.MaximumBrakeNotch, false); TrainManager.ApplyAirBrakeHandle(train, TrainManager.AirBrakeHandleState.Service); } if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } if (train == PlayerTrain) { if (Game.CurrentScore.ArrivalStation <= stationIndex) { Game.CurrentScore.ArrivalStation = stationIndex + 1; } } if (train == PlayerTrain) { if (Game.Stations[stationIndex].ArrivalTime >= 0.0) { Game.SecondsSinceMidnight = Game.Stations[stationIndex].ArrivalTime; } else if (Game.Stations[stationIndex].DepartureTime >= 0.0) { Game.SecondsSinceMidnight = Game.Stations[stationIndex].DepartureTime - Game.Stations[stationIndex].StopTime; } } for (int i = 0; i < train.Cars.Length; i++) { train.Cars[i].Specs.AnticipatedLeftDoorsOpened = Game.Stations[stationIndex].OpenLeftDoors; train.Cars[i].Specs.AnticipatedRightDoorsOpened = Game.Stations[stationIndex].OpenRightDoors; // for (int j = 0; j < train.Cars[i].Specs.Doors.Length; j++) { // if (train.Cars[i].Specs.Doors[j].Direction == -1) { // train.Cars[i].Specs.Doors[j].State = Game.Stations[stationIndex].OpenLeftDoors ? 1.0 : 0.0; // } else if (train.Cars[i].Specs.Doors[j].Direction == 1) { // train.Cars[i].Specs.Doors[j].State = Game.Stations[stationIndex].OpenRightDoors ? 1.0 : 0.0; // } // } } if (train == PlayerTrain) { Game.CurrentScore.DepartureStation = stationIndex; Game.CurrentInterface = Game.InterfaceType.Normal; Game.Messages = new Game.Message[] { }; } ObjectManager.UpdateAnimatedWorldObjects(0.0, true); TrainManager.UpdateTrainObjects(0.0, true); if (train == PlayerTrain) { if (train.Plugin != null) { train.Plugin.EndJump(); } } } } // update train physics and controls private static void UpdateTrainPhysicsAndControls(Train Train, double TimeElapsed) { if (TimeElapsed == 0.0) { return; } // move cars for (int i = 0; i < Train.Cars.Length; i++) { MoveCar(Train, i, Train.Cars[i].Specs.CurrentSpeed * TimeElapsed, TimeElapsed); if (Train.State == TrainState.Disposed) { return; } } // update station and doors UpdateTrainStation(Train, TimeElapsed); UpdateTrainDoors(Train, TimeElapsed); // delayed handles { // power notch if (Train.Specs.CurrentPowerNotch.DelayedChanges.Length == 0) { if (Train.Specs.CurrentPowerNotch.Safety < Train.Specs.CurrentPowerNotch.Actual) { if (Train.Specs.PowerNotchReduceSteps <= 1) { Train.Specs.CurrentPowerNotch.AddChange(Train, Train.Specs.CurrentPowerNotch.Actual - 1, Train.Specs.DelayPowerDown); } else if (Train.Specs.CurrentPowerNotch.Safety + Train.Specs.PowerNotchReduceSteps <= Train.Specs.CurrentPowerNotch.Actual | Train.Specs.CurrentPowerNotch.Safety == 0) { Train.Specs.CurrentPowerNotch.AddChange(Train, Train.Specs.CurrentPowerNotch.Safety, Train.Specs.DelayPowerDown); } } else if (Train.Specs.CurrentPowerNotch.Safety > Train.Specs.CurrentPowerNotch.Actual) { Train.Specs.CurrentPowerNotch.AddChange(Train, Train.Specs.CurrentPowerNotch.Actual + 1, Train.Specs.DelayPowerUp); } } else { int m = Train.Specs.CurrentPowerNotch.DelayedChanges.Length - 1; if (Train.Specs.CurrentPowerNotch.Safety < Train.Specs.CurrentPowerNotch.DelayedChanges[m].Value) { Train.Specs.CurrentPowerNotch.AddChange(Train, Train.Specs.CurrentPowerNotch.Safety, Train.Specs.DelayPowerDown); } else if (Train.Specs.CurrentPowerNotch.Safety > Train.Specs.CurrentPowerNotch.DelayedChanges[m].Value) { Train.Specs.CurrentPowerNotch.AddChange(Train, Train.Specs.CurrentPowerNotch.Safety, Train.Specs.DelayPowerUp); } } if (Train.Specs.CurrentPowerNotch.DelayedChanges.Length >= 1) { if (Train.Specs.CurrentPowerNotch.DelayedChanges[0].Time <= Game.SecondsSinceMidnight) { Train.Specs.CurrentPowerNotch.Actual = Train.Specs.CurrentPowerNotch.DelayedChanges[0].Value; Train.Specs.CurrentPowerNotch.RemoveChanges(1); } } } { // brake notch int sec = Train.Specs.CurrentEmergencyBrake.Safety ? Train.Specs.MaximumBrakeNotch : Train.Specs.CurrentBrakeNotch.Safety; if (Train.Specs.CurrentBrakeNotch.DelayedChanges.Length == 0) { if (sec < Train.Specs.CurrentBrakeNotch.Actual) { Train.Specs.CurrentBrakeNotch.AddChange(Train, Train.Specs.CurrentBrakeNotch.Actual - 1, Train.Specs.DelayBrakeDown); } else if (sec > Train.Specs.CurrentBrakeNotch.Actual) { Train.Specs.CurrentBrakeNotch.AddChange(Train, Train.Specs.CurrentBrakeNotch.Actual + 1, Train.Specs.DelayBrakeUp); } } else { int m = Train.Specs.CurrentBrakeNotch.DelayedChanges.Length - 1; if (sec < Train.Specs.CurrentBrakeNotch.DelayedChanges[m].Value) { Train.Specs.CurrentBrakeNotch.AddChange(Train, sec, Train.Specs.DelayBrakeDown); } else if (sec > Train.Specs.CurrentBrakeNotch.DelayedChanges[m].Value) { Train.Specs.CurrentBrakeNotch.AddChange(Train, sec, Train.Specs.DelayBrakeUp); } } if (Train.Specs.CurrentBrakeNotch.DelayedChanges.Length >= 1) { if (Train.Specs.CurrentBrakeNotch.DelayedChanges[0].Time <= Game.SecondsSinceMidnight) { Train.Specs.CurrentBrakeNotch.Actual = Train.Specs.CurrentBrakeNotch.DelayedChanges[0].Value; Train.Specs.CurrentBrakeNotch.RemoveChanges(1); } } } { // air brake handle if (Train.Specs.AirBrake.Handle.DelayedValue != AirBrakeHandleState.Invalid) { if (Train.Specs.AirBrake.Handle.DelayedTime <= Game.SecondsSinceMidnight) { Train.Specs.AirBrake.Handle.Actual = Train.Specs.AirBrake.Handle.DelayedValue; Train.Specs.AirBrake.Handle.DelayedValue = AirBrakeHandleState.Invalid; } } else { if (Train.Specs.AirBrake.Handle.Safety == AirBrakeHandleState.Release & Train.Specs.AirBrake.Handle.Actual != AirBrakeHandleState.Release) { Train.Specs.AirBrake.Handle.DelayedValue = AirBrakeHandleState.Release; Train.Specs.AirBrake.Handle.DelayedTime = Game.SecondsSinceMidnight; } else if (Train.Specs.AirBrake.Handle.Safety == AirBrakeHandleState.Service & Train.Specs.AirBrake.Handle.Actual != AirBrakeHandleState.Service) { Train.Specs.AirBrake.Handle.DelayedValue = AirBrakeHandleState.Service; Train.Specs.AirBrake.Handle.DelayedTime = Game.SecondsSinceMidnight; } else if (Train.Specs.AirBrake.Handle.Safety == AirBrakeHandleState.Lap) { Train.Specs.AirBrake.Handle.Actual = AirBrakeHandleState.Lap; } } } { // emergency brake if (Train.Specs.CurrentEmergencyBrake.Safety & !Train.Specs.CurrentEmergencyBrake.Actual) { double t = Game.SecondsSinceMidnight; if (t < Train.Specs.CurrentEmergencyBrake.ApplicationTime) Train.Specs.CurrentEmergencyBrake.ApplicationTime = t; if (Train.Specs.CurrentEmergencyBrake.ApplicationTime <= Game.SecondsSinceMidnight) { Train.Specs.CurrentEmergencyBrake.Actual = true; Train.Specs.CurrentEmergencyBrake.ApplicationTime = double.MaxValue; } } else if (!Train.Specs.CurrentEmergencyBrake.Safety) { Train.Specs.CurrentEmergencyBrake.Actual = false; } } Train.Specs.CurrentHoldBrake.Actual = Train.Specs.CurrentHoldBrake.Driver; // update speeds UpdateSpeeds(Train, TimeElapsed); // run sound for (int i = 0; i < Train.Cars.Length; i++) { const double factor = 0.04; // 90 km/h -> m/s -> 1/x double speed = Math.Abs(Train.Cars[i].Specs.CurrentSpeed); if (Train.Cars[i].Derailed) { speed = 0.0; } double pitch = speed * factor; double basegain; if (Train.Cars[i].Specs.CurrentSpeed == 0.0) { if (i != 0) { Train.Cars[i].Sounds.RunNextReasynchronizationPosition = Train.Cars[0].FrontAxle.Follower.TrackPosition; } } else if (Train.Cars[i].Sounds.RunNextReasynchronizationPosition == double.MaxValue & Train.Cars[i].Sounds.FrontAxleRunIndex >= 0) { double distance = Math.Abs(Train.Cars[i].FrontAxle.Follower.TrackPosition - World.CameraTrackFollower.TrackPosition); const double minDistance = 150.0; const double maxDistance = 750.0; if (distance > minDistance) { if (Train.Cars[i].Sounds.FrontAxleRunIndex < Train.Cars[i].Sounds.Run.Length) { Sounds.SoundBuffer buffer = Train.Cars[i].Sounds.Run[Train.Cars[i].Sounds.FrontAxleRunIndex].Buffer; if (buffer != null) { double duration = Sounds.GetDuration(buffer); if (duration > 0.0) { double offset = distance > maxDistance ? 25.0 : 300.0; Train.Cars[i].Sounds.RunNextReasynchronizationPosition = duration * Math.Ceiling((Train.Cars[0].FrontAxle.Follower.TrackPosition + offset) / duration); } } } } } if (Train.Cars[i].FrontAxle.Follower.TrackPosition >= Train.Cars[i].Sounds.RunNextReasynchronizationPosition) { Train.Cars[i].Sounds.RunNextReasynchronizationPosition = double.MaxValue; basegain = 0.0; } else { basegain = speed < 2.77777777777778 ? 0.36 * speed : 1.0; } for (int j = 0; j < Train.Cars[i].Sounds.Run.Length; j++) { if (j == Train.Cars[i].Sounds.FrontAxleRunIndex | j == Train.Cars[i].Sounds.RearAxleRunIndex) { Train.Cars[i].Sounds.RunVolume[j] += 3.0 * TimeElapsed; if (Train.Cars[i].Sounds.RunVolume[j] > 1.0) Train.Cars[i].Sounds.RunVolume[j] = 1.0; } else { Train.Cars[i].Sounds.RunVolume[j] -= 3.0 * TimeElapsed; if (Train.Cars[i].Sounds.RunVolume[j] < 0.0) Train.Cars[i].Sounds.RunVolume[j] = 0.0; } double gain = basegain * Train.Cars[i].Sounds.RunVolume[j]; if (Sounds.IsPlaying(Train.Cars[i].Sounds.Run[j].Source)) { if (pitch > 0.01 & gain > 0.001) { Train.Cars[i].Sounds.Run[j].Source.Pitch = pitch; Train.Cars[i].Sounds.Run[j].Source.Volume = gain; } else { Sounds.StopSound(Train.Cars[i].Sounds.Run[j].Source); } } else if (pitch > 0.02 & gain > 0.01) { Sounds.SoundBuffer buffer = Train.Cars[i].Sounds.Run[j].Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Sounds.Run[j].Position; Train.Cars[i].Sounds.Run[j].Source = Sounds.PlaySound(buffer, pitch, gain, pos, Train, i, true); } } } } // motor sound for (int i = 0; i < Train.Cars.Length; i++) { if (Train.Cars[i].Specs.IsMotorCar) { OpenBveApi.Math.Vector3 pos = Train.Cars[i].Sounds.Motor.Position; double speed = Math.Abs(Train.Cars[i].Specs.CurrentPerceivedSpeed); int idx = (int)Math.Round(speed * Train.Cars[i].Sounds.Motor.SpeedConversionFactor); int odir = Train.Cars[i].Sounds.Motor.CurrentAccelerationDirection; int ndir = Math.Sign(Train.Cars[i].Specs.CurrentAccelerationOutput); for (int h = 0; h < 2; h++) { int j = h == 0 ? TrainManager.MotorSound.MotorP1 : TrainManager.MotorSound.MotorP2; int k = h == 0 ? TrainManager.MotorSound.MotorB1 : TrainManager.MotorSound.MotorB2; if (odir > 0 & ndir <= 0) { if (j < Train.Cars[i].Sounds.Motor.Tables.Length) { Sounds.StopSound(Train.Cars[i].Sounds.Motor.Tables[j].Source); Train.Cars[i].Sounds.Motor.Tables[j].Source = null; Train.Cars[i].Sounds.Motor.Tables[j].Buffer = null; } } else if (odir < 0 & ndir >= 0) { if (k < Train.Cars[i].Sounds.Motor.Tables.Length) { Sounds.StopSound(Train.Cars[i].Sounds.Motor.Tables[k].Source); Train.Cars[i].Sounds.Motor.Tables[k].Source = null; Train.Cars[i].Sounds.Motor.Tables[k].Buffer = null; } } if (ndir != 0) { if (ndir < 0) j = k; if (j < Train.Cars[i].Sounds.Motor.Tables.Length) { int idx2 = idx; if (idx2 >= Train.Cars[i].Sounds.Motor.Tables[j].Entries.Length) { idx2 = Train.Cars[i].Sounds.Motor.Tables[j].Entries.Length - 1; } if (idx2 >= 0) { Sounds.SoundBuffer obuf = Train.Cars[i].Sounds.Motor.Tables[j].Buffer; Sounds.SoundBuffer nbuf = Train.Cars[i].Sounds.Motor.Tables[j].Entries[idx2].Buffer; double pitch = Train.Cars[i].Sounds.Motor.Tables[j].Entries[idx2].Pitch; double gain = Train.Cars[i].Sounds.Motor.Tables[j].Entries[idx2].Gain; if (ndir == 1) { /// power double max = Train.Cars[i].Specs.AccelerationCurveMaximum; if (max != 0.0) { double cur = Train.Cars[i].Specs.CurrentAccelerationOutput; if (cur < 0.0) cur = 0.0; gain *= Math.Pow(cur / max, 0.25); } } else if (ndir == -1) { /// brake double max = Train.Cars[i].Specs.BrakeDecelerationAtServiceMaximumPressure; if (max != 0.0) { double cur = -Train.Cars[i].Specs.CurrentAccelerationOutput; if (cur < 0.0) cur = 0.0; gain *= Math.Pow(cur / max, 0.25); } } if (obuf != nbuf) { Sounds.StopSound(Train.Cars[i].Sounds.Motor.Tables[j].Source); if (nbuf != null) { Train.Cars[i].Sounds.Motor.Tables[j].Source = Sounds.PlaySound(nbuf, pitch, gain, pos, Train, i, true); Train.Cars[i].Sounds.Motor.Tables[j].Buffer = nbuf; } else { Train.Cars[i].Sounds.Motor.Tables[j].Source = null; Train.Cars[i].Sounds.Motor.Tables[j].Buffer = null; } } else if (nbuf != null) { if (Train.Cars[i].Sounds.Motor.Tables[j].Source != null) { Train.Cars[i].Sounds.Motor.Tables[j].Source.Pitch = pitch; Train.Cars[i].Sounds.Motor.Tables[j].Source.Volume = gain; } } else { Sounds.StopSound(Train.Cars[i].Sounds.Motor.Tables[j].Source); Train.Cars[i].Sounds.Motor.Tables[j].Source = null; Train.Cars[i].Sounds.Motor.Tables[j].Buffer = null; } } else { Sounds.StopSound(Train.Cars[i].Sounds.Motor.Tables[j].Source); Train.Cars[i].Sounds.Motor.Tables[j].Source = null; Train.Cars[i].Sounds.Motor.Tables[j].Buffer = null; } } } } Train.Cars[i].Sounds.Motor.CurrentAccelerationDirection = ndir; } } // safety system if (!Game.MinimalisticSimulation | Train != PlayerTrain) { UpdateSafetySystem(Train, TimeElapsed); } { // breaker sound bool breaker; if (Train.Cars[Train.DriverCar].Specs.BrakeType == CarBrakeType.AutomaticAirBrake) { breaker = Train.Specs.CurrentReverser.Actual != 0 & Train.Specs.CurrentPowerNotch.Safety >= 1 & Train.Specs.AirBrake.Handle.Safety == AirBrakeHandleState.Release & !Train.Specs.CurrentEmergencyBrake.Safety & !Train.Specs.CurrentHoldBrake.Actual; } else { breaker = Train.Specs.CurrentReverser.Actual != 0 & Train.Specs.CurrentPowerNotch.Safety >= 1 & Train.Specs.CurrentBrakeNotch.Safety == 0 & !Train.Specs.CurrentEmergencyBrake.Safety & !Train.Specs.CurrentHoldBrake.Actual; } if (breaker & !Train.Cars[Train.DriverCar].Sounds.BreakerResumed) { // resume if (Train.Cars[Train.DriverCar].Sounds.BreakerResume.Buffer != null) { Sounds.PlaySound(Train.Cars[Train.DriverCar].Sounds.BreakerResume.Buffer, 1.0, 1.0, Train.Cars[Train.DriverCar].Sounds.BreakerResume.Position, Train, Train.DriverCar, false); } if (Train.Cars[Train.DriverCar].Sounds.BreakerResumeOrInterrupt.Buffer != null) { Sounds.PlaySound(Train.Cars[Train.DriverCar].Sounds.BreakerResumeOrInterrupt.Buffer, 1.0, 1.0, Train.Cars[Train.DriverCar].Sounds.BreakerResumeOrInterrupt.Position, Train, Train.DriverCar, false); } Train.Cars[Train.DriverCar].Sounds.BreakerResumed = true; } else if (!breaker & Train.Cars[Train.DriverCar].Sounds.BreakerResumed) { // interrupt if (Train.Cars[Train.DriverCar].Sounds.BreakerResumeOrInterrupt.Buffer != null) { Sounds.PlaySound(Train.Cars[Train.DriverCar].Sounds.BreakerResumeOrInterrupt.Buffer, 1.0, 1.0, Train.Cars[Train.DriverCar].Sounds.BreakerResumeOrInterrupt.Position, Train, Train.DriverCar, false); } Train.Cars[Train.DriverCar].Sounds.BreakerResumed = false; } } // passengers UpdateTrainPassengers(Train, TimeElapsed); // signals if (Train.CurrentSectionLimit == 0.0) { if (Train.Specs.CurrentEmergencyBrake.Driver & Train.Specs.CurrentAverageSpeed > -0.03 & Train.Specs.CurrentAverageSpeed < 0.03) { Train.CurrentSectionLimit = 6.94444444444444; if (Train == PlayerTrain) { string s = Interface.GetInterfaceString("message_signal_proceed"); double a = 3.6 * Train.CurrentSectionLimit; s = s.Replace("[speed]", a.ToString("0", System.Globalization.CultureInfo.InvariantCulture)); Game.AddMessage(s, Game.MessageDependency.None, Interface.GameMode.Normal, Game.MessageColor.Red, Game.SecondsSinceMidnight + 5.0); } } } // infrequent updates Train.InternalTimerTimeElapsed += TimeElapsed; if (Train.InternalTimerTimeElapsed > 10.0) { Train.InternalTimerTimeElapsed -= 10.0; SynchronizeTrain(Train); UpdateAtmosphericConstants(Train); } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/World.cs000066400000000000000000001653321171674032100211700ustar00rootroot00000000000000#pragma warning disable 0660 // Defines == or != but does not override Object.Equals #pragma warning disable 0661 // Defines == or != but does not override Object.GetHashCode using System; using OpenBveApi.Colors; using OpenBveApi.Math; namespace OpenBve { internal static class World { // vectors /// Represents a 2D vector of System.Double coordinates. /// This structure is outdated. Use OpenBveApi.Math.Vector2 instead. internal struct Vector2D { internal double X; internal double Y; internal Vector2D(double X, double Y) { this.X = X; this.Y = Y; } } /// Represents a 2D vector of System.Single coordinates. internal struct Vector2Df { internal float X; internal float Y; internal Vector2Df(float X, float Y) { this.X = X; this.Y = Y; } } // /// Represents a 3D vector of System.Double coordinates. // /// This structure is outdated. Use OpenBveApi.Math.Vector3 instead. // internal struct Vector3D { // internal double X; // internal double Y; // internal double Z; // internal Vector3(double X, double Y, double Z) { // this.X = X; // this.Y = Y; // this.Z = Z; // } // /// Returns a normalized vector based on a 2D vector in the XZ plane and an additional Y-coordinate. // /// The vector in the XZ-plane. The X and Y components in Vector represent the X- and Z-coordinates, respectively. // /// The Y-coordinate. // internal Vector3(Vector2D Vector, double Y) { // double t = 1.0 / Math.Sqrt(Vector.X * Vector.X + Vector.Y * Vector.Y + Y * Y); // this.X = t * Vector.X; // this.Y = t * Y; // this.Z = t * Vector.Y; // } // /// Returns the sum of two vectors. // internal static Vector3D Add(Vector3D A, Vector3D B) { // return new Vector3(A.X + B.X, A.Y + B.Y, A.Z + B.Z); // } // /// Returns the difference of two vectors. // internal static Vector3D Subtract(Vector3D A, Vector3D B) { // return new Vector3(A.X - B.X, A.Y - B.Y, A.Z - B.Z); // } // /// Gets a matching OpenBveApi.Math.Vector3 structure. // internal OpenBveApi.Math.Vector3 GetAPIStructure() { // return new OpenBveApi.Math.Vector3(this.X, this.Y, this.Z); // } // } /// Returns a normalized vector based on a 2D vector in the XZ plane and an additional Y-coordinate. /// The vector in the XZ-plane. The X and Y components in Vector represent the X- and Z-coordinates, respectively. /// The Y-coordinate. internal static Vector3 GetVector3(Vector2D Vector, double Y) { double t = 1.0 / Math.Sqrt(Vector.X * Vector.X + Vector.Y * Vector.Y + Y * Y); return new Vector3(t * Vector.X, t * Y, t * Vector.Y); } /// Represents a 3D vector of System.Single coordinates. internal struct Vector3Df { internal float X; internal float Y; internal float Z; internal Vector3Df(float X, float Y, float Z) { this.X = X; this.Y = Y; this.Z = Z; } internal bool IsZero() { if (this.X != 0.0f) return false; if (this.Y != 0.0f) return false; if (this.Z != 0.0f) return false; return true; } } // vertices /// Represents a vertex consisting of 3D coordinates and 2D texture coordinates. internal struct Vertex { internal Vector3 Coordinates; internal Vector2Df TextureCoordinates; internal Vertex(double X, double Y, double Z) { this.Coordinates = new Vector3(X, Y, Z); this.TextureCoordinates = new Vector2Df(0.0f, 0.0f); } internal Vertex(Vector3 Coordinates, Vector2Df TextureCoordinates) { this.Coordinates = Coordinates; this.TextureCoordinates = TextureCoordinates; } // operators public static bool operator ==(Vertex A, Vertex B) { if (A.Coordinates.X != B.Coordinates.X | A.Coordinates.Y != B.Coordinates.Y | A.Coordinates.Z != B.Coordinates.Z) return false; if (A.TextureCoordinates.X != B.TextureCoordinates.X | A.TextureCoordinates.Y != B.TextureCoordinates.Y) return false; return true; } public static bool operator !=(Vertex A, Vertex B) { if (A.Coordinates.X != B.Coordinates.X | A.Coordinates.Y != B.Coordinates.Y | A.Coordinates.Z != B.Coordinates.Z) return true; if (A.TextureCoordinates.X != B.TextureCoordinates.X | A.TextureCoordinates.Y != B.TextureCoordinates.Y) return true; return false; } } // mesh material /// Represents material properties. internal struct MeshMaterial { /// A bit mask combining constants of the MeshMaterial structure. internal byte Flags; internal Color32 Color; internal Color24 TransparentColor; internal Color24 EmissiveColor; internal Textures.Texture DaytimeTexture; internal Textures.Texture NighttimeTexture; /// A value between 0 (daytime) and 255 (nighttime). internal byte DaytimeNighttimeBlend; internal MeshMaterialBlendMode BlendMode; /// A bit mask specifying the glow properties. Use GetGlowAttenuationData to create valid data for this field. internal ushort GlowAttenuationData; internal const int EmissiveColorMask = 1; internal const int TransparentColorMask = 2; // operators public static bool operator ==(MeshMaterial A, MeshMaterial B) { if (A.Flags != B.Flags) return false; if (A.Color.R != B.Color.R | A.Color.G != B.Color.G | A.Color.B != B.Color.B | A.Color.A != B.Color.A) return false; if (A.TransparentColor.R != B.TransparentColor.R | A.TransparentColor.G != B.TransparentColor.G | A.TransparentColor.B != B.TransparentColor.B) return false; if (A.EmissiveColor.R != B.EmissiveColor.R | A.EmissiveColor.G != B.EmissiveColor.G | A.EmissiveColor.B != B.EmissiveColor.B) return false; if (A.DaytimeTexture != B.DaytimeTexture) return false; if (A.NighttimeTexture != B.NighttimeTexture) return false; if (A.BlendMode != B.BlendMode) return false; if (A.GlowAttenuationData != B.GlowAttenuationData) return false; return true; } public static bool operator !=(MeshMaterial A, MeshMaterial B) { if (A.Flags != B.Flags) return true; if (A.Color.R != B.Color.R | A.Color.G != B.Color.G | A.Color.B != B.Color.B | A.Color.A != B.Color.A) return true; if (A.TransparentColor.R != B.TransparentColor.R | A.TransparentColor.G != B.TransparentColor.G | A.TransparentColor.B != B.TransparentColor.B) return true; if (A.EmissiveColor.R != B.EmissiveColor.R | A.EmissiveColor.G != B.EmissiveColor.G | A.EmissiveColor.B != B.EmissiveColor.B) return true; if (A.DaytimeTexture != B.DaytimeTexture) return true; if (A.NighttimeTexture != B.NighttimeTexture) return true; if (A.BlendMode != B.BlendMode) return true; if (A.GlowAttenuationData != B.GlowAttenuationData) return true; return false; } } internal enum MeshMaterialBlendMode : byte { Normal = 0, Additive = 1 } // mesh face vertex /// Represents a reference to a vertex and the normal to be used for that vertex. internal struct MeshFaceVertex { /// A reference to an element in the Vertex array of the contained Mesh structure. internal ushort Index; /// The normal to be used at the vertex. internal Vector3Df Normal; internal MeshFaceVertex(int Index) { this.Index = (ushort)Index; this.Normal = new Vector3Df(0.0f, 0.0f, 0.0f); } internal MeshFaceVertex(int Index, Vector3Df Normal) { this.Index = (ushort)Index; this.Normal = Normal; } // operators public static bool operator ==(MeshFaceVertex A, MeshFaceVertex B) { if (A.Index != B.Index) return false; if (A.Normal.X != B.Normal.X) return false; if (A.Normal.Y != B.Normal.Y) return false; if (A.Normal.Z != B.Normal.Z) return false; return true; } public static bool operator !=(MeshFaceVertex A, MeshFaceVertex B) { if (A.Index != B.Index) return true; if (A.Normal.X != B.Normal.X) return true; if (A.Normal.Y != B.Normal.Y) return true; if (A.Normal.Z != B.Normal.Z) return true; return false; } } // mesh face /// Represents a face consisting of vertices and material attributes. internal struct MeshFace { internal MeshFaceVertex[] Vertices; /// A reference to an element in the Material array of the containing Mesh structure. internal ushort Material; /// A bit mask combining constants of the MeshFace structure. internal byte Flags; internal MeshFace(int[] Vertices) { this.Vertices = new MeshFaceVertex[Vertices.Length]; for (int i = 0; i < Vertices.Length; i++) { this.Vertices[i] = new MeshFaceVertex(Vertices[i]); } this.Material = 0; this.Flags = 0; } internal void Flip() { if ((this.Flags & FaceTypeMask) == FaceTypeQuadStrip) { for (int i = 0; i < this.Vertices.Length; i += 2) { MeshFaceVertex x = this.Vertices[i]; this.Vertices[i] = this.Vertices[i + 1]; this.Vertices[i + 1] = x; } } else { int n = this.Vertices.Length; for (int i = 0; i < (n >> 1); i++) { MeshFaceVertex x = this.Vertices[i]; this.Vertices[i] = this.Vertices[n - i - 1]; this.Vertices[n - i - 1] = x; } } } internal const int FaceTypeMask = 7; internal const int FaceTypePolygon = 0; internal const int FaceTypeTriangles = 1; internal const int FaceTypeTriangleStrip = 2; internal const int FaceTypeQuads = 3; internal const int FaceTypeQuadStrip = 4; internal const int Face2Mask = 8; } // mesh /// Represents a mesh consisting of a series of vertices, faces and material properties. internal struct Mesh { internal Vertex[] Vertices; internal MeshMaterial[] Materials; internal MeshFace[] Faces; /// Creates a mesh consisting of one face, which is represented by individual vertices, and a color. /// The vertices that make up one face. /// The color to be applied on the face. internal Mesh(Vertex[] Vertices, Color32 Color) { this.Vertices = Vertices; this.Materials = new MeshMaterial[1]; this.Materials[0].Color = Color; this.Faces = new MeshFace[1]; this.Faces[0].Material = 0; this.Faces[0].Vertices = new MeshFaceVertex[Vertices.Length]; for (int i = 0; i < Vertices.Length; i++) { this.Faces[0].Vertices[i].Index = (ushort)i; } } /// Creates a mesh consisting of the specified vertices, faces and color. /// The vertices used. /// A list of faces represented by a list of references to vertices. /// The color to be applied on all of the faces. internal Mesh(Vertex[] Vertices, int[][] FaceVertices, Color32 Color) { this.Vertices = Vertices; this.Materials = new MeshMaterial[1]; this.Materials[0].Color = Color; this.Faces = new MeshFace[FaceVertices.Length]; for (int i = 0; i < FaceVertices.Length; i++) { this.Faces[i] = new MeshFace(FaceVertices[i]); } } } // glow internal enum GlowAttenuationMode { None = 0, DivisionExponent2 = 1, DivisionExponent4 = 2, } /// Creates glow attenuation data from a half distance and a mode. The resulting value can be later passed to SplitGlowAttenuationData in order to reconstruct the parameters. /// The distance at which the glow is at 50% of its full intensity. The value is clamped to the integer range from 1 to 4096. Values less than or equal to 0 disable glow attenuation. /// The glow attenuation mode. /// A System.UInt16 packed with the information about the half distance and glow attenuation mode. internal static ushort GetGlowAttenuationData(double HalfDistance, GlowAttenuationMode Mode) { if (HalfDistance <= 0.0 | Mode == GlowAttenuationMode.None) return 0; if (HalfDistance < 1.0) { HalfDistance = 1.0; } else if (HalfDistance > 4095.0) { HalfDistance = 4095.0; } return (ushort)((int)Math.Round(HalfDistance) | ((int)Mode << 12)); } /// Recreates the half distance and the glow attenuation mode from a packed System.UInt16 that was created by GetGlowAttenuationData. /// The data returned by GetGlowAttenuationData. /// The mode of glow attenuation. /// The half distance of glow attenuation. internal static void SplitGlowAttenuationData(ushort Data, out GlowAttenuationMode Mode, out double HalfDistance) { Mode = (GlowAttenuationMode)(Data >> 12); HalfDistance = (double)(Data & 4095); } // display internal static double HorizontalViewingAngle; internal static double VerticalViewingAngle; internal static double OriginalVerticalViewingAngle; internal static double AspectRatio; /// The current viewing distance in the forward direction. internal static double ForwardViewingDistance; /// The current viewing distance in the backward direction. internal static double BackwardViewingDistance; /// The extra viewing distance used for determining visibility of animated objects. internal static double ExtraViewingDistance; /// The user-selected viewing distance. internal static double BackgroundImageDistance; internal struct Background { internal Textures.Texture Texture; internal int Repetition; internal bool KeepAspectRatio; internal Background(Textures.Texture Texture, int Repetition, bool KeepAspectRatio) { this.Texture = Texture; this.Repetition = Repetition; this.KeepAspectRatio = KeepAspectRatio; } } internal static Background CurrentBackground = new Background(null, 6, false); internal static Background TargetBackground = new Background(null, 6, false); internal const double TargetBackgroundDefaultCountdown = 0.8; internal static double TargetBackgroundCountdown; // driver body internal struct DriverBody { internal double SlowX; internal double FastX; internal double Roll; internal ObjectManager.Damping RollDamping; internal double SlowY; internal double FastY; internal double Pitch; internal ObjectManager.Damping PitchDamping; } internal static DriverBody CurrentDriverBody; internal static void UpdateDriverBody(double TimeElapsed) { if (CameraRestriction == CameraRestrictionMode.NotAvailable) { { // pitch double targetY = TrainManager.PlayerTrain.Specs.CurrentAverageAcceleration; const double accelerationSlow = 0.25; const double accelerationFast = 2.0; if (CurrentDriverBody.SlowY < targetY) { CurrentDriverBody.SlowY += accelerationSlow * TimeElapsed; if (CurrentDriverBody.SlowY > targetY) { CurrentDriverBody.SlowY = targetY; } } else if (CurrentDriverBody.SlowY > targetY) { CurrentDriverBody.SlowY -= accelerationSlow * TimeElapsed; if (CurrentDriverBody.SlowY < targetY) { CurrentDriverBody.SlowY = targetY; } } if (CurrentDriverBody.FastY < targetY) { CurrentDriverBody.FastY += accelerationFast * TimeElapsed; if (CurrentDriverBody.FastY > targetY) { CurrentDriverBody.FastY = targetY; } } else if (CurrentDriverBody.FastY > targetY) { CurrentDriverBody.FastY -= accelerationFast * TimeElapsed; if (CurrentDriverBody.FastY < targetY) { CurrentDriverBody.FastY = targetY; } } double diffY = CurrentDriverBody.FastY - CurrentDriverBody.SlowY; diffY = (double)Math.Sign(diffY) * diffY * diffY; CurrentDriverBody.Pitch = 0.5 * Math.Atan(0.1 * diffY); if (CurrentDriverBody.Pitch > 0.1) { CurrentDriverBody.Pitch = 0.1; } if (CurrentDriverBody.PitchDamping == null) { CurrentDriverBody.PitchDamping = new ObjectManager.Damping(6.0, 0.3); } ObjectManager.UpdateDamping(ref CurrentDriverBody.PitchDamping, TimeElapsed, ref CurrentDriverBody.Pitch); } { // roll int c = TrainManager.PlayerTrain.DriverCar; double frontRadius = TrainManager.PlayerTrain.Cars[c].FrontAxle.Follower.CurveRadius; double rearRadius = TrainManager.PlayerTrain.Cars[c].RearAxle.Follower.CurveRadius; double radius; if (frontRadius != 0.0 & rearRadius != 0.0) { if (frontRadius != -rearRadius) { radius = 2.0 * frontRadius * rearRadius / (frontRadius + rearRadius); } else { radius = 0.0; } } else if (frontRadius != 0.0) { radius = 2.0 * frontRadius; } else if (rearRadius != 0.0) { radius = 2.0 * rearRadius; } else { radius = 0.0; } double targetX; if (radius != 0.0) { double speed = TrainManager.PlayerTrain.Cars[c].Specs.CurrentSpeed; targetX = speed * speed / radius; } else { targetX = 0.0; } const double accelerationSlow = 1.0; const double accelerationFast = 10.0; if (CurrentDriverBody.SlowX < targetX) { CurrentDriverBody.SlowX += accelerationSlow * TimeElapsed; if (CurrentDriverBody.SlowX > targetX) { CurrentDriverBody.SlowX = targetX; } } else if (CurrentDriverBody.SlowX > targetX) { CurrentDriverBody.SlowX -= accelerationSlow * TimeElapsed; if (CurrentDriverBody.SlowX < targetX) { CurrentDriverBody.SlowX = targetX; } } if (CurrentDriverBody.FastX < targetX) { CurrentDriverBody.FastX += accelerationFast * TimeElapsed; if (CurrentDriverBody.FastX > targetX) { CurrentDriverBody.FastX = targetX; } } else if (CurrentDriverBody.FastX > targetX) { CurrentDriverBody.FastX -= accelerationFast * TimeElapsed; if (CurrentDriverBody.FastX < targetX) { CurrentDriverBody.FastX = targetX; } } double diffX = CurrentDriverBody.SlowX - CurrentDriverBody.FastX; diffX = (double)Math.Sign(diffX) * diffX * diffX; CurrentDriverBody.Roll = 0.5 * Math.Atan(0.3 * diffX); if (CurrentDriverBody.RollDamping == null) { CurrentDriverBody.RollDamping = new ObjectManager.Damping(6.0, 0.3); } ObjectManager.UpdateDamping(ref CurrentDriverBody.RollDamping, TimeElapsed, ref CurrentDriverBody.Roll); } } } // mouse grab internal static bool MouseGrabEnabled = false; internal static bool MouseGrabIgnoreOnce = false; internal static Vector2D MouseGrabTarget = new Vector2D(0.0, 0.0); internal static void UpdateMouseGrab(double TimeElapsed) { if (MouseGrabEnabled) { double factor; if (CameraMode == CameraViewMode.Interior | CameraMode == CameraViewMode.InteriorLookAhead) { factor = 1.0; } else { factor = 3.0; } CameraAlignmentDirection.Yaw += factor * MouseGrabTarget.X; CameraAlignmentDirection.Pitch -= factor * MouseGrabTarget.Y; MouseGrabTarget = new Vector2D(0.0, 0.0); } } // relative camera internal struct CameraAlignment { internal Vector3 Position; internal double Yaw; internal double Pitch; internal double Roll; internal double TrackPosition; internal double Zoom; internal CameraAlignment(Vector3 Position, double Yaw, double Pitch, double Roll, double TrackPosition, double Zoom) { this.Position = Position; this.Yaw = Yaw; this.Pitch = Pitch; this.Roll = Roll; this.TrackPosition = TrackPosition; this.Zoom = Zoom; } } internal static TrackManager.TrackFollower CameraTrackFollower; internal static CameraAlignment CameraCurrentAlignment; internal static CameraAlignment CameraAlignmentDirection; internal static CameraAlignment CameraAlignmentSpeed; internal static double CameraSpeed; internal const double CameraInteriorTopSpeed = 5.0; internal const double CameraInteriorTopAngularSpeed = 5.0; internal const double CameraExteriorTopSpeed = 50.0; internal const double CameraExteriorTopAngularSpeed = 10.0; internal const double CameraZoomTopSpeed = 2.0; internal enum CameraViewMode { Interior, InteriorLookAhead, Exterior, Track, FlyBy, FlyByZooming } internal static CameraViewMode CameraMode; // camera memory internal static CameraAlignment CameraSavedInterior; internal static CameraAlignment CameraSavedExterior; internal static CameraAlignment CameraSavedTrack; // camera restriction internal static Vector3 CameraRestrictionBottomLeft = new Vector3(-1.0, -1.0, 1.0); internal static Vector3 CameraRestrictionTopRight = new Vector3(1.0, 1.0, 1.0); internal enum CameraRestrictionMode { /// Represents a 3D cab. NotAvailable = -1, /// Represents a 2D cab with camera restriction disabled. Off = 0, /// Represents a 2D cab with camera restriction enabled. On = 1 } internal static CameraRestrictionMode CameraRestriction = CameraRestrictionMode.NotAvailable; // absolute camera internal static Vector3 AbsoluteCameraPosition; internal static Vector3 AbsoluteCameraDirection; internal static Vector3 AbsoluteCameraUp; internal static Vector3 AbsoluteCameraSide; // camera restriction internal static void InitializeCameraRestriction() { if ((CameraMode == CameraViewMode.Interior | CameraMode == CameraViewMode.InteriorLookAhead) & CameraRestriction == CameraRestrictionMode.On) { CameraAlignmentSpeed = new CameraAlignment(); UpdateAbsoluteCamera(0.0); if (!PerformCameraRestrictionTest()) { CameraCurrentAlignment = new CameraAlignment(); VerticalViewingAngle = OriginalVerticalViewingAngle; MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); UpdateAbsoluteCamera(0.0); UpdateViewingDistances(); if (!PerformCameraRestrictionTest()) { CameraCurrentAlignment.Position.Z = 0.8; UpdateAbsoluteCamera(0.0); PerformProgressiveAdjustmentForCameraRestriction(ref CameraCurrentAlignment.Position.Z, 0.0, true); if (!PerformCameraRestrictionTest()) { CameraCurrentAlignment.Position.X = 0.5 * (CameraRestrictionBottomLeft.X + CameraRestrictionTopRight.X); CameraCurrentAlignment.Position.Y = 0.5 * (CameraRestrictionBottomLeft.Y + CameraRestrictionTopRight.Y); CameraCurrentAlignment.Position.Z = 0.0; UpdateAbsoluteCamera(0.0); if (PerformCameraRestrictionTest()) { PerformProgressiveAdjustmentForCameraRestriction(ref CameraCurrentAlignment.Position.X, 0.0, true); PerformProgressiveAdjustmentForCameraRestriction(ref CameraCurrentAlignment.Position.Y, 0.0, true); } else { CameraCurrentAlignment.Position.Z = 0.8; UpdateAbsoluteCamera(0.0); PerformProgressiveAdjustmentForCameraRestriction(ref CameraCurrentAlignment.Position.Z, 0.0, true); if (!PerformCameraRestrictionTest()) { CameraCurrentAlignment = new CameraAlignment(); } } } } UpdateAbsoluteCamera(0.0); } } } internal static bool PerformProgressiveAdjustmentForCameraRestriction(ref double Source, double Target, bool Zoom) { if ((CameraMode != CameraViewMode.Interior & CameraMode != CameraViewMode.InteriorLookAhead) | CameraRestriction != CameraRestrictionMode.On) { Source = Target; return true; } else { double best = Source; const int Precision = 8; double a = Source; double b = Target; Source = Target; if (Zoom) ApplyZoom(); if (PerformCameraRestrictionTest()) { return true; } else { double x = 0.5 * (a + b); bool q = true; for (int i = 0; i < Precision; i++) { Source = x; if (Zoom) ApplyZoom(); q = PerformCameraRestrictionTest(); if (q) { a = x; best = x; } else { b = x; } x = 0.5 * (a + b); } Source = best; if (Zoom) ApplyZoom(); return q; } } } internal static bool PerformCameraRestrictionTest() { if (World.CameraRestriction == CameraRestrictionMode.On) { Vector3[] p = new Vector3[] { CameraRestrictionBottomLeft, CameraRestrictionTopRight }; Vector2D[] r = new Vector2D[2]; for (int j = 0; j < 2; j++) { // determine relative world coordinates World.Rotate(ref p[j].X, ref p[j].Y, ref p[j].Z, World.AbsoluteCameraDirection.X, World.AbsoluteCameraDirection.Y, World.AbsoluteCameraDirection.Z, World.AbsoluteCameraUp.X, World.AbsoluteCameraUp.Y, World.AbsoluteCameraUp.Z, World.AbsoluteCameraSide.X, World.AbsoluteCameraSide.Y, World.AbsoluteCameraSide.Z); double rx = -Math.Tan(World.CameraCurrentAlignment.Yaw) - World.CameraCurrentAlignment.Position.X; double ry = -Math.Tan(World.CameraCurrentAlignment.Pitch) - World.CameraCurrentAlignment.Position.Y; double rz = -World.CameraCurrentAlignment.Position.Z; p[j].X += rx * World.AbsoluteCameraSide.X + ry * World.AbsoluteCameraUp.X + rz * World.AbsoluteCameraDirection.X; p[j].Y += rx * World.AbsoluteCameraSide.Y + ry * World.AbsoluteCameraUp.Y + rz * World.AbsoluteCameraDirection.Y; p[j].Z += rx * World.AbsoluteCameraSide.Z + ry * World.AbsoluteCameraUp.Z + rz * World.AbsoluteCameraDirection.Z; // determine screen coordinates double ez = AbsoluteCameraDirection.X * p[j].X + AbsoluteCameraDirection.Y * p[j].Y + AbsoluteCameraDirection.Z * p[j].Z; if (ez == 0.0) return false; double ex = AbsoluteCameraSide.X * p[j].X + AbsoluteCameraSide.Y * p[j].Y + AbsoluteCameraSide.Z * p[j].Z; double ey = AbsoluteCameraUp.X * p[j].X + AbsoluteCameraUp.Y * p[j].Y + AbsoluteCameraUp.Z * p[j].Z; r[j].X = ex / (ez * Math.Tan(0.5 * HorizontalViewingAngle)); r[j].Y = ey / (ez * Math.Tan(0.5 * VerticalViewingAngle)); } return r[0].X <= -1.0025 & r[1].X >= 1.0025 & r[0].Y <= -1.0025 & r[1].Y >= 1.0025; } else { return true; } } // update absolute camera internal static void UpdateAbsoluteCamera(double TimeElapsed) { // zoom double zm = World.CameraCurrentAlignment.Zoom; AdjustAlignment(ref World.CameraCurrentAlignment.Zoom, World.CameraAlignmentDirection.Zoom, ref World.CameraAlignmentSpeed.Zoom, TimeElapsed, true); if (zm != World.CameraCurrentAlignment.Zoom) { ApplyZoom(); } if (CameraMode == CameraViewMode.FlyBy | CameraMode == CameraViewMode.FlyByZooming) { // fly-by AdjustAlignment(ref World.CameraCurrentAlignment.Position.X, World.CameraAlignmentDirection.Position.X, ref World.CameraAlignmentSpeed.Position.X, TimeElapsed); AdjustAlignment(ref World.CameraCurrentAlignment.Position.Y, World.CameraAlignmentDirection.Position.Y, ref World.CameraAlignmentSpeed.Position.Y, TimeElapsed); double tr = World.CameraCurrentAlignment.TrackPosition; AdjustAlignment(ref World.CameraCurrentAlignment.TrackPosition, World.CameraAlignmentDirection.TrackPosition, ref World.CameraAlignmentSpeed.TrackPosition, TimeElapsed); if (tr != World.CameraCurrentAlignment.TrackPosition) { TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, World.CameraCurrentAlignment.TrackPosition, true, false); UpdateViewingDistances(); } // camera double px = World.CameraTrackFollower.WorldPosition.X; double py = World.CameraTrackFollower.WorldPosition.Y; double pz = World.CameraTrackFollower.WorldPosition.Z; // position to focus on double tx, ty, tz; double zoomMultiplier; { const double heightFactor = 0.75; TrainManager.Train bestTrain = null; double bestDistanceSquared = double.MaxValue; TrainManager.Train secondBestTrain = null; double secondBestDistanceSquared = double.MaxValue; foreach (TrainManager.Train train in TrainManager.Trains) { if (train.State == TrainManager.TrainState.Available) { double x = 0.5 * (train.Cars[0].FrontAxle.Follower.WorldPosition.X + train.Cars[0].RearAxle.Follower.WorldPosition.X); double y = 0.5 * (train.Cars[0].FrontAxle.Follower.WorldPosition.Y + train.Cars[0].RearAxle.Follower.WorldPosition.Y) + heightFactor * train.Cars[0].Height; double z = 0.5 * (train.Cars[0].FrontAxle.Follower.WorldPosition.Z + train.Cars[0].RearAxle.Follower.WorldPosition.Z); double dx = x - px; double dy = y - py; double dz = z - pz; double d = dx * dx + dy * dy + dz * dz; if (d < bestDistanceSquared) { secondBestTrain = bestTrain; secondBestDistanceSquared = bestDistanceSquared; bestTrain = train; bestDistanceSquared = d; } else if (d < secondBestDistanceSquared) { secondBestTrain = train; secondBestDistanceSquared = d; } } } if (bestTrain != null) { const double maxDistance = 100.0; double bestDistance = Math.Sqrt(bestDistanceSquared); double secondBestDistance = Math.Sqrt(secondBestDistanceSquared); if (secondBestTrain != null && secondBestDistance - bestDistance <= maxDistance) { double x1 = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.X + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.X); double y1 = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Y + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.Y) + heightFactor * bestTrain.Cars[0].Height; double z1 = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Z + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.Z); double d1; { double dx = x1 - px; double dy = y1 - py; double dz = z1 - pz; d1 = dx * dx + dy * dy + dz * dz; } double x2 = 0.5 * (secondBestTrain.Cars[0].FrontAxle.Follower.WorldPosition.X + secondBestTrain.Cars[0].RearAxle.Follower.WorldPosition.X); double y2 = 0.5 * (secondBestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Y + secondBestTrain.Cars[0].RearAxle.Follower.WorldPosition.Y) + heightFactor * secondBestTrain.Cars[0].Height; double z2 = 0.5 * (secondBestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Z + secondBestTrain.Cars[0].RearAxle.Follower.WorldPosition.Z); double d2; { double dx = x2 - px; double dy = y2 - py; double dz = z2 - pz; d2 = dx * dx + dy * dy + dz * dz; } double t = 0.5 - (secondBestDistance - bestDistance) / (2.0 * maxDistance); if (t < 0.0) t = 0.0; t = 2.0 * t * t; /* in order to change the shape of the interpolation curve */ tx = (1.0 - t) * x1 + t * x2; ty = (1.0 - t) * y1 + t * y2; tz = (1.0 - t) * z1 + t * z2; zoomMultiplier = 1.0 - 2.0 * t; } else { tx = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.X + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.X); ty = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Y + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.Y) + heightFactor * bestTrain.Cars[0].Height; tz = 0.5 * (bestTrain.Cars[0].FrontAxle.Follower.WorldPosition.Z + bestTrain.Cars[0].RearAxle.Follower.WorldPosition.Z); zoomMultiplier = 1.0; } } else { tx = 0.0; ty = 0.0; tz = 0.0; zoomMultiplier = 1.0; } } // camera { double dx = World.CameraTrackFollower.WorldDirection.X; double dy = World.CameraTrackFollower.WorldDirection.Y; double dz = World.CameraTrackFollower.WorldDirection.Z; double ux = World.CameraTrackFollower.WorldUp.X; double uy = World.CameraTrackFollower.WorldUp.Y; double uz = World.CameraTrackFollower.WorldUp.Z; double sx = World.CameraTrackFollower.WorldSide.X; double sy = World.CameraTrackFollower.WorldSide.Y; double sz = World.CameraTrackFollower.WorldSide.Z; double ox = World.CameraCurrentAlignment.Position.X; double oy = World.CameraCurrentAlignment.Position.Y; double oz = World.CameraCurrentAlignment.Position.Z; double cx = px + sx * ox + ux * oy + dx * oz; double cy = py + sy * ox + uy * oy + dy * oz; double cz = pz + sz * ox + uz * oy + dz * oz; AbsoluteCameraPosition = new Vector3(cx, cy, cz); dx = tx - cx; dy = ty - cy; dz = tz - cz; double t = Math.Sqrt(dx * dx + dy * dy + dz * dz); double ti = 1.0 / t; dx *= ti; dy *= ti; dz *= ti; AbsoluteCameraDirection = new Vector3(dx, dy, dz); AbsoluteCameraSide = new Vector3(dz, 0.0, -dx); Normalize(ref AbsoluteCameraSide.X, ref AbsoluteCameraSide.Y, ref AbsoluteCameraSide.Z); World.Cross(dx, dy, dz, AbsoluteCameraSide.X, AbsoluteCameraSide.Y, AbsoluteCameraSide.Z, out AbsoluteCameraUp.X, out AbsoluteCameraUp.Y, out AbsoluteCameraUp.Z); UpdateViewingDistances(); if (CameraMode == CameraViewMode.FlyByZooming) { // zoom const double fadeOutDistance = 600.0; /* the distance with the highest zoom factor is half the fade-out distance */ const double maxZoomFactor = 7.0; /* the zoom factor at half the fade-out distance */ const double factor = 256.0 / (fadeOutDistance * fadeOutDistance * fadeOutDistance * fadeOutDistance * fadeOutDistance * fadeOutDistance * fadeOutDistance * fadeOutDistance); double zoom; if (t < fadeOutDistance) { double tdist4 = fadeOutDistance - t; tdist4 *= tdist4; tdist4 *= tdist4; double t4 = t * t; t4 *= t4; zoom = 1.0 + factor * zoomMultiplier * (maxZoomFactor - 1.0) * tdist4 * t4; } else { zoom = 1.0; } World.VerticalViewingAngle = World.OriginalVerticalViewingAngle / zoom; MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); } } } else { // non-fly-by { // current alignment AdjustAlignment(ref World.CameraCurrentAlignment.Position.X, World.CameraAlignmentDirection.Position.X, ref World.CameraAlignmentSpeed.Position.X, TimeElapsed); AdjustAlignment(ref World.CameraCurrentAlignment.Position.Y, World.CameraAlignmentDirection.Position.Y, ref World.CameraAlignmentSpeed.Position.Y, TimeElapsed); AdjustAlignment(ref World.CameraCurrentAlignment.Position.Z, World.CameraAlignmentDirection.Position.Z, ref World.CameraAlignmentSpeed.Position.Z, TimeElapsed); if ((CameraMode == CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) & CameraRestriction == CameraRestrictionMode.On) { if (CameraCurrentAlignment.Position.Z > 0.75) { CameraCurrentAlignment.Position.Z = 0.75; } } bool q = World.CameraAlignmentSpeed.Yaw != 0.0 | World.CameraAlignmentSpeed.Pitch != 0.0 | World.CameraAlignmentSpeed.Roll != 0.0; AdjustAlignment(ref World.CameraCurrentAlignment.Yaw, World.CameraAlignmentDirection.Yaw, ref World.CameraAlignmentSpeed.Yaw, TimeElapsed); AdjustAlignment(ref World.CameraCurrentAlignment.Pitch, World.CameraAlignmentDirection.Pitch, ref World.CameraAlignmentSpeed.Pitch, TimeElapsed); AdjustAlignment(ref World.CameraCurrentAlignment.Roll, World.CameraAlignmentDirection.Roll, ref World.CameraAlignmentSpeed.Roll, TimeElapsed); double tr = World.CameraCurrentAlignment.TrackPosition; AdjustAlignment(ref World.CameraCurrentAlignment.TrackPosition, World.CameraAlignmentDirection.TrackPosition, ref World.CameraAlignmentSpeed.TrackPosition, TimeElapsed); if (tr != World.CameraCurrentAlignment.TrackPosition) { TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, World.CameraCurrentAlignment.TrackPosition, true, false); q = true; } if (q) { UpdateViewingDistances(); } } // camera double cx = World.CameraTrackFollower.WorldPosition.X; double cy = World.CameraTrackFollower.WorldPosition.Y; double cz = World.CameraTrackFollower.WorldPosition.Z; double dx = World.CameraTrackFollower.WorldDirection.X; double dy = World.CameraTrackFollower.WorldDirection.Y; double dz = World.CameraTrackFollower.WorldDirection.Z; double ux = World.CameraTrackFollower.WorldUp.X; double uy = World.CameraTrackFollower.WorldUp.Y; double uz = World.CameraTrackFollower.WorldUp.Z; double sx = World.CameraTrackFollower.WorldSide.X; double sy = World.CameraTrackFollower.WorldSide.Y; double sz = World.CameraTrackFollower.WorldSide.Z; double lookaheadYaw; double lookaheadPitch; if (CameraMode == CameraViewMode.InteriorLookAhead) { // look-ahead double d = 20.0; if (TrainManager.PlayerTrain.Specs.CurrentAverageSpeed > 0.0) { d += 3.0 * (Math.Sqrt(TrainManager.PlayerTrain.Specs.CurrentAverageSpeed * TrainManager.PlayerTrain.Specs.CurrentAverageSpeed + 1.0) - 1.0); } d -= TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].FrontAxlePosition; TrackManager.TrackFollower f = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].FrontAxle.Follower; f.TriggerType = TrackManager.EventTriggerType.None; TrackManager.UpdateTrackFollower(ref f, f.TrackPosition + d, true, false); double rx = f.WorldPosition.X - cx + World.CameraTrackFollower.WorldSide.X * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverX + World.CameraTrackFollower.WorldUp.X * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverY + World.CameraTrackFollower.WorldDirection.X * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverZ; double ry = f.WorldPosition.Y - cy + World.CameraTrackFollower.WorldSide.Y * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverX + World.CameraTrackFollower.WorldUp.Y * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverY + World.CameraTrackFollower.WorldDirection.Y * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverZ; double rz = f.WorldPosition.Z - cz + World.CameraTrackFollower.WorldSide.Z * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverX + World.CameraTrackFollower.WorldUp.Z * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverY + World.CameraTrackFollower.WorldDirection.Z * TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverZ; World.Normalize(ref rx, ref ry, ref rz); double t = dz * (sy * ux - sx * uy) + dy * (-sz * ux + sx * uz) + dx * (sz * uy - sy * uz); if (t != 0.0) { t = 1.0 / t; double tx = (rz * (-dy * ux + dx * uy) + ry * (dz * ux - dx * uz) + rx * (-dz * uy + dy * uz)) * t; double ty = (rz * (dy * sx - dx * sy) + ry * (-dz * sx + dx * sz) + rx * (dz * sy - dy * sz)) * t; double tz = (rz * (sy * ux - sx * uy) + ry * (-sz * ux + sx * uz) + rx * (sz * uy - sy * uz)) * t; lookaheadYaw = tx * tz != 0.0 ? Math.Atan2(tx, tz) : 0.0; if (ty < -1.0) { lookaheadPitch = -0.5 * Math.PI; } else if (ty > 1.0) { lookaheadPitch = 0.5 * Math.PI; } else { lookaheadPitch = Math.Asin(ty); } } else { lookaheadYaw = 0.0; lookaheadPitch = 0.0; } } else { lookaheadYaw = 0.0; lookaheadPitch = 0.0; } { // cab pitch and yaw double tx = World.CameraCurrentAlignment.Position.X; double ty = World.CameraCurrentAlignment.Position.Y; double tz = World.CameraCurrentAlignment.Position.Z; double dx2 = dx, dy2 = dy, dz2 = dz; double ux2 = ux, uy2 = uy, uz2 = uz; if ((World.CameraMode == CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) & TrainManager.PlayerTrain != null) { int c = TrainManager.PlayerTrain.DriverCar; if (c >= 0) { if (TrainManager.PlayerTrain.Cars[c].CarSections.Length == 0 || !TrainManager.PlayerTrain.Cars[c].CarSections[0].Overlay) { double a = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverPitch; double cosa = Math.Cos(-a); double sina = Math.Sin(-a); World.Rotate(ref dx2, ref dy2, ref dz2, sx, sy, sz, cosa, sina); World.Rotate(ref ux2, ref uy2, ref uz2, sx, sy, sz, cosa, sina); } } } cx += sx * tx + ux2 * ty + dx2 * tz; cy += sy * tx + uy2 * ty + dy2 * tz; cz += sz * tx + uz2 * ty + dz2 * tz; } // yaw, pitch, roll double headYaw = World.CameraCurrentAlignment.Yaw + lookaheadYaw; if ((World.CameraMode == CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) & TrainManager.PlayerTrain != null) { if (TrainManager.PlayerTrain.DriverCar >= 0) { headYaw += TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverYaw; } } double headPitch = World.CameraCurrentAlignment.Pitch + lookaheadPitch; if ((World.CameraMode == CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) & TrainManager.PlayerTrain != null) { if (TrainManager.PlayerTrain.DriverCar >= 0) { headPitch += TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverPitch; } } double bodyPitch = 0.0; double bodyRoll = 0.0; double headRoll = World.CameraCurrentAlignment.Roll; // rotation if (CameraRestriction == CameraRestrictionMode.NotAvailable & (CameraMode == CameraViewMode.Interior | CameraMode == CameraViewMode.InteriorLookAhead)) { // with body and head bodyPitch += CurrentDriverBody.Pitch; headPitch -= 0.2 * CurrentDriverBody.Pitch; bodyRoll += CurrentDriverBody.Roll; headRoll += 0.2 * CurrentDriverBody.Roll; const double bodyHeight = 0.6; const double headHeight = 0.1; { // body pitch double ry = (Math.Cos(-bodyPitch) - 1.0) * bodyHeight; double rz = Math.Sin(-bodyPitch) * bodyHeight; cx += dx * rz + ux * ry; cy += dy * rz + uy * ry; cz += dz * rz + uz * ry; if (bodyPitch != 0.0) { double cosa = Math.Cos(-bodyPitch); double sina = Math.Sin(-bodyPitch); World.Rotate(ref dx, ref dy, ref dz, sx, sy, sz, cosa, sina); World.Rotate(ref ux, ref uy, ref uz, sx, sy, sz, cosa, sina); } } { // body roll double rx = Math.Sin(bodyRoll) * bodyHeight; double ry = (Math.Cos(bodyRoll) - 1.0) * bodyHeight; cx += sx * rx + ux * ry; cy += sy * rx + uy * ry; cz += sz * rx + uz * ry; if (bodyRoll != 0.0) { double cosa = Math.Cos(-bodyRoll); double sina = Math.Sin(-bodyRoll); World.Rotate(ref ux, ref uy, ref uz, dx, dy, dz, cosa, sina); World.Rotate(ref sx, ref sy, ref sz, dx, dy, dz, cosa, sina); } } { // head yaw double rx = Math.Sin(headYaw) * headHeight; double rz = (Math.Cos(headYaw) - 1.0) * headHeight; cx += sx * rx + dx * rz; cy += sy * rx + dy * rz; cz += sz * rx + dz * rz; if (headYaw != 0.0) { double cosa = Math.Cos(headYaw); double sina = Math.Sin(headYaw); World.Rotate(ref dx, ref dy, ref dz, ux, uy, uz, cosa, sina); World.Rotate(ref sx, ref sy, ref sz, ux, uy, uz, cosa, sina); } } { // head pitch double ry = (Math.Cos(-headPitch) - 1.0) * headHeight; double rz = Math.Sin(-headPitch) * headHeight; cx += dx * rz + ux * ry; cy += dy * rz + uy * ry; cz += dz * rz + uz * ry; if (headPitch != 0.0) { double cosa = Math.Cos(-headPitch); double sina = Math.Sin(-headPitch); World.Rotate(ref dx, ref dy, ref dz, sx, sy, sz, cosa, sina); World.Rotate(ref ux, ref uy, ref uz, sx, sy, sz, cosa, sina); } } { // head roll double rx = Math.Sin(headRoll) * headHeight; double ry = (Math.Cos(headRoll) - 1.0) * headHeight; cx += sx * rx + ux * ry; cy += sy * rx + uy * ry; cz += sz * rx + uz * ry; if (headRoll != 0.0) { double cosa = Math.Cos(-headRoll); double sina = Math.Sin(-headRoll); World.Rotate(ref ux, ref uy, ref uz, dx, dy, dz, cosa, sina); World.Rotate(ref sx, ref sy, ref sz, dx, dy, dz, cosa, sina); } } } else { // without body or head double totalYaw = headYaw; double totalPitch = headPitch + bodyPitch; double totalRoll = bodyRoll + headRoll; if (totalYaw != 0.0) { double cosa = Math.Cos(totalYaw); double sina = Math.Sin(totalYaw); World.Rotate(ref dx, ref dy, ref dz, ux, uy, uz, cosa, sina); World.Rotate(ref sx, ref sy, ref sz, ux, uy, uz, cosa, sina); } if (totalPitch != 0.0) { double cosa = Math.Cos(-totalPitch); double sina = Math.Sin(-totalPitch); World.Rotate(ref dx, ref dy, ref dz, sx, sy, sz, cosa, sina); World.Rotate(ref ux, ref uy, ref uz, sx, sy, sz, cosa, sina); } if (totalRoll != 0.0) { double cosa = Math.Cos(-totalRoll); double sina = Math.Sin(-totalRoll); World.Rotate(ref ux, ref uy, ref uz, dx, dy, dz, cosa, sina); World.Rotate(ref sx, ref sy, ref sz, dx, dy, dz, cosa, sina); } } // finish AbsoluteCameraPosition = new Vector3(cx, cy, cz); AbsoluteCameraDirection = new Vector3(dx, dy, dz); AbsoluteCameraUp = new Vector3(ux, uy, uz); AbsoluteCameraSide = new Vector3(sx, sy, sz); } } private static void AdjustAlignment(ref double Source, double Direction, ref double Speed, double TimeElapsed) { AdjustAlignment(ref Source, Direction, ref Speed, TimeElapsed, false); } private static void AdjustAlignment(ref double Source, double Direction, ref double Speed, double TimeElapsed, bool Zoom) { if (Direction != 0.0 | Speed != 0.0) { if (TimeElapsed > 0.0) { if (Direction == 0.0) { double d = (0.025 + 5.0 * Math.Abs(Speed)) * TimeElapsed; if (Speed >= -d & Speed <= d) { Speed = 0.0; } else { Speed -= (double)Math.Sign(Speed) * d; } } else { double t = Math.Abs(Direction); double d = ((1.15 - 1.0 / (1.0 + 0.025 * Math.Abs(Speed)))) * TimeElapsed; Speed += Direction * d; if (Speed < -t) { Speed = -t; } else if (Speed > t) { Speed = t; } } double x = Source + Speed * TimeElapsed; if (!PerformProgressiveAdjustmentForCameraRestriction(ref Source, x, Zoom)) { Speed = 0.0; } } } } private static void ApplyZoom() { World.VerticalViewingAngle = World.OriginalVerticalViewingAngle * Math.Exp(World.CameraCurrentAlignment.Zoom); if (World.VerticalViewingAngle < 0.001) World.VerticalViewingAngle = 0.001; if (World.VerticalViewingAngle > 1.5) World.VerticalViewingAngle = 1.5; MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); } // update viewing distance internal static void UpdateViewingDistances() { double f = Math.Atan2(World.CameraTrackFollower.WorldDirection.Z, World.CameraTrackFollower.WorldDirection.X); double c = Math.Atan2(World.AbsoluteCameraDirection.Z, World.AbsoluteCameraDirection.X) - f; if (c < -Math.PI) { c += 2.0 * Math.PI; } else if (c > Math.PI) { c -= 2.0 * Math.PI; } double a0 = c - 0.5 * World.HorizontalViewingAngle; double a1 = c + 0.5 * World.HorizontalViewingAngle; double max; if (a0 <= 0.0 & a1 >= 0.0) { max = 1.0; } else { double c0 = Math.Cos(a0); double c1 = Math.Cos(a1); max = c0 > c1 ? c0 : c1; if (max < 0.0) max = 0.0; } double min; if (a0 <= -Math.PI | a1 >= Math.PI) { min = -1.0; } else { double c0 = Math.Cos(a0); double c1 = Math.Cos(a1); min = c0 < c1 ? c0 : c1; if (min > 0.0) min = 0.0; } double d = World.BackgroundImageDistance + World.ExtraViewingDistance; World.ForwardViewingDistance = d * max; World.BackwardViewingDistance = -d * min; ObjectManager.UpdateVisibility(World.CameraTrackFollower.TrackPosition + World.CameraCurrentAlignment.Position.Z, true); } // ================================ internal static void Cross(double ax, double ay, double az, double bx, double by, double bz, out double cx, out double cy, out double cz) { cx = ay * bz - az * by; cy = az * bx - ax * bz; cz = ax * by - ay * bx; } // transformation internal struct Transformation { internal Vector3 X; internal Vector3 Y; internal Vector3 Z; internal Transformation(double Yaw, double Pitch, double Roll) { if (Yaw == 0.0 & Pitch == 0.0 & Roll == 0.0) { this.X = new Vector3(1.0, 0.0, 0.0); this.Y = new Vector3(0.0, 1.0, 0.0); this.Z = new Vector3(0.0, 0.0, 1.0); } else if (Pitch == 0.0 & Roll == 0.0) { double cosYaw = Math.Cos(Yaw); double sinYaw = Math.Sin(Yaw); this.X = new Vector3(cosYaw, 0.0, -sinYaw); this.Y = new Vector3(0.0, 1.0, 0.0); this.Z = new Vector3(sinYaw, 0.0, cosYaw); } else { double sx = 1.0, sy = 0.0, sz = 0.0; double ux = 0.0, uy = 1.0, uz = 0.0; double dx = 0.0, dy = 0.0, dz = 1.0; double cosYaw = Math.Cos(Yaw); double sinYaw = Math.Sin(Yaw); double cosPitch = Math.Cos(-Pitch); double sinPitch = Math.Sin(-Pitch); double cosRoll = Math.Cos(-Roll); double sinRoll = Math.Sin(-Roll); Rotate(ref sx, ref sy, ref sz, ux, uy, uz, cosYaw, sinYaw); Rotate(ref dx, ref dy, ref dz, ux, uy, uz, cosYaw, sinYaw); Rotate(ref ux, ref uy, ref uz, sx, sy, sz, cosPitch, sinPitch); Rotate(ref dx, ref dy, ref dz, sx, sy, sz, cosPitch, sinPitch); Rotate(ref sx, ref sy, ref sz, dx, dy, dz, cosRoll, sinRoll); Rotate(ref ux, ref uy, ref uz, dx, dy, dz, cosRoll, sinRoll); this.X = new Vector3(sx, sy, sz); this.Y = new Vector3(ux, uy, uz); this.Z = new Vector3(dx, dy, dz); } } internal Transformation(Transformation Transformation, double Yaw, double Pitch, double Roll) { double sx = Transformation.X.X, sy = Transformation.X.Y, sz = Transformation.X.Z; double ux = Transformation.Y.X, uy = Transformation.Y.Y, uz = Transformation.Y.Z; double dx = Transformation.Z.X, dy = Transformation.Z.Y, dz = Transformation.Z.Z; double cosYaw = Math.Cos(Yaw); double sinYaw = Math.Sin(Yaw); double cosPitch = Math.Cos(-Pitch); double sinPitch = Math.Sin(-Pitch); double cosRoll = Math.Cos(Roll); double sinRoll = Math.Sin(Roll); Rotate(ref sx, ref sy, ref sz, ux, uy, uz, cosYaw, sinYaw); Rotate(ref dx, ref dy, ref dz, ux, uy, uz, cosYaw, sinYaw); Rotate(ref ux, ref uy, ref uz, sx, sy, sz, cosPitch, sinPitch); Rotate(ref dx, ref dy, ref dz, sx, sy, sz, cosPitch, sinPitch); Rotate(ref sx, ref sy, ref sz, dx, dy, dz, cosRoll, sinRoll); Rotate(ref ux, ref uy, ref uz, dx, dy, dz, cosRoll, sinRoll); this.X = new Vector3(sx, sy, sz); this.Y = new Vector3(ux, uy, uz); this.Z = new Vector3(dx, dy, dz); } internal Transformation(Transformation BaseTransformation, Transformation AuxTransformation) { Vector3 x = BaseTransformation.X; Vector3 y = BaseTransformation.Y; Vector3 z = BaseTransformation.Z; Vector3 s = AuxTransformation.X; Vector3 u = AuxTransformation.Y; Vector3 d = AuxTransformation.Z; Rotate(ref x.X, ref x.Y, ref x.Z, d.X, d.Y, d.Z, u.X, u.Y, u.Z, s.X, s.Y, s.Z); Rotate(ref y.X, ref y.Y, ref y.Z, d.X, d.Y, d.Z, u.X, u.Y, u.Z, s.X, s.Y, s.Z); Rotate(ref z.X, ref z.Y, ref z.Z, d.X, d.Y, d.Z, u.X, u.Y, u.Z, s.X, s.Y, s.Z); this.X = x; this.Y = y; this.Z = z; } } // rotate internal static void Rotate(ref double px, ref double py, ref double pz, double dx, double dy, double dz, double cosa, double sina) { double t = 1.0 / Math.Sqrt(dx * dx + dy * dy + dz * dz); dx *= t; dy *= t; dz *= t; double oc = 1.0 - cosa; double x = (cosa + oc * dx * dx) * px + (oc * dx * dy - sina * dz) * py + (oc * dx * dz + sina * dy) * pz; double y = (cosa + oc * dy * dy) * py + (oc * dx * dy + sina * dz) * px + (oc * dy * dz - sina * dx) * pz; double z = (cosa + oc * dz * dz) * pz + (oc * dx * dz - sina * dy) * px + (oc * dy * dz + sina * dx) * py; px = x; py = y; pz = z; } internal static void Rotate(ref float px, ref float py, ref float pz, double dx, double dy, double dz, double cosa, double sina) { double t = 1.0 / Math.Sqrt(dx * dx + dy * dy + dz * dz); dx *= t; dy *= t; dz *= t; double oc = 1.0 - cosa; double x = (cosa + oc * dx * dx) * (double)px + (oc * dx * dy - sina * dz) * (double)py + (oc * dx * dz + sina * dy) * (double)pz; double y = (cosa + oc * dy * dy) * (double)py + (oc * dx * dy + sina * dz) * (double)px + (oc * dy * dz - sina * dx) * (double)pz; double z = (cosa + oc * dz * dz) * (double)pz + (oc * dx * dz - sina * dy) * (double)px + (oc * dy * dz + sina * dx) * (double)py; px = (float)x; py = (float)y; pz = (float)z; } internal static void Rotate(ref Vector2D Vector, double cosa, double sina) { double u = Vector.X * cosa - Vector.Y * sina; double v = Vector.X * sina + Vector.Y * cosa; Vector.X = u; Vector.Y = v; } internal static void Rotate(ref float px, ref float py, ref float pz, double dx, double dy, double dz, double ux, double uy, double uz, double sx, double sy, double sz) { double x, y, z; x = sx * (double)px + ux * (double)py + dx * (double)pz; y = sy * (double)px + uy * (double)py + dy * (double)pz; z = sz * (double)px + uz * (double)py + dz * (double)pz; px = (float)x; py = (float)y; pz = (float)z; } internal static void Rotate(ref double px, ref double py, ref double pz, double dx, double dy, double dz, double ux, double uy, double uz, double sx, double sy, double sz) { double x, y, z; x = sx * px + ux * py + dx * pz; y = sy * px + uy * py + dy * pz; z = sz * px + uz * py + dz * pz; px = x; py = y; pz = z; } internal static void Rotate(ref float px, ref float py, ref float pz, Transformation t) { double x, y, z; x = t.X.X * (double)px + t.Y.X * (double)py + t.Z.X * (double)pz; y = t.X.Y * (double)px + t.Y.Y * (double)py + t.Z.Y * (double)pz; z = t.X.Z * (double)px + t.Y.Z * (double)py + t.Z.Z * (double)pz; px = (float)x; py = (float)y; pz = (float)z; } internal static void Rotate(ref double px, ref double py, ref double pz, Transformation t) { double x, y, z; x = t.X.X * px + t.Y.X * py + t.Z.X * pz; y = t.X.Y * px + t.Y.Y * py + t.Z.Y * pz; z = t.X.Z * px + t.Y.Z * py + t.Z.Z * pz; px = x; py = y; pz = z; } internal static void RotatePlane(ref Vector3 Vector, double cosa, double sina) { double u = Vector.X * cosa - Vector.Z * sina; double v = Vector.X * sina + Vector.Z * cosa; Vector.X = u; Vector.Z = v; } internal static void RotatePlane(ref Vector3Df Vector, double cosa, double sina) { double u = (double)Vector.X * cosa - (double)Vector.Z * sina; double v = (double)Vector.X * sina + (double)Vector.Z * cosa; Vector.X = (float)u; Vector.Z = (float)v; } internal static void RotateUpDown(ref Vector3 Vector, Vector2D Direction, double cosa, double sina) { double dx = Direction.X, dy = Direction.Y; double x = Vector.X, y = Vector.Y, z = Vector.Z; double u = dy * x - dx * z; double v = dx * x + dy * z; Vector.X = dy * u + dx * v * cosa - dx * y * sina; Vector.Y = y * cosa + v * sina; Vector.Z = -dx * u + dy * v * cosa - dy * y * sina; } internal static void RotateUpDown(ref Vector3 Vector, double dx, double dy, double cosa, double sina) { double x = Vector.X, y = Vector.Y, z = Vector.Z; double u = dy * x - dx * z; double v = dx * x + dy * z; Vector.X = dy * u + dx * v * cosa - dx * y * sina; Vector.Y = y * cosa + v * sina; Vector.Z = -dx * u + dy * v * cosa - dy * y * sina; } internal static void RotateUpDown(ref Vector3Df Vector, double dx, double dy, double cosa, double sina) { double x = (double)Vector.X, y = (double)Vector.Y, z = (double)Vector.Z; double u = dy * x - dx * z; double v = dx * x + dy * z; Vector.X = (float)(dy * u + dx * v * cosa - dx * y * sina); Vector.Y = (float)(y * cosa + v * sina); Vector.Z = (float)(-dx * u + dy * v * cosa - dy * y * sina); } internal static void RotateUpDown(ref double px, ref double py, ref double pz, double dx, double dz, double cosa, double sina) { double x = px, y = py, z = pz; double u = dz * x - dx * z; double v = dx * x + dz * z; px = dz * u + dx * v * cosa - dx * y * sina; py = y * cosa + v * sina; pz = -dx * u + dz * v * cosa - dz * y * sina; } // normalize internal static void Normalize(ref double x, ref double y) { double t = x * x + y * y; if (t != 0.0) { t = 1.0 / Math.Sqrt(t); x *= t; y *= t; } } internal static void Normalize(ref double x, ref double y, ref double z) { double t = x * x + y * y + z * z; if (t != 0.0) { t = 1.0 / Math.Sqrt(t); x *= t; y *= t; z *= t; } } // create normals internal static void CreateNormals(ref Mesh Mesh) { for (int i = 0; i < Mesh.Faces.Length; i++) { CreateNormals(ref Mesh, i); } } internal static void CreateNormals(ref Mesh Mesh, int FaceIndex) { if (Mesh.Faces[FaceIndex].Vertices.Length >= 3) { int i0 = (int)Mesh.Faces[FaceIndex].Vertices[0].Index; int i1 = (int)Mesh.Faces[FaceIndex].Vertices[1].Index; int i2 = (int)Mesh.Faces[FaceIndex].Vertices[2].Index; double ax = Mesh.Vertices[i1].Coordinates.X - Mesh.Vertices[i0].Coordinates.X; double ay = Mesh.Vertices[i1].Coordinates.Y - Mesh.Vertices[i0].Coordinates.Y; double az = Mesh.Vertices[i1].Coordinates.Z - Mesh.Vertices[i0].Coordinates.Z; double bx = Mesh.Vertices[i2].Coordinates.X - Mesh.Vertices[i0].Coordinates.X; double by = Mesh.Vertices[i2].Coordinates.Y - Mesh.Vertices[i0].Coordinates.Y; double bz = Mesh.Vertices[i2].Coordinates.Z - Mesh.Vertices[i0].Coordinates.Z; double nx = ay * bz - az * by; double ny = az * bx - ax * bz; double nz = ax * by - ay * bx; double t = nx * nx + ny * ny + nz * nz; if (t != 0.0) { t = 1.0 / Math.Sqrt(t); float mx = (float)(nx * t); float my = (float)(ny * t); float mz = (float)(nz * t); for (int j = 0; j < Mesh.Faces[FaceIndex].Vertices.Length; j++) { if (Mesh.Faces[FaceIndex].Vertices[j].Normal.IsZero()) { Mesh.Faces[FaceIndex].Vertices[j].Normal = new Vector3Df(mx, my, mz); } } } else { for (int j = 0; j < Mesh.Faces[FaceIndex].Vertices.Length; j++) { if (Mesh.Faces[FaceIndex].Vertices[j].Normal.IsZero()) { Mesh.Faces[FaceIndex].Vertices[j].Normal = new Vector3Df(0.0f, 1.0f, 0.0f); } } } } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formImage.Designer.cs000066400000000000000000000037541171674032100235450ustar00rootroot00000000000000namespace OpenBve { partial class formImage { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.SuspendLayout(); // // formImage // this.AutoScaleDimensions = new System.Drawing.SizeF(96.0f, 96.0f); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.BackColor = System.Drawing.Color.Black; this.ClientSize = new System.Drawing.Size(960, 600); this.DoubleBuffered = true; this.MinimizeBox = false; this.Name = "formImage"; this.ShowIcon = false; this.ShowInTaskbar = false; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "openBVE"; this.Paint += new System.Windows.Forms.PaintEventHandler(this.formImage_Paint); this.Resize += new System.EventHandler(this.formImage_Resize); this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.formImage_KeyDown); this.ResumeLayout(false); } #endregion } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formImage.cs000066400000000000000000000042301171674032100217740ustar00rootroot00000000000000using System; using System.Drawing; using System.Windows.Forms; namespace OpenBve { internal partial class formImage : Form { internal formImage() { InitializeComponent(); } // show image dialog internal static void ShowImageDialog(Image Image) { formImage Dialog = new formImage(); Dialog.CurrentImage = Image; Dialog.ShowDialog(); Dialog.Dispose(); } // members internal Image CurrentImage = null; // resize private void formImage_Resize(object sender, EventArgs e) { this.Invalidate(); } // key down private void formImage_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Escape) { this.Close(); } } // paint private void formImage_Paint(object sender, PaintEventArgs e) { float aw = (float)this.ClientRectangle.Width; float ah = (float)this.ClientRectangle.Height; float ar = aw / ah; float bw = (float)CurrentImage.Width; float bh = (float)CurrentImage.Height; float br = bw / bh; try { e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; System.Drawing.Drawing2D.LinearGradientBrush Gradient = new System.Drawing.Drawing2D.LinearGradientBrush(new Point(0, 0), new Point(0, this.ClientRectangle.Height), SystemColors.Control, SystemColors.ControlDark); e.Graphics.FillRectangle(Gradient, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height); if (ar > br) { e.Graphics.DrawImage(CurrentImage, new RectangleF(0.5f * (aw - ah * br), 0.0f, ah * br, ah), new RectangleF(0.0f, 0.0f, bw, bh), GraphicsUnit.Pixel); } else { e.Graphics.DrawImage(CurrentImage, new RectangleF(0.0f, 0.5f * (ah - aw / br), aw, aw / br), new RectangleF(0.0f, 0.0f, bw, bh), GraphicsUnit.Pixel); } } catch { } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formImage.resources000066400000000000000000000002641171674032100234040ustar00rootroot00000000000000lSystem.Resources.ResourceReader, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceSetPADPADPopenbve-1.4.0.10/openBVE/OpenBve/OldCode/formImage.resx000066400000000000000000000132661171674032100223610ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 openbve-1.4.0.10/openBVE/OpenBve/OldCode/formMain.Controls.cs000066400000000000000000000645531171674032100234560ustar00rootroot00000000000000using System; using System.Drawing; using System.Windows.Forms; using Tao.Sdl; namespace OpenBve { internal partial class formMain : Form { // ======== // controls // ======== // controls private void listviewControls_SelectedIndexChanged(object sender, EventArgs e) { if (listviewControls.SelectedIndices.Count == 1) { int i = listviewControls.SelectedIndices[0]; { this.Tag = new object(); { /// command int j; for (j = 0; j < Interface.CommandInfos.Length; j++) { if (Interface.CommandInfos[j].Command == Interface.CurrentControls[i].Command) { comboboxCommand.SelectedIndex = j; break; } } if (j == Interface.CommandInfos.Length) { comboboxCommand.SelectedIndex = -1; } } /// data if (Interface.CurrentControls[i].Method == Interface.ControlMethod.Keyboard) { radiobuttonKeyboard.Checked = true; } else if (Interface.CurrentControls[i].Method == Interface.ControlMethod.Joystick) { radiobuttonJoystick.Checked = true; } else { radiobuttonKeyboard.Checked = false; radiobuttonJoystick.Checked = false; } panelKeyboard.Enabled = radiobuttonKeyboard.Checked; if (radiobuttonKeyboard.Checked) { int j; for (j = 0; j < Interface.Keys.Length; j++) { if (Interface.Keys[j].Value == Interface.CurrentControls[i].Element) { comboboxKeyboardKey.SelectedIndex = j; break; } } if (j == Interface.Keys.Length) { comboboxKeyboardKey.SelectedIndex = -1; } checkboxKeyboardShift.Checked = (Interface.CurrentControls[i].Modifier & Interface.KeyboardModifier.Shift) != 0; checkboxKeyboardCtrl.Checked = (Interface.CurrentControls[i].Modifier & Interface.KeyboardModifier.Ctrl) != 0; checkboxKeyboardAlt.Checked = (Interface.CurrentControls[i].Modifier & Interface.KeyboardModifier.Alt) != 0; } else if (radiobuttonJoystick.Checked) { labelJoystickAssignmentValue.Text = GetControlDetails(i); } else { comboboxKeyboardKey.SelectedIndex = -1; checkboxKeyboardShift.Checked = false; checkboxKeyboardCtrl.Checked = false; checkboxKeyboardAlt.Checked = false; } panelJoystick.Enabled = radiobuttonJoystick.Checked; /// finalize this.Tag = null; } buttonControlRemove.Enabled = true; buttonControlUp.Enabled = i > 0; buttonControlDown.Enabled = i < Interface.CurrentControls.Length - 1; groupboxControl.Enabled = true; } else { this.Tag = new object(); comboboxCommand.SelectedIndex = -1; radiobuttonKeyboard.Checked = false; radiobuttonJoystick.Checked = false; groupboxControl.Enabled = false; comboboxKeyboardKey.SelectedIndex = -1; checkboxKeyboardShift.Checked = false; checkboxKeyboardCtrl.Checked = false; checkboxKeyboardAlt.Checked = false; labelJoystickAssignmentValue.Text = ""; this.Tag = null; buttonControlRemove.Enabled = false; buttonControlUp.Enabled = false; buttonControlDown.Enabled = false; } } private void UpdateControlListElement(ListViewItem Item, int Index, bool ResizeColumns) { Interface.CommandInfo Info; Interface.TryGetCommandInfo(Interface.CurrentControls[Index].Command, out Info); Item.SubItems[0].Text = Info.Name; switch (Info.Type) { case Interface.CommandType.Digital: Item.SubItems[1].Text = Interface.GetInterfaceString("controls_list_type_digital"); break; case Interface.CommandType.AnalogHalf: Item.SubItems[1].Text = Interface.GetInterfaceString("controls_list_type_analoghalf"); break; case Interface.CommandType.AnalogFull: Item.SubItems[1].Text = Interface.GetInterfaceString("controls_list_type_analogfull"); break; default: Item.SubItems[1].Text = Info.Type.ToString(); break; } Item.SubItems[2].Text = Info.Description; if (Interface.CurrentControls[Index].Method == Interface.ControlMethod.Keyboard) { Item.ImageKey = "keyboard"; } else if (Interface.CurrentControls[Index].Method == Interface.ControlMethod.Joystick) { if (Info.Type == Interface.CommandType.AnalogHalf | Info.Type == Interface.CommandType.AnalogFull) { Item.ImageKey = "joystick"; } else { Item.ImageKey = "gamepad"; } } else { Item.ImageKey = null; } Item.SubItems[3].Text = GetControlDetails(Index); if (ResizeColumns) { listviewControls.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); } } // get control details private string GetControlDetails(int Index) { Interface.CommandInfo Info; Interface.TryGetCommandInfo(Interface.CurrentControls[Index].Command, out Info); System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string Separator = Interface.GetInterfaceString("controls_assignment_separator"); if (Interface.CurrentControls[Index].Method == Interface.ControlMethod.Keyboard) { string t = Interface.GetInterfaceString("controls_assignment_keyboard") + Separator; if ((Interface.CurrentControls[Index].Modifier & Interface.KeyboardModifier.Shift) != 0) t += Interface.GetInterfaceString("controls_assignment_keyboard_shift"); if ((Interface.CurrentControls[Index].Modifier & Interface.KeyboardModifier.Ctrl) != 0) t += Interface.GetInterfaceString("controls_assignment_keyboard_ctrl"); if ((Interface.CurrentControls[Index].Modifier & Interface.KeyboardModifier.Alt) != 0) t += Interface.GetInterfaceString("controls_assignment_keyboard_alt"); int j; for (j = 0; j < Interface.Keys.Length; j++) { if (Interface.Keys[j].Value == Interface.CurrentControls[Index].Element) { t += Interface.Keys[j].Description; break; } } if (j == Interface.Keys.Length) { t += "{" + Interface.CurrentControls[Index].Element.ToString(Culture) + "}"; } return t; } else if (Interface.CurrentControls[Index].Method == Interface.ControlMethod.Joystick) { string t = Interface.GetInterfaceString("controls_assignment_joystick").Replace("[index]", (Interface.CurrentControls[Index].Device + 1).ToString(Culture)); switch (Interface.CurrentControls[Index].Component) { case Interface.JoystickComponent.Axis: t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_axis").Replace("[index]", (Interface.CurrentControls[Index].Element + 1).ToString(Culture)); if (Interface.CurrentControls[Index].Direction == -1) { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_axis_negative"); } else if (Interface.CurrentControls[Index].Direction == 1) { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_axis_positive"); } else { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_axis_invalid"); } break; case Interface.JoystickComponent.Button: t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_button").Replace("[index]", (Interface.CurrentControls[Index].Element + 1).ToString(Culture)); break; case Interface.JoystickComponent.Hat: t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_hat").Replace("[index]", (Interface.CurrentControls[Index].Element + 1).ToString(Culture)); if (Interface.CurrentControls[Index].Direction == (int)Sdl.SDL_HAT_LEFT) { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_hat_left"); } else if (Interface.CurrentControls[Index].Direction == (int)Sdl.SDL_HAT_LEFTUP) { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_hat_upleft"); } else if (Interface.CurrentControls[Index].Direction == (int)Sdl.SDL_HAT_UP) { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_hat_up"); } else if (Interface.CurrentControls[Index].Direction == (int)Sdl.SDL_HAT_RIGHTUP) { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_hat_upright"); } else if (Interface.CurrentControls[Index].Direction == (int)Sdl.SDL_HAT_RIGHT) { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_hat_right"); } else if (Interface.CurrentControls[Index].Direction == (int)Sdl.SDL_HAT_RIGHTDOWN) { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_hat_downright"); } else if (Interface.CurrentControls[Index].Direction == (int)Sdl.SDL_HAT_DOWN) { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_hat_down"); } else if (Interface.CurrentControls[Index].Direction == (int)Sdl.SDL_HAT_LEFTDOWN) { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_hat_downleft"); } else { t += Separator + Interface.GetInterfaceString("controls_assignment_joystick_hat_invalid"); } break; default: break; } return t; } else { return Interface.GetInterfaceString("controls_assignment_invalid"); } } // control add private void buttonControlAdd_Click(object sender, EventArgs e) { for (int i = 0; i < Interface.CurrentControls.Length; i++) { listviewControls.Items[i].Selected = false; } int n = Interface.CurrentControls.Length; Array.Resize(ref Interface.CurrentControls, n + 1); Interface.CurrentControls[n].Command = Interface.Command.None; ListViewItem Item = new ListViewItem(new string[] { "", "", "", "" }); UpdateControlListElement(Item, n, true); listviewControls.Items.Add(Item); Item.Selected = true; } // control remove private void buttonControlRemove_Click(object sender, EventArgs e) { if (listviewControls.SelectedIndices.Count == 1) { int j = listviewControls.SelectedIndices[0]; for (int i = j; i < Interface.CurrentControls.Length - 1; i++) { Interface.CurrentControls[i] = Interface.CurrentControls[i + 1]; } Array.Resize(ref Interface.CurrentControls, Interface.CurrentControls.Length - 1); listviewControls.Items[j].Remove(); } } // control up private void buttonControlUp_Click(object sender, EventArgs e) { if (listviewControls.SelectedIndices.Count == 1) { int j = listviewControls.SelectedIndices[0]; if (j > 0) { Interface.Control c = Interface.CurrentControls[j]; Interface.CurrentControls[j] = Interface.CurrentControls[j - 1]; Interface.CurrentControls[j - 1] = c; ListViewItem v = listviewControls.Items[j]; listviewControls.Items.RemoveAt(j); listviewControls.Items.Insert(j - 1, v); } } } // control down private void buttonControlDown_Click(object sender, EventArgs e) { if (listviewControls.SelectedIndices.Count == 1) { int j = listviewControls.SelectedIndices[0]; if (j < Interface.CurrentControls.Length - 1) { Interface.Control c = Interface.CurrentControls[j]; Interface.CurrentControls[j] = Interface.CurrentControls[j + 1]; Interface.CurrentControls[j + 1] = c; ListViewItem v = listviewControls.Items[j]; listviewControls.Items.RemoveAt(j); listviewControls.Items.Insert(j + 1, v); } } } // command private void comboboxCommand_SelectedIndexChanged(object sender, EventArgs e) { if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int i = listviewControls.SelectedIndices[0]; int j = comboboxCommand.SelectedIndex; if (j >= 0) { Interface.CurrentControls[i].Command = Interface.CommandInfos[j].Command; Interface.CommandInfo Info; Interface.TryGetCommandInfo(Interface.CommandInfos[j].Command, out Info); Interface.CurrentControls[i].InheritedType = Info.Type; UpdateControlListElement(listviewControls.Items[i], i, true); } } } // ======== // keyboard // ======== // keyboard private void radiobuttonKeyboard_CheckedChanged(object sender, EventArgs e) { if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int i = listviewControls.SelectedIndices[0]; Interface.CurrentControls[i].Method = Interface.ControlMethod.Keyboard; UpdateControlListElement(listviewControls.Items[i], i, true); } panelKeyboard.Enabled = radiobuttonKeyboard.Checked; } // key private void comboboxKeyboardKey_SelectedIndexChanged(object sender, EventArgs e) { if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int i = listviewControls.SelectedIndices[0]; int j = comboboxKeyboardKey.SelectedIndex; if (j >= 0) { Interface.CurrentControls[i].Element = Interface.Keys[j].Value; } UpdateControlListElement(listviewControls.Items[i], i, true); } } // modifiers private void checkboxKeyboardShift_CheckedChanged(object sender, EventArgs e) { if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int i = listviewControls.SelectedIndices[0]; Interface.CurrentControls[i].Modifier = (checkboxKeyboardShift.Checked ? Interface.KeyboardModifier.Shift : Interface.KeyboardModifier.None) | (checkboxKeyboardCtrl.Checked ? Interface.KeyboardModifier.Ctrl : Interface.KeyboardModifier.None) | (checkboxKeyboardAlt.Checked ? Interface.KeyboardModifier.Alt : Interface.KeyboardModifier.None); UpdateControlListElement(listviewControls.Items[i], i, true); } } private void checkboxKeyboardCtrl_CheckedChanged(object sender, EventArgs e) { if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int i = listviewControls.SelectedIndices[0]; Interface.CurrentControls[i].Modifier = (checkboxKeyboardShift.Checked ? Interface.KeyboardModifier.Shift : Interface.KeyboardModifier.None) | (checkboxKeyboardCtrl.Checked ? Interface.KeyboardModifier.Ctrl : Interface.KeyboardModifier.None) | (checkboxKeyboardAlt.Checked ? Interface.KeyboardModifier.Alt : Interface.KeyboardModifier.None); UpdateControlListElement(listviewControls.Items[i], i, true); } } private void checkboxKeyboardAlt_CheckedChanged(object sender, EventArgs e) { if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int i = listviewControls.SelectedIndices[0]; Interface.CurrentControls[i].Modifier = (checkboxKeyboardShift.Checked ? Interface.KeyboardModifier.Shift : Interface.KeyboardModifier.None) | (checkboxKeyboardCtrl.Checked ? Interface.KeyboardModifier.Ctrl : Interface.KeyboardModifier.None) | (checkboxKeyboardAlt.Checked ? Interface.KeyboardModifier.Alt : Interface.KeyboardModifier.None); UpdateControlListElement(listviewControls.Items[i], i, true); } } // ======== // joystick // ======== // joystick private void radiobuttonJoystick_CheckedChanged(object sender, EventArgs e) { if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int i = listviewControls.SelectedIndices[0]; Interface.CurrentControls[i].Method = Interface.ControlMethod.Joystick; UpdateControlListElement(listviewControls.Items[i], i, true); } panelJoystick.Enabled = radiobuttonJoystick.Checked; } // details private void UpdateJoystickDetails() { if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int j = listviewControls.SelectedIndices[0]; labelJoystickAssignmentValue.Text = GetControlDetails(j); } } // import private void buttonControlsImport_Click(object sender, EventArgs e) { OpenFileDialog Dialog = new OpenFileDialog(); Dialog.CheckFileExists = true; //Dialog.InitialDirectory = Interface.GetControlsFolder(); Dialog.Filter = Interface.GetInterfaceString("dialog_controlsfiles") + "|*.controls|" + Interface.GetInterfaceString("dialog_allfiles") + "|*"; if (Dialog.ShowDialog() == DialogResult.OK) { try { Interface.LoadControls(Dialog.FileName, out Interface.CurrentControls); for (int i = 0; i < listviewControls.SelectedItems.Count; i++) { listviewControls.SelectedItems[i].Selected = false; } listviewControls.Items.Clear(); ListViewItem[] Items = new ListViewItem[Interface.CurrentControls.Length]; for (int i = 0; i < Interface.CurrentControls.Length; i++) { Items[i] = new ListViewItem(new string[] { "", "", "", "" }); UpdateControlListElement(Items[i], i, false); } listviewControls.Items.AddRange(Items); listviewControls.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } } } // export private void buttonControlsExport_Click(object sender, EventArgs e) { SaveFileDialog Dialog = new SaveFileDialog(); Dialog.OverwritePrompt = true; //Dialog.InitialDirectory = Interface.GetControlsFolder(); Dialog.Filter = Interface.GetInterfaceString("dialog_controlsfiles") + "|*.controls|" + Interface.GetInterfaceString("dialog_allfiles") + "|*"; if (Dialog.ShowDialog() == DialogResult.OK) { try { Interface.SaveControls(Dialog.FileName); } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } } } // joystick grab private void textboxJoystickGrab_Enter(object sender, EventArgs e) { bool FullAxis = false; if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int j = listviewControls.SelectedIndices[0]; if (Interface.CurrentControls[j].InheritedType == Interface.CommandType.AnalogFull) { FullAxis = true; } } if (FullAxis) { textboxJoystickGrab.Text = Interface.GetInterfaceString("controls_selection_joystick_assignment_grab_fullaxis"); } else { textboxJoystickGrab.Text = Interface.GetInterfaceString("controls_selection_joystick_assignment_grab_normal"); } textboxJoystickGrab.BackColor = Color.Crimson; textboxJoystickGrab.ForeColor = Color.White; } private void textboxJoystickGrab_Leave(object sender, EventArgs e) { textboxJoystickGrab.Text = Interface.GetInterfaceString("controls_selection_joystick_assignment_grab"); textboxJoystickGrab.BackColor = panelControls.BackColor; textboxJoystickGrab.ForeColor = Color.Black; } // attached joysticks private void pictureboxJoysticks_Paint(object sender, PaintEventArgs e) { int device = -1; Interface.JoystickComponent component = Interface.JoystickComponent.Invalid; int element = -1; int direction = -1; Interface.CommandType type = Interface.CommandType.Digital; if (this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int j = listviewControls.SelectedIndices[0]; if (Interface.CurrentControls[j].Method == Interface.ControlMethod.Joystick) { device = Interface.CurrentControls[j].Device; component = Interface.CurrentControls[j].Component; element = Interface.CurrentControls[j].Element; direction = Interface.CurrentControls[j].Direction; type = Interface.CurrentControls[j].InheritedType; } } Sdl.SDL_JoystickUpdate(); System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; Font f = new Font(this.Font.Name, 0.875f * this.Font.Size); float x = 2.0f, y = 2.0f; float threshold = ((float)trackbarJoystickAxisThreshold.Value - (float)trackbarJoystickAxisThreshold.Minimum) / (float)(trackbarJoystickAxisThreshold.Maximum - trackbarJoystickAxisThreshold.Minimum); for (int i = 0; i < Joysticks.AttachedJoysticks.Length; i++) { float w, h; if (JoystickImage != null) { e.Graphics.DrawImage(JoystickImage, x, y); w = (float)JoystickImage.Width; h = (float)JoystickImage.Height; if (h < 64.0f) h = 64.0f; } else { w = 64.0f; h = 64.0f; e.Graphics.DrawRectangle(new Pen(labelControlsTitle.BackColor), x, y, w, h); } { /// joystick number e.Graphics.FillEllipse(Brushes.Gold, x + w - 16.0f, y, 16.0f, 16.0f); e.Graphics.DrawEllipse(Pens.Black, x + w - 16.0f, y, 16.0f, 16.0f); string t = (i + 1).ToString(Culture); SizeF s = e.Graphics.MeasureString(t, f); e.Graphics.DrawString(t, f, Brushes.Black, x + w - 8.0f - 0.5f * s.Width, y + 8.0f - 0.5f * s.Height); } { /// joystick name e.Graphics.DrawString(Joysticks.AttachedJoysticks[i].Name, this.Font, Brushes.Black, x + w + 8.0f, y); } float m; if (groupboxJoysticks.Enabled) { m = x; Pen p = new Pen(Color.DarkGoldenrod, 2.0f); Pen ps = new Pen(Color.Firebrick, 2.0f); { /// first row float u = x + w + 8.0f; float v = y + 24.0f; float g = h - 24.0f; { /// trackballs int n = Sdl.SDL_JoystickNumBalls(Joysticks.AttachedJoysticks[i].SdlHandle); for (int j = 0; j < n; j++) { e.Graphics.DrawEllipse(Pens.Gray, u, v, g, g); string t = "L" + (j + 1).ToString(Culture); SizeF s = e.Graphics.MeasureString(t, f); e.Graphics.DrawString(t, f, Brushes.Gray, u + 0.5f * (g - s.Width), v + 0.5f * (g - s.Height)); int dx, dy; Sdl.SDL_JoystickGetBall(Joysticks.AttachedJoysticks[i].SdlHandle, j, out dx, out dy); u += g + 8.0f; } } { /// hats int n = Sdl.SDL_JoystickNumHats(Joysticks.AttachedJoysticks[i].SdlHandle); for (int j = 0; j < n; j++) { if (device == i & component == Interface.JoystickComponent.Hat & element == j) { e.Graphics.DrawEllipse(ps, u, v, g, g); } else { e.Graphics.DrawEllipse(p, u, v, g, g); } string t = "H" + (j + 1).ToString(Culture); SizeF s = e.Graphics.MeasureString(t, f); e.Graphics.DrawString(t, f, Brushes.Black, u + 0.5f * (g - s.Width), v + 0.5f * (g - s.Height)); byte a = Sdl.SDL_JoystickGetHat(Joysticks.AttachedJoysticks[i].SdlHandle, j); if (a != Sdl.SDL_HAT_CENTERED) { double rx = (a & Sdl.SDL_HAT_LEFT) != 0 ? -1.0 : (a & Sdl.SDL_HAT_RIGHT) != 0 ? 1.0 : 0.0; double ry = (a & Sdl.SDL_HAT_UP) != 0 ? -1.0 : (a & Sdl.SDL_HAT_DOWN) != 0 ? 1.0 : 0.0; double rt = rx * rx + ry * ry; rt = 1.0 / Math.Sqrt(rt); rx *= rt; ry *= rt; float dx = (float)(0.5 * rx * (g - 8.0)); float dy = (float)(0.5 * ry * (g - 8.0)); e.Graphics.FillEllipse(Brushes.White, u + 0.5f * g + dx - 4.0f, v + 0.5f * g + dy - 4.0f, 8.0f, 8.0f); e.Graphics.DrawEllipse(new Pen(Color.Firebrick, 2.0f), u + 0.5f * g + dx - 4.0f, v + 0.5f * g + dy - 4.0f, 8.0f, 8.0f); } if (device == i & component == Interface.JoystickComponent.Hat & element == j) { double rx = (direction & Sdl.SDL_HAT_LEFT) != 0 ? -1.0 : (direction & Sdl.SDL_HAT_RIGHT) != 0 ? 1.0 : 0.0; double ry = (direction & Sdl.SDL_HAT_UP) != 0 ? -1.0 : (direction & Sdl.SDL_HAT_DOWN) != 0 ? 1.0 : 0.0; double rt = rx * rx + ry * ry; rt = 1.0 / Math.Sqrt(rt); rx *= rt; ry *= rt; float dx = (float)(0.5 * rx * (g - 8.0)); float dy = (float)(0.5 * ry * (g - 8.0)); e.Graphics.FillEllipse(Brushes.Firebrick, u + 0.5f * g + dx - 2.0f, v + 0.5f * g + dy - 2.0f, 4.0f, 4.0f); } u += g + 8.0f; } } if (u > m) m = u; } { /// second row float u = x; float v = y + h + 8.0f; { /// axes int n = Sdl.SDL_JoystickNumAxes(Joysticks.AttachedJoysticks[i].SdlHandle); float g = (float)pictureboxJoysticks.ClientRectangle.Height - v - 2.0f; for (int j = 0; j < n; j++) { float r = (float)Sdl.SDL_JoystickGetAxis(Joysticks.AttachedJoysticks[i].SdlHandle, j) / 32768.0f; float r0 = r < 0.0f ? r : 0.0f; float r1 = r > 0.0f ? r : 0.0f; if ((float)Math.Abs((double)r) < threshold) { e.Graphics.FillRectangle(Brushes.RosyBrown, u, v + 0.5f * g - 0.5f * r1 * g, 16.0f, 0.5f * g * (r1 - r0)); } else { e.Graphics.FillRectangle(Brushes.Firebrick, u, v + 0.5f * g - 0.5f * r1 * g, 16.0f, 0.5f * g * (r1 - r0)); } if (device == i & component == Interface.JoystickComponent.Axis & element == j) { if (direction == -1 & type != Interface.CommandType.AnalogFull) { e.Graphics.DrawRectangle(p, u, v, 16.0f, g); e.Graphics.DrawRectangle(ps, u, v + 0.5f * g, 16.0f, 0.5f * g); } else if (direction == 1 & type != Interface.CommandType.AnalogFull) { e.Graphics.DrawRectangle(p, u, v, 16.0f, g); e.Graphics.DrawRectangle(ps, u, v, 16.0f, 0.5f * g); } else { e.Graphics.DrawRectangle(ps, u, v, 16.0f, g); } } else { e.Graphics.DrawRectangle(p, u, v, 16.0f, g); } e.Graphics.DrawLine(p, u, v + (0.5f - 0.5f * threshold) * g, u + 16.0f, v + (0.5f - 0.5f * threshold) * g); e.Graphics.DrawLine(p, u, v + (0.5f + 0.5f * threshold) * g, u + 16.0f, v + (0.5f + 0.5f * threshold) * g); string t = "A" + (j + 1).ToString(Culture); SizeF s = e.Graphics.MeasureString(t, f); e.Graphics.DrawString(t, f, Brushes.Black, u + 0.5f * (16.0f - s.Width), v + g - s.Height - 2.0f); u += 24.0f; } } { /// buttons int n = Sdl.SDL_JoystickNumButtons(Joysticks.AttachedJoysticks[i].SdlHandle); float g = (float)0.5f * (pictureboxJoysticks.ClientRectangle.Height - v - 10.0f); for (int j = 0; j < n; j++) { bool q = Sdl.SDL_JoystickGetButton(Joysticks.AttachedJoysticks[i].SdlHandle, j) != 0; float dv = (float)(j & 1) * (g + 8.0f); if (q) e.Graphics.FillRectangle(Brushes.Firebrick, u, v + dv, g, g); if (device == i & component == Interface.JoystickComponent.Button & element == j) { e.Graphics.DrawRectangle(ps, u, v + dv, g, g); } else { e.Graphics.DrawRectangle(p, u, v + dv, g, g); } string t = "B" + (j + 1).ToString(Culture); SizeF s = e.Graphics.MeasureString(t, f); e.Graphics.DrawString(t, f, Brushes.Black, u + 0.5f * (g - s.Width), v + dv + 0.5f * (g - s.Height)); if ((j & 1) != 0 | j == n - 1) u += g + 8.0f; } } if (u > m) m = u; } } else { m = x + w + 64.0f; } x = m + 8.0f; } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formMain.Designer.cs000066400000000000000000007150271171674032100234120ustar00rootroot00000000000000namespace OpenBve { partial class formMain { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.labelFillerOne = new System.Windows.Forms.Label(); this.pictureboxLogo = new System.Windows.Forms.PictureBox(); this.labelVerticalSeparator = new System.Windows.Forms.Label(); this.buttonClose = new System.Windows.Forms.Button(); this.panelStart = new System.Windows.Forms.Panel(); this.comboboxMode = new System.Windows.Forms.ComboBox(); this.labelMode = new System.Windows.Forms.Label(); this.groupboxTrainSelection = new System.Windows.Forms.GroupBox(); this.tabcontrolTrainSelection = new System.Windows.Forms.TabControl(); this.tabpageTrainManaged = new System.Windows.Forms.TabPage(); this.treeviewTrainAddOns = new System.Windows.Forms.TreeView(); this.textboxTrainFilter = new System.Windows.Forms.TextBox(); this.tabpageTrainBrowse = new System.Windows.Forms.TabPage(); this.listviewTrainFolders = new System.Windows.Forms.ListView(); this.textboxTrainFolder = new System.Windows.Forms.TextBox(); this.tabpageTrainRecently = new System.Windows.Forms.TabPage(); this.listviewTrainRecently = new System.Windows.Forms.ListView(); this.tabpageTrainDefault = new System.Windows.Forms.TabPage(); this.checkboxTrainDefault = new System.Windows.Forms.CheckBox(); this.groupboxTrainDetails = new System.Windows.Forms.GroupBox(); this.tabcontrolTrainDetails = new System.Windows.Forms.TabControl(); this.tabpageTrainDescription = new System.Windows.Forms.TabPage(); this.textboxTrainDescription = new System.Windows.Forms.TextBox(); this.pictureboxTrainImage = new System.Windows.Forms.PictureBox(); this.tabpageTrainSettings = new System.Windows.Forms.TabPage(); this.panelTrainEncoding = new System.Windows.Forms.Panel(); this.labelTrainEncoding = new System.Windows.Forms.Label(); this.buttonTrainEncodingBig5 = new System.Windows.Forms.Button(); this.comboboxTrainEncoding = new System.Windows.Forms.ComboBox(); this.buttonTrainEncodingShiftJis = new System.Windows.Forms.Button(); this.buttonTrainEncodingLatin1 = new System.Windows.Forms.Button(); this.labelTrainEncodingPreview = new System.Windows.Forms.Label(); this.textboxTrainEncodingPreview = new System.Windows.Forms.TextBox(); this.buttonStart = new System.Windows.Forms.Button(); this.labelStart = new System.Windows.Forms.Label(); this.labelTrain = new System.Windows.Forms.Label(); this.groupboxRouteSelection = new System.Windows.Forms.GroupBox(); this.tabcontrolRouteSelection = new System.Windows.Forms.TabControl(); this.tabpageRouteManaged = new System.Windows.Forms.TabPage(); this.treeviewRouteAddOns = new System.Windows.Forms.TreeView(); this.textboxRouteFilter = new System.Windows.Forms.TextBox(); this.tabpageRouteBrowse = new System.Windows.Forms.TabPage(); this.listviewRouteFiles = new System.Windows.Forms.ListView(); this.textboxRouteFolder = new System.Windows.Forms.TextBox(); this.tabpageRouteRecently = new System.Windows.Forms.TabPage(); this.listviewRouteRecently = new System.Windows.Forms.ListView(); this.groupboxRouteDetails = new System.Windows.Forms.GroupBox(); this.tabcontrolRouteDetails = new System.Windows.Forms.TabControl(); this.tabpageRouteDescription = new System.Windows.Forms.TabPage(); this.textboxRouteDescription = new System.Windows.Forms.TextBox(); this.pictureboxRouteImage = new System.Windows.Forms.PictureBox(); this.tabpageRouteMap = new System.Windows.Forms.TabPage(); this.pictureboxRouteMap = new System.Windows.Forms.PictureBox(); this.tabpageRouteGradient = new System.Windows.Forms.TabPage(); this.pictureboxRouteGradient = new System.Windows.Forms.PictureBox(); this.tabpageRouteSettings = new System.Windows.Forms.TabPage(); this.panelRouteEncoding = new System.Windows.Forms.Panel(); this.buttonRouteEncodingLatin1 = new System.Windows.Forms.Button(); this.buttonRouteEncodingBig5 = new System.Windows.Forms.Button(); this.labelRouteEncoding = new System.Windows.Forms.Label(); this.buttonRouteEncodingShiftJis = new System.Windows.Forms.Button(); this.comboboxRouteEncoding = new System.Windows.Forms.ComboBox(); this.labelRouteEncodingPreview = new System.Windows.Forms.Label(); this.textboxRouteEncodingPreview = new System.Windows.Forms.TextBox(); this.labelRoute = new System.Windows.Forms.Label(); this.labelStartTitleSeparator = new System.Windows.Forms.Label(); this.labelStartTitle = new System.Windows.Forms.Label(); this.labelStartTitleBackground = new System.Windows.Forms.Label(); this.labelFillerTwo = new System.Windows.Forms.Label(); this.panelOptions = new System.Windows.Forms.Panel(); this.panelOptionsRight = new System.Windows.Forms.Panel(); this.groupboxDistance = new System.Windows.Forms.GroupBox(); this.comboboxMotionBlur = new System.Windows.Forms.ComboBox(); this.labelMotionBlur = new System.Windows.Forms.Label(); this.labelDistanceUnit = new System.Windows.Forms.Label(); this.updownDistance = new System.Windows.Forms.NumericUpDown(); this.labelDistance = new System.Windows.Forms.Label(); this.groupboxControls = new System.Windows.Forms.GroupBox(); this.trackbarJoystickAxisThreshold = new System.Windows.Forms.TrackBar(); this.checkboxJoysticksUsed = new System.Windows.Forms.CheckBox(); this.labelJoystickAxisThreshold = new System.Windows.Forms.Label(); this.groupboxVerbosity = new System.Windows.Forms.GroupBox(); this.checkboxErrorMessages = new System.Windows.Forms.CheckBox(); this.checkboxWarningMessages = new System.Windows.Forms.CheckBox(); this.groupboxSimulation = new System.Windows.Forms.GroupBox(); this.checkboxBlackBox = new System.Windows.Forms.CheckBox(); this.checkboxDerailments = new System.Windows.Forms.CheckBox(); this.checkboxCollisions = new System.Windows.Forms.CheckBox(); this.checkboxToppling = new System.Windows.Forms.CheckBox(); this.groupboxSound = new System.Windows.Forms.GroupBox(); this.updownSoundNumber = new System.Windows.Forms.NumericUpDown(); this.comboboxSoundRange = new System.Windows.Forms.ComboBox(); this.labelSoundRange = new System.Windows.Forms.Label(); this.labelSoundNumber = new System.Windows.Forms.Label(); this.panelOptionsLeft = new System.Windows.Forms.Panel(); this.groupboxDisplayMode = new System.Windows.Forms.GroupBox(); this.comboboxVSync = new System.Windows.Forms.ComboBox(); this.labelVSync = new System.Windows.Forms.Label(); this.radiobuttonFullscreen = new System.Windows.Forms.RadioButton(); this.radiobuttonWindow = new System.Windows.Forms.RadioButton(); this.groupboxWindow = new System.Windows.Forms.GroupBox(); this.updownWindowHeight = new System.Windows.Forms.NumericUpDown(); this.labelWindowHeight = new System.Windows.Forms.Label(); this.updownWindowWidth = new System.Windows.Forms.NumericUpDown(); this.labelWindowWidth = new System.Windows.Forms.Label(); this.groupboxFullscreen = new System.Windows.Forms.GroupBox(); this.comboboxFullscreenBits = new System.Windows.Forms.ComboBox(); this.labelFullscreenBits = new System.Windows.Forms.Label(); this.updownFullscreenHeight = new System.Windows.Forms.NumericUpDown(); this.labelFullscreenHeight = new System.Windows.Forms.Label(); this.updownFullscreenWidth = new System.Windows.Forms.NumericUpDown(); this.labelFullscreenWidth = new System.Windows.Forms.Label(); this.groupboxInterpolation = new System.Windows.Forms.GroupBox(); this.labelTransparencyQuality = new System.Windows.Forms.Label(); this.labelTransparencyPerformance = new System.Windows.Forms.Label(); this.labelTransparency = new System.Windows.Forms.Label(); this.updownAnisotropic = new System.Windows.Forms.NumericUpDown(); this.labelAnisotropic = new System.Windows.Forms.Label(); this.comboboxInterpolation = new System.Windows.Forms.ComboBox(); this.labelInterpolation = new System.Windows.Forms.Label(); this.trackbarTransparency = new System.Windows.Forms.TrackBar(); this.pictureboxLanguage = new System.Windows.Forms.PictureBox(); this.comboboxLanguages = new System.Windows.Forms.ComboBox(); this.labelOptionsTitleSeparator = new System.Windows.Forms.Label(); this.labelOptionsTitle = new System.Windows.Forms.Label(); this.labelOptionsTitleBackground = new System.Windows.Forms.Label(); this.labelFillerThree = new System.Windows.Forms.Label(); this.panelPanels = new System.Windows.Forms.Panel(); this.labelPanelsBottom = new System.Windows.Forms.Label(); this.radiobuttonGetAddOns = new System.Windows.Forms.RadioButton(); this.radiobuttonOptions = new System.Windows.Forms.RadioButton(); this.radiobuttonControls = new System.Windows.Forms.RadioButton(); this.radiobuttonReview = new System.Windows.Forms.RadioButton(); this.radiobuttonStart = new System.Windows.Forms.RadioButton(); this.labelPanelsTop = new System.Windows.Forms.Label(); this.panelReview = new System.Windows.Forms.Panel(); this.comboboxBlackBoxFormat = new System.Windows.Forms.ComboBox(); this.labelBlackBoxFormat = new System.Windows.Forms.Label(); this.groupboxReviewDateTime = new System.Windows.Forms.GroupBox(); this.labelReviewTimeValue = new System.Windows.Forms.Label(); this.labelReviewTimeCaption = new System.Windows.Forms.Label(); this.labelReviewDateValue = new System.Windows.Forms.Label(); this.labelReviewDateCaption = new System.Windows.Forms.Label(); this.buttonBlackBoxExport = new System.Windows.Forms.Button(); this.labelBlackBox = new System.Windows.Forms.Label(); this.groupboxScore = new System.Windows.Forms.GroupBox(); this.checkboxScorePenalties = new System.Windows.Forms.CheckBox(); this.buttonScoreExport = new System.Windows.Forms.Button(); this.listviewScore = new System.Windows.Forms.ListView(); this.columnheaderScoreTime = new System.Windows.Forms.ColumnHeader(); this.columnheaderScorePosition = new System.Windows.Forms.ColumnHeader(); this.columnheaderScoreValue = new System.Windows.Forms.ColumnHeader(); this.columnheaderScoreCumulative = new System.Windows.Forms.ColumnHeader(); this.columnheaderScoreText = new System.Windows.Forms.ColumnHeader(); this.groupboxReviewTrain = new System.Windows.Forms.GroupBox(); this.labelReviewTrainValue = new System.Windows.Forms.Label(); this.labelReviewTrainCaption = new System.Windows.Forms.Label(); this.groupboxReviewRoute = new System.Windows.Forms.GroupBox(); this.labelReviewRouteValue = new System.Windows.Forms.Label(); this.labelReviewRouteCaption = new System.Windows.Forms.Label(); this.labelConditions = new System.Windows.Forms.Label(); this.groupboxRating = new System.Windows.Forms.GroupBox(); this.labelRatingRatioValue = new System.Windows.Forms.Label(); this.labelRatingModeValue = new System.Windows.Forms.Label(); this.labelRatingModeCaption = new System.Windows.Forms.Label(); this.labelRatingRatioCaption = new System.Windows.Forms.Label(); this.labelRatingMaximumValue = new System.Windows.Forms.Label(); this.labelRatingMaximumCaption = new System.Windows.Forms.Label(); this.labelRatingAchievedValue = new System.Windows.Forms.Label(); this.labelRatingAchievedCaption = new System.Windows.Forms.Label(); this.labelRatingDescription = new System.Windows.Forms.Label(); this.labelRatingColor = new System.Windows.Forms.Label(); this.labelScore = new System.Windows.Forms.Label(); this.labelReviewTitleSeparator = new System.Windows.Forms.Label(); this.labelReviewTitle = new System.Windows.Forms.Label(); this.labelReviewTitleBackground = new System.Windows.Forms.Label(); this.timerEvents = new System.Windows.Forms.Timer(this.components); this.panelControls = new System.Windows.Forms.Panel(); this.buttonControlsExport = new System.Windows.Forms.Button(); this.buttonControlsImport = new System.Windows.Forms.Button(); this.buttonControlDown = new System.Windows.Forms.Button(); this.buttonControlUp = new System.Windows.Forms.Button(); this.buttonControlRemove = new System.Windows.Forms.Button(); this.buttonControlAdd = new System.Windows.Forms.Button(); this.groupboxJoysticks = new System.Windows.Forms.GroupBox(); this.pictureboxJoysticks = new System.Windows.Forms.PictureBox(); this.listviewControls = new System.Windows.Forms.ListView(); this.columnheaderControlsCommands = new System.Windows.Forms.ColumnHeader(); this.columnheaderType = new System.Windows.Forms.ColumnHeader(); this.columnheaderControlsDescription = new System.Windows.Forms.ColumnHeader(); this.columnheaderControlsAssignment = new System.Windows.Forms.ColumnHeader(); this.labelControlsTitleSeparator = new System.Windows.Forms.Label(); this.labelControlsTitle = new System.Windows.Forms.Label(); this.labelControlsTitleBackground = new System.Windows.Forms.Label(); this.groupboxControl = new System.Windows.Forms.GroupBox(); this.comboboxCommand = new System.Windows.Forms.ComboBox(); this.labelCommand = new System.Windows.Forms.Label(); this.panelJoystick = new System.Windows.Forms.Panel(); this.labelJoystickAssignmentCaption = new System.Windows.Forms.Label(); this.textboxJoystickGrab = new System.Windows.Forms.TextBox(); this.labelJoystickAssignmentValue = new System.Windows.Forms.Label(); this.panelKeyboard = new System.Windows.Forms.Panel(); this.comboboxKeyboardKey = new System.Windows.Forms.ComboBox(); this.labelKeyboardKey = new System.Windows.Forms.Label(); this.checkboxKeyboardAlt = new System.Windows.Forms.CheckBox(); this.checkboxKeyboardCtrl = new System.Windows.Forms.CheckBox(); this.checkboxKeyboardShift = new System.Windows.Forms.CheckBox(); this.labelKeyboardModifier = new System.Windows.Forms.Label(); this.radiobuttonJoystick = new System.Windows.Forms.RadioButton(); this.radiobuttonKeyboard = new System.Windows.Forms.RadioButton(); this.panelInfo = new System.Windows.Forms.Panel(); this.labelInfoCenter = new System.Windows.Forms.Label(); this.linkUpdates = new System.Windows.Forms.LinkLabel(); this.linkHomepage = new System.Windows.Forms.LinkLabel(); this.labelVersion = new System.Windows.Forms.Label(); this.labelInfoBottom = new System.Windows.Forms.Label(); this.labelInfoTop = new System.Windows.Forms.Label(); this.panelGetAddOns = new System.Windows.Forms.Panel(); this.panelPackages = new System.Windows.Forms.Panel(); this.checkboxFilterNoWIPs = new System.Windows.Forms.CheckBox(); this.groupboxPackage = new System.Windows.Forms.GroupBox(); this.linkPackageHomepage = new System.Windows.Forms.LinkLabel(); this.buttonScreenshotNext = new System.Windows.Forms.Button(); this.buttonScreenshotPrevious = new System.Windows.Forms.Button(); this.pictureboxScreenshot = new System.Windows.Forms.PictureBox(); this.labelPackageInformation = new System.Windows.Forms.Label(); this.textboxPackageDescription = new System.Windows.Forms.TextBox(); this.buttonPackageInstall = new System.Windows.Forms.Button(); this.buttonPackageRemove = new System.Windows.Forms.Button(); this.treeviewPackages = new System.Windows.Forms.TreeView(); this.checkboxFilterUpdates = new System.Windows.Forms.CheckBox(); this.checkboxFilterSharedLibraries = new System.Windows.Forms.CheckBox(); this.checkboxFilterLibraries = new System.Windows.Forms.CheckBox(); this.checkboxFilterTrains = new System.Windows.Forms.CheckBox(); this.checkboxFilterRoutes = new System.Windows.Forms.CheckBox(); this.textboxFilter = new System.Windows.Forms.TextBox(); this.labelFilter = new System.Windows.Forms.Label(); this.progressbarDownloading = new System.Windows.Forms.ProgressBar(); this.labelDownloading = new System.Windows.Forms.Label(); this.label18 = new System.Windows.Forms.Label(); this.labelGetAddOnsTitle = new System.Windows.Forms.Label(); this.labelGetAddOnsBackground = new System.Windows.Forms.Label(); this.timerInstall = new System.Windows.Forms.Timer(this.components); this.timerFilter = new System.Windows.Forms.Timer(this.components); ((System.ComponentModel.ISupportInitialize)(this.pictureboxLogo)).BeginInit(); this.panelStart.SuspendLayout(); this.groupboxTrainSelection.SuspendLayout(); this.tabcontrolTrainSelection.SuspendLayout(); this.tabpageTrainManaged.SuspendLayout(); this.tabpageTrainBrowse.SuspendLayout(); this.tabpageTrainRecently.SuspendLayout(); this.tabpageTrainDefault.SuspendLayout(); this.groupboxTrainDetails.SuspendLayout(); this.tabcontrolTrainDetails.SuspendLayout(); this.tabpageTrainDescription.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxTrainImage)).BeginInit(); this.tabpageTrainSettings.SuspendLayout(); this.panelTrainEncoding.SuspendLayout(); this.groupboxRouteSelection.SuspendLayout(); this.tabcontrolRouteSelection.SuspendLayout(); this.tabpageRouteManaged.SuspendLayout(); this.tabpageRouteBrowse.SuspendLayout(); this.tabpageRouteRecently.SuspendLayout(); this.groupboxRouteDetails.SuspendLayout(); this.tabcontrolRouteDetails.SuspendLayout(); this.tabpageRouteDescription.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxRouteImage)).BeginInit(); this.tabpageRouteMap.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxRouteMap)).BeginInit(); this.tabpageRouteGradient.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxRouteGradient)).BeginInit(); this.tabpageRouteSettings.SuspendLayout(); this.panelRouteEncoding.SuspendLayout(); this.panelOptions.SuspendLayout(); this.panelOptionsRight.SuspendLayout(); this.groupboxDistance.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.updownDistance)).BeginInit(); this.groupboxControls.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.trackbarJoystickAxisThreshold)).BeginInit(); this.groupboxVerbosity.SuspendLayout(); this.groupboxSimulation.SuspendLayout(); this.groupboxSound.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.updownSoundNumber)).BeginInit(); this.panelOptionsLeft.SuspendLayout(); this.groupboxDisplayMode.SuspendLayout(); this.groupboxWindow.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.updownWindowHeight)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.updownWindowWidth)).BeginInit(); this.groupboxFullscreen.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.updownFullscreenHeight)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.updownFullscreenWidth)).BeginInit(); this.groupboxInterpolation.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.updownAnisotropic)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackbarTransparency)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxLanguage)).BeginInit(); this.panelPanels.SuspendLayout(); this.panelReview.SuspendLayout(); this.groupboxReviewDateTime.SuspendLayout(); this.groupboxScore.SuspendLayout(); this.groupboxReviewTrain.SuspendLayout(); this.groupboxReviewRoute.SuspendLayout(); this.groupboxRating.SuspendLayout(); this.panelControls.SuspendLayout(); this.groupboxJoysticks.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxJoysticks)).BeginInit(); this.groupboxControl.SuspendLayout(); this.panelJoystick.SuspendLayout(); this.panelKeyboard.SuspendLayout(); this.panelInfo.SuspendLayout(); this.panelGetAddOns.SuspendLayout(); this.panelPackages.SuspendLayout(); this.groupboxPackage.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxScreenshot)).BeginInit(); this.SuspendLayout(); // // labelFillerOne // this.labelFillerOne.BackColor = System.Drawing.Color.Silver; this.labelFillerOne.Location = new System.Drawing.Point(0, 0); this.labelFillerOne.Name = "labelFillerOne"; this.labelFillerOne.Size = new System.Drawing.Size(160, 160); this.labelFillerOne.TabIndex = 0; // // pictureboxLogo // this.pictureboxLogo.BackColor = System.Drawing.Color.Silver; this.pictureboxLogo.Location = new System.Drawing.Point(16, 16); this.pictureboxLogo.Name = "pictureboxLogo"; this.pictureboxLogo.Size = new System.Drawing.Size(128, 128); this.pictureboxLogo.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureboxLogo.TabIndex = 1; this.pictureboxLogo.TabStop = false; // // labelVerticalSeparator // this.labelVerticalSeparator.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.labelVerticalSeparator.BackColor = System.Drawing.Color.White; this.labelVerticalSeparator.Location = new System.Drawing.Point(158, 0); this.labelVerticalSeparator.Name = "labelVerticalSeparator"; this.labelVerticalSeparator.Size = new System.Drawing.Size(2, 584); this.labelVerticalSeparator.TabIndex = 9; // // buttonClose // this.buttonClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonClose.AutoEllipsis = true; this.buttonClose.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonClose.Location = new System.Drawing.Point(8, 552); this.buttonClose.Name = "buttonClose"; this.buttonClose.Size = new System.Drawing.Size(144, 24); this.buttonClose.TabIndex = 5; this.buttonClose.Text = "Close"; this.buttonClose.UseVisualStyleBackColor = true; this.buttonClose.Click += new System.EventHandler(this.buttonClose_Click); // // panelStart // this.panelStart.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelStart.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(229)))), ((int)(((byte)(229)))), ((int)(((byte)(255))))); this.panelStart.Controls.Add(this.comboboxMode); this.panelStart.Controls.Add(this.labelMode); this.panelStart.Controls.Add(this.groupboxTrainSelection); this.panelStart.Controls.Add(this.groupboxTrainDetails); this.panelStart.Controls.Add(this.buttonStart); this.panelStart.Controls.Add(this.labelStart); this.panelStart.Controls.Add(this.labelTrain); this.panelStart.Controls.Add(this.groupboxRouteSelection); this.panelStart.Controls.Add(this.groupboxRouteDetails); this.panelStart.Controls.Add(this.labelRoute); this.panelStart.Controls.Add(this.labelStartTitleSeparator); this.panelStart.Controls.Add(this.labelStartTitle); this.panelStart.Controls.Add(this.labelStartTitleBackground); this.panelStart.Location = new System.Drawing.Point(160, 0); this.panelStart.Name = "panelStart"; this.panelStart.Size = new System.Drawing.Size(640, 584); this.panelStart.TabIndex = 7; // // comboboxMode // this.comboboxMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.comboboxMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxMode.FormattingEnabled = true; this.comboboxMode.Location = new System.Drawing.Point(144, 552); this.comboboxMode.Name = "comboboxMode"; this.comboboxMode.Size = new System.Drawing.Size(144, 21); this.comboboxMode.TabIndex = 11; // // labelMode // this.labelMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelMode.AutoEllipsis = true; this.labelMode.ForeColor = System.Drawing.Color.Black; this.labelMode.Location = new System.Drawing.Point(16, 552); this.labelMode.Name = "labelMode"; this.labelMode.Size = new System.Drawing.Size(128, 16); this.labelMode.TabIndex = 10; this.labelMode.Text = "Mode of driving:"; this.labelMode.TextAlign = System.Drawing.ContentAlignment.TopRight; // // groupboxTrainSelection // this.groupboxTrainSelection.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.groupboxTrainSelection.Controls.Add(this.tabcontrolTrainSelection); this.groupboxTrainSelection.ForeColor = System.Drawing.Color.Black; this.groupboxTrainSelection.Location = new System.Drawing.Point(8, 312); this.groupboxTrainSelection.Name = "groupboxTrainSelection"; this.groupboxTrainSelection.Size = new System.Drawing.Size(316, 200); this.groupboxTrainSelection.TabIndex = 7; this.groupboxTrainSelection.TabStop = false; this.groupboxTrainSelection.Text = "Selection"; // // tabcontrolTrainSelection // this.tabcontrolTrainSelection.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.tabcontrolTrainSelection.Controls.Add(this.tabpageTrainManaged); this.tabcontrolTrainSelection.Controls.Add(this.tabpageTrainBrowse); this.tabcontrolTrainSelection.Controls.Add(this.tabpageTrainRecently); this.tabcontrolTrainSelection.Controls.Add(this.tabpageTrainDefault); this.tabcontrolTrainSelection.Location = new System.Drawing.Point(8, 16); this.tabcontrolTrainSelection.Name = "tabcontrolTrainSelection"; this.tabcontrolTrainSelection.SelectedIndex = 0; this.tabcontrolTrainSelection.Size = new System.Drawing.Size(300, 176); this.tabcontrolTrainSelection.TabIndex = 0; // // tabpageTrainManaged // this.tabpageTrainManaged.Controls.Add(this.treeviewTrainAddOns); this.tabpageTrainManaged.Controls.Add(this.textboxTrainFilter); this.tabpageTrainManaged.Location = new System.Drawing.Point(4, 22); this.tabpageTrainManaged.Name = "tabpageTrainManaged"; this.tabpageTrainManaged.Size = new System.Drawing.Size(292, 150); this.tabpageTrainManaged.TabIndex = 3; this.tabpageTrainManaged.Text = "Add-ons"; this.tabpageTrainManaged.UseVisualStyleBackColor = true; // // treeviewTrainAddOns // this.treeviewTrainAddOns.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.treeviewTrainAddOns.HideSelection = false; this.treeviewTrainAddOns.Location = new System.Drawing.Point(7, 31); this.treeviewTrainAddOns.Name = "treeviewTrainAddOns"; this.treeviewTrainAddOns.Size = new System.Drawing.Size(278, 112); this.treeviewTrainAddOns.TabIndex = 10; this.treeviewTrainAddOns.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.TreeviewTrainAddOnsAfterSelect); // // textboxTrainFilter // this.textboxTrainFilter.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxTrainFilter.Location = new System.Drawing.Point(7, 7); this.textboxTrainFilter.Name = "textboxTrainFilter"; this.textboxTrainFilter.Size = new System.Drawing.Size(276, 20); this.textboxTrainFilter.TabIndex = 9; this.textboxTrainFilter.TextChanged += new System.EventHandler(this.TextboxTrainFilterTextChanged); // // tabpageTrainBrowse // this.tabpageTrainBrowse.Controls.Add(this.listviewTrainFolders); this.tabpageTrainBrowse.Controls.Add(this.textboxTrainFolder); this.tabpageTrainBrowse.Location = new System.Drawing.Point(4, 22); this.tabpageTrainBrowse.Name = "tabpageTrainBrowse"; this.tabpageTrainBrowse.Padding = new System.Windows.Forms.Padding(3); this.tabpageTrainBrowse.Size = new System.Drawing.Size(292, 150); this.tabpageTrainBrowse.TabIndex = 0; this.tabpageTrainBrowse.Text = "File browser"; this.tabpageTrainBrowse.UseVisualStyleBackColor = true; // // listviewTrainFolders // this.listviewTrainFolders.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.listviewTrainFolders.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; this.listviewTrainFolders.HideSelection = false; this.listviewTrainFolders.LabelWrap = false; this.listviewTrainFolders.Location = new System.Drawing.Point(8, 32); this.listviewTrainFolders.MultiSelect = false; this.listviewTrainFolders.Name = "listviewTrainFolders"; this.listviewTrainFolders.ShowGroups = false; this.listviewTrainFolders.Size = new System.Drawing.Size(276, 111); this.listviewTrainFolders.TabIndex = 1; this.listviewTrainFolders.UseCompatibleStateImageBehavior = false; this.listviewTrainFolders.View = System.Windows.Forms.View.Details; this.listviewTrainFolders.SelectedIndexChanged += new System.EventHandler(this.listviewTrainFolders_SelectedIndexChanged); this.listviewTrainFolders.DoubleClick += new System.EventHandler(this.listviewTrainFolders_DoubleClick); this.listviewTrainFolders.KeyDown += new System.Windows.Forms.KeyEventHandler(this.listviewTrainFolders_KeyDown); // // textboxTrainFolder // this.textboxTrainFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxTrainFolder.Location = new System.Drawing.Point(8, 8); this.textboxTrainFolder.Name = "textboxTrainFolder"; this.textboxTrainFolder.Size = new System.Drawing.Size(276, 20); this.textboxTrainFolder.TabIndex = 0; this.textboxTrainFolder.TextChanged += new System.EventHandler(this.textboxTrainFolder_TextChanged); // // tabpageTrainRecently // this.tabpageTrainRecently.Controls.Add(this.listviewTrainRecently); this.tabpageTrainRecently.Location = new System.Drawing.Point(4, 22); this.tabpageTrainRecently.Name = "tabpageTrainRecently"; this.tabpageTrainRecently.Padding = new System.Windows.Forms.Padding(3); this.tabpageTrainRecently.Size = new System.Drawing.Size(292, 150); this.tabpageTrainRecently.TabIndex = 1; this.tabpageTrainRecently.Text = "Recently used"; this.tabpageTrainRecently.UseVisualStyleBackColor = true; // // listviewTrainRecently // this.listviewTrainRecently.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.listviewTrainRecently.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; this.listviewTrainRecently.HideSelection = false; this.listviewTrainRecently.LabelWrap = false; this.listviewTrainRecently.Location = new System.Drawing.Point(8, 8); this.listviewTrainRecently.MultiSelect = false; this.listviewTrainRecently.Name = "listviewTrainRecently"; this.listviewTrainRecently.ShowGroups = false; this.listviewTrainRecently.Size = new System.Drawing.Size(276, 136); this.listviewTrainRecently.TabIndex = 0; this.listviewTrainRecently.UseCompatibleStateImageBehavior = false; this.listviewTrainRecently.View = System.Windows.Forms.View.Details; this.listviewTrainRecently.SelectedIndexChanged += new System.EventHandler(this.listviewTrainRecently_SelectedIndexChanged); // // tabpageTrainDefault // this.tabpageTrainDefault.Controls.Add(this.checkboxTrainDefault); this.tabpageTrainDefault.Location = new System.Drawing.Point(4, 22); this.tabpageTrainDefault.Name = "tabpageTrainDefault"; this.tabpageTrainDefault.Padding = new System.Windows.Forms.Padding(3); this.tabpageTrainDefault.Size = new System.Drawing.Size(292, 150); this.tabpageTrainDefault.TabIndex = 2; this.tabpageTrainDefault.Text = "Route default"; this.tabpageTrainDefault.UseVisualStyleBackColor = true; // // checkboxTrainDefault // this.checkboxTrainDefault.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.checkboxTrainDefault.AutoSize = true; this.checkboxTrainDefault.Checked = true; this.checkboxTrainDefault.CheckState = System.Windows.Forms.CheckState.Checked; this.checkboxTrainDefault.ForeColor = System.Drawing.SystemColors.ControlText; this.checkboxTrainDefault.Location = new System.Drawing.Point(8, 8); this.checkboxTrainDefault.Name = "checkboxTrainDefault"; this.checkboxTrainDefault.Size = new System.Drawing.Size(211, 17); this.checkboxTrainDefault.TabIndex = 1; this.checkboxTrainDefault.Text = "Use the default train the route suggests"; this.checkboxTrainDefault.UseVisualStyleBackColor = true; this.checkboxTrainDefault.CheckedChanged += new System.EventHandler(this.CheckboxTrainDefaultCheckedChanged); // // groupboxTrainDetails // this.groupboxTrainDetails.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.groupboxTrainDetails.Controls.Add(this.tabcontrolTrainDetails); this.groupboxTrainDetails.ForeColor = System.Drawing.Color.Black; this.groupboxTrainDetails.Location = new System.Drawing.Point(332, 312); this.groupboxTrainDetails.Name = "groupboxTrainDetails"; this.groupboxTrainDetails.Size = new System.Drawing.Size(316, 200); this.groupboxTrainDetails.TabIndex = 8; this.groupboxTrainDetails.TabStop = false; this.groupboxTrainDetails.Text = "Details"; this.groupboxTrainDetails.Visible = false; // // tabcontrolTrainDetails // this.tabcontrolTrainDetails.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.tabcontrolTrainDetails.Controls.Add(this.tabpageTrainDescription); this.tabcontrolTrainDetails.Controls.Add(this.tabpageTrainSettings); this.tabcontrolTrainDetails.Location = new System.Drawing.Point(8, 16); this.tabcontrolTrainDetails.Name = "tabcontrolTrainDetails"; this.tabcontrolTrainDetails.SelectedIndex = 0; this.tabcontrolTrainDetails.Size = new System.Drawing.Size(300, 176); this.tabcontrolTrainDetails.TabIndex = 0; // // tabpageTrainDescription // this.tabpageTrainDescription.Controls.Add(this.textboxTrainDescription); this.tabpageTrainDescription.Controls.Add(this.pictureboxTrainImage); this.tabpageTrainDescription.Location = new System.Drawing.Point(4, 22); this.tabpageTrainDescription.Name = "tabpageTrainDescription"; this.tabpageTrainDescription.Padding = new System.Windows.Forms.Padding(3); this.tabpageTrainDescription.Size = new System.Drawing.Size(292, 150); this.tabpageTrainDescription.TabIndex = 0; this.tabpageTrainDescription.Text = "Description"; this.tabpageTrainDescription.UseVisualStyleBackColor = true; // // textboxTrainDescription // this.textboxTrainDescription.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxTrainDescription.BackColor = System.Drawing.SystemColors.Window; this.textboxTrainDescription.Location = new System.Drawing.Point(168, 8); this.textboxTrainDescription.Multiline = true; this.textboxTrainDescription.Name = "textboxTrainDescription"; this.textboxTrainDescription.ReadOnly = true; this.textboxTrainDescription.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textboxTrainDescription.Size = new System.Drawing.Size(116, 136); this.textboxTrainDescription.TabIndex = 0; // // pictureboxTrainImage // this.pictureboxTrainImage.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.pictureboxTrainImage.Cursor = System.Windows.Forms.Cursors.Hand; this.pictureboxTrainImage.Location = new System.Drawing.Point(8, 8); this.pictureboxTrainImage.Name = "pictureboxTrainImage"; this.pictureboxTrainImage.Size = new System.Drawing.Size(152, 136); this.pictureboxTrainImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureboxTrainImage.TabIndex = 0; this.pictureboxTrainImage.TabStop = false; this.pictureboxTrainImage.Click += new System.EventHandler(this.pictureboxTrainImage_Click); // // tabpageTrainSettings // this.tabpageTrainSettings.Controls.Add(this.panelTrainEncoding); this.tabpageTrainSettings.Controls.Add(this.labelTrainEncodingPreview); this.tabpageTrainSettings.Controls.Add(this.textboxTrainEncodingPreview); this.tabpageTrainSettings.Location = new System.Drawing.Point(4, 22); this.tabpageTrainSettings.Name = "tabpageTrainSettings"; this.tabpageTrainSettings.Padding = new System.Windows.Forms.Padding(3); this.tabpageTrainSettings.Size = new System.Drawing.Size(292, 150); this.tabpageTrainSettings.TabIndex = 3; this.tabpageTrainSettings.Text = "Settings"; this.tabpageTrainSettings.UseVisualStyleBackColor = true; // // panelTrainEncoding // this.panelTrainEncoding.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelTrainEncoding.Controls.Add(this.labelTrainEncoding); this.panelTrainEncoding.Controls.Add(this.buttonTrainEncodingBig5); this.panelTrainEncoding.Controls.Add(this.comboboxTrainEncoding); this.panelTrainEncoding.Controls.Add(this.buttonTrainEncodingShiftJis); this.panelTrainEncoding.Controls.Add(this.buttonTrainEncodingLatin1); this.panelTrainEncoding.Location = new System.Drawing.Point(8, 8); this.panelTrainEncoding.Name = "panelTrainEncoding"; this.panelTrainEncoding.Size = new System.Drawing.Size(276, 48); this.panelTrainEncoding.TabIndex = 10; // // labelTrainEncoding // this.labelTrainEncoding.AutoEllipsis = true; this.labelTrainEncoding.ForeColor = System.Drawing.SystemColors.ControlText; this.labelTrainEncoding.Location = new System.Drawing.Point(0, 0); this.labelTrainEncoding.Name = "labelTrainEncoding"; this.labelTrainEncoding.Size = new System.Drawing.Size(96, 16); this.labelTrainEncoding.TabIndex = 0; this.labelTrainEncoding.Text = "Encoding:"; this.labelTrainEncoding.TextAlign = System.Drawing.ContentAlignment.TopRight; // // buttonTrainEncodingBig5 // this.buttonTrainEncodingBig5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonTrainEncodingBig5.ForeColor = System.Drawing.SystemColors.ControlText; this.buttonTrainEncodingBig5.Location = new System.Drawing.Point(212, 24); this.buttonTrainEncodingBig5.Name = "buttonTrainEncodingBig5"; this.buttonTrainEncodingBig5.Size = new System.Drawing.Size(64, 24); this.buttonTrainEncodingBig5.TabIndex = 9; this.buttonTrainEncodingBig5.Text = "Big5"; this.buttonTrainEncodingBig5.UseVisualStyleBackColor = true; this.buttonTrainEncodingBig5.Click += new System.EventHandler(this.buttonTrainEncodingBig5_Click); // // comboboxTrainEncoding // this.comboboxTrainEncoding.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.comboboxTrainEncoding.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxTrainEncoding.FormattingEnabled = true; this.comboboxTrainEncoding.Location = new System.Drawing.Point(96, 0); this.comboboxTrainEncoding.Name = "comboboxTrainEncoding"; this.comboboxTrainEncoding.Size = new System.Drawing.Size(180, 21); this.comboboxTrainEncoding.TabIndex = 1; this.comboboxTrainEncoding.SelectedIndexChanged += new System.EventHandler(this.comboboxTrainEncoding_SelectedIndexChanged); // // buttonTrainEncodingShiftJis // this.buttonTrainEncodingShiftJis.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonTrainEncodingShiftJis.ForeColor = System.Drawing.SystemColors.ControlText; this.buttonTrainEncodingShiftJis.Location = new System.Drawing.Point(148, 24); this.buttonTrainEncodingShiftJis.Name = "buttonTrainEncodingShiftJis"; this.buttonTrainEncodingShiftJis.Size = new System.Drawing.Size(64, 24); this.buttonTrainEncodingShiftJis.TabIndex = 3; this.buttonTrainEncodingShiftJis.Text = "Shift_JIS"; this.buttonTrainEncodingShiftJis.UseVisualStyleBackColor = true; this.buttonTrainEncodingShiftJis.Click += new System.EventHandler(this.buttonTrainEncodingShiftJis_Click); // // buttonTrainEncodingLatin1 // this.buttonTrainEncodingLatin1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonTrainEncodingLatin1.ForeColor = System.Drawing.SystemColors.ControlText; this.buttonTrainEncodingLatin1.Location = new System.Drawing.Point(84, 24); this.buttonTrainEncodingLatin1.Name = "buttonTrainEncodingLatin1"; this.buttonTrainEncodingLatin1.Size = new System.Drawing.Size(64, 24); this.buttonTrainEncodingLatin1.TabIndex = 2; this.buttonTrainEncodingLatin1.Text = "Latin-1"; this.buttonTrainEncodingLatin1.UseVisualStyleBackColor = true; this.buttonTrainEncodingLatin1.Click += new System.EventHandler(this.buttonTrainEncodingLatin1_Click); // // labelTrainEncodingPreview // this.labelTrainEncodingPreview.AutoEllipsis = true; this.labelTrainEncodingPreview.ForeColor = System.Drawing.SystemColors.ControlText; this.labelTrainEncodingPreview.Location = new System.Drawing.Point(8, 64); this.labelTrainEncodingPreview.Name = "labelTrainEncodingPreview"; this.labelTrainEncodingPreview.Size = new System.Drawing.Size(96, 16); this.labelTrainEncodingPreview.TabIndex = 4; this.labelTrainEncodingPreview.Text = "Preview:"; this.labelTrainEncodingPreview.TextAlign = System.Drawing.ContentAlignment.TopRight; // // textboxTrainEncodingPreview // this.textboxTrainEncodingPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxTrainEncodingPreview.BackColor = System.Drawing.SystemColors.Window; this.textboxTrainEncodingPreview.Location = new System.Drawing.Point(104, 64); this.textboxTrainEncodingPreview.Multiline = true; this.textboxTrainEncodingPreview.Name = "textboxTrainEncodingPreview"; this.textboxTrainEncodingPreview.ReadOnly = true; this.textboxTrainEncodingPreview.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textboxTrainEncodingPreview.Size = new System.Drawing.Size(180, 80); this.textboxTrainEncodingPreview.TabIndex = 5; // // buttonStart // this.buttonStart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonStart.AutoEllipsis = true; this.buttonStart.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonStart.Enabled = false; this.buttonStart.Location = new System.Drawing.Point(512, 552); this.buttonStart.Name = "buttonStart"; this.buttonStart.Size = new System.Drawing.Size(120, 24); this.buttonStart.TabIndex = 12; this.buttonStart.Text = "Start"; this.buttonStart.UseVisualStyleBackColor = true; this.buttonStart.Click += new System.EventHandler(this.buttonStart_Click); // // labelStart // this.labelStart.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelStart.AutoEllipsis = true; this.labelStart.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(130)))), ((int)(((byte)(153))))); this.labelStart.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelStart.ForeColor = System.Drawing.Color.White; this.labelStart.Location = new System.Drawing.Point(7, 520); this.labelStart.Name = "labelStart"; this.labelStart.Size = new System.Drawing.Size(625, 24); this.labelStart.TabIndex = 9; this.labelStart.Text = "Start"; this.labelStart.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // labelTrain // this.labelTrain.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelTrain.AutoEllipsis = true; this.labelTrain.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(130)))), ((int)(((byte)(153))))); this.labelTrain.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelTrain.ForeColor = System.Drawing.Color.White; this.labelTrain.Location = new System.Drawing.Point(8, 280); this.labelTrain.Name = "labelTrain"; this.labelTrain.Size = new System.Drawing.Size(624, 24); this.labelTrain.TabIndex = 6; this.labelTrain.Text = "Train"; this.labelTrain.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // groupboxRouteSelection // this.groupboxRouteSelection.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.groupboxRouteSelection.Controls.Add(this.tabcontrolRouteSelection); this.groupboxRouteSelection.ForeColor = System.Drawing.Color.Black; this.groupboxRouteSelection.Location = new System.Drawing.Point(8, 72); this.groupboxRouteSelection.Name = "groupboxRouteSelection"; this.groupboxRouteSelection.Size = new System.Drawing.Size(316, 200); this.groupboxRouteSelection.TabIndex = 4; this.groupboxRouteSelection.TabStop = false; this.groupboxRouteSelection.Text = "Selection"; // // tabcontrolRouteSelection // this.tabcontrolRouteSelection.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.tabcontrolRouteSelection.Controls.Add(this.tabpageRouteManaged); this.tabcontrolRouteSelection.Controls.Add(this.tabpageRouteBrowse); this.tabcontrolRouteSelection.Controls.Add(this.tabpageRouteRecently); this.tabcontrolRouteSelection.Location = new System.Drawing.Point(8, 16); this.tabcontrolRouteSelection.Name = "tabcontrolRouteSelection"; this.tabcontrolRouteSelection.SelectedIndex = 0; this.tabcontrolRouteSelection.Size = new System.Drawing.Size(300, 176); this.tabcontrolRouteSelection.TabIndex = 0; // // tabpageRouteManaged // this.tabpageRouteManaged.Controls.Add(this.treeviewRouteAddOns); this.tabpageRouteManaged.Controls.Add(this.textboxRouteFilter); this.tabpageRouteManaged.Location = new System.Drawing.Point(4, 22); this.tabpageRouteManaged.Name = "tabpageRouteManaged"; this.tabpageRouteManaged.Padding = new System.Windows.Forms.Padding(3); this.tabpageRouteManaged.Size = new System.Drawing.Size(292, 150); this.tabpageRouteManaged.TabIndex = 2; this.tabpageRouteManaged.Text = "Add-ons"; this.tabpageRouteManaged.UseVisualStyleBackColor = true; // // treeviewRouteAddOns // this.treeviewRouteAddOns.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.treeviewRouteAddOns.HideSelection = false; this.treeviewRouteAddOns.Location = new System.Drawing.Point(8, 32); this.treeviewRouteAddOns.Name = "treeviewRouteAddOns"; this.treeviewRouteAddOns.Size = new System.Drawing.Size(278, 112); this.treeviewRouteAddOns.TabIndex = 8; this.treeviewRouteAddOns.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.TreeviewRouteAddOnsAfterSelect); // // textboxRouteFilter // this.textboxRouteFilter.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxRouteFilter.Location = new System.Drawing.Point(8, 8); this.textboxRouteFilter.Name = "textboxRouteFilter"; this.textboxRouteFilter.Size = new System.Drawing.Size(276, 20); this.textboxRouteFilter.TabIndex = 2; this.textboxRouteFilter.TextChanged += new System.EventHandler(this.TextboxRouteFilterTextChanged); // // tabpageRouteBrowse // this.tabpageRouteBrowse.Controls.Add(this.listviewRouteFiles); this.tabpageRouteBrowse.Controls.Add(this.textboxRouteFolder); this.tabpageRouteBrowse.Location = new System.Drawing.Point(4, 22); this.tabpageRouteBrowse.Name = "tabpageRouteBrowse"; this.tabpageRouteBrowse.Padding = new System.Windows.Forms.Padding(3); this.tabpageRouteBrowse.Size = new System.Drawing.Size(292, 150); this.tabpageRouteBrowse.TabIndex = 0; this.tabpageRouteBrowse.Text = "File browser"; this.tabpageRouteBrowse.UseVisualStyleBackColor = true; // // listviewRouteFiles // this.listviewRouteFiles.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.listviewRouteFiles.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; this.listviewRouteFiles.HideSelection = false; this.listviewRouteFiles.LabelWrap = false; this.listviewRouteFiles.Location = new System.Drawing.Point(8, 32); this.listviewRouteFiles.MultiSelect = false; this.listviewRouteFiles.Name = "listviewRouteFiles"; this.listviewRouteFiles.ShowGroups = false; this.listviewRouteFiles.Size = new System.Drawing.Size(276, 111); this.listviewRouteFiles.TabIndex = 1; this.listviewRouteFiles.UseCompatibleStateImageBehavior = false; this.listviewRouteFiles.View = System.Windows.Forms.View.Details; this.listviewRouteFiles.SelectedIndexChanged += new System.EventHandler(this.listviewRouteFiles_SelectedIndexChanged); this.listviewRouteFiles.DoubleClick += new System.EventHandler(this.listviewRouteFiles_DoubleClick); this.listviewRouteFiles.KeyDown += new System.Windows.Forms.KeyEventHandler(this.listviewRouteFiles_KeyDown); // // textboxRouteFolder // this.textboxRouteFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxRouteFolder.Location = new System.Drawing.Point(8, 8); this.textboxRouteFolder.Name = "textboxRouteFolder"; this.textboxRouteFolder.Size = new System.Drawing.Size(280, 20); this.textboxRouteFolder.TabIndex = 0; this.textboxRouteFolder.TextChanged += new System.EventHandler(this.textboxRouteFolder_TextChanged); // // tabpageRouteRecently // this.tabpageRouteRecently.Controls.Add(this.listviewRouteRecently); this.tabpageRouteRecently.Location = new System.Drawing.Point(4, 22); this.tabpageRouteRecently.Name = "tabpageRouteRecently"; this.tabpageRouteRecently.Padding = new System.Windows.Forms.Padding(3); this.tabpageRouteRecently.Size = new System.Drawing.Size(292, 150); this.tabpageRouteRecently.TabIndex = 1; this.tabpageRouteRecently.Text = "Recently used"; this.tabpageRouteRecently.UseVisualStyleBackColor = true; // // listviewRouteRecently // this.listviewRouteRecently.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.listviewRouteRecently.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; this.listviewRouteRecently.HideSelection = false; this.listviewRouteRecently.LabelWrap = false; this.listviewRouteRecently.Location = new System.Drawing.Point(8, 8); this.listviewRouteRecently.MultiSelect = false; this.listviewRouteRecently.Name = "listviewRouteRecently"; this.listviewRouteRecently.ShowGroups = false; this.listviewRouteRecently.Size = new System.Drawing.Size(276, 136); this.listviewRouteRecently.TabIndex = 0; this.listviewRouteRecently.UseCompatibleStateImageBehavior = false; this.listviewRouteRecently.View = System.Windows.Forms.View.Details; this.listviewRouteRecently.SelectedIndexChanged += new System.EventHandler(this.listviewRouteRecently_SelectedIndexChanged); // // groupboxRouteDetails // this.groupboxRouteDetails.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.groupboxRouteDetails.Controls.Add(this.tabcontrolRouteDetails); this.groupboxRouteDetails.ForeColor = System.Drawing.Color.Black; this.groupboxRouteDetails.Location = new System.Drawing.Point(332, 72); this.groupboxRouteDetails.Name = "groupboxRouteDetails"; this.groupboxRouteDetails.Size = new System.Drawing.Size(316, 200); this.groupboxRouteDetails.TabIndex = 5; this.groupboxRouteDetails.TabStop = false; this.groupboxRouteDetails.Text = "Details"; this.groupboxRouteDetails.Visible = false; // // tabcontrolRouteDetails // this.tabcontrolRouteDetails.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.tabcontrolRouteDetails.Controls.Add(this.tabpageRouteDescription); this.tabcontrolRouteDetails.Controls.Add(this.tabpageRouteMap); this.tabcontrolRouteDetails.Controls.Add(this.tabpageRouteGradient); this.tabcontrolRouteDetails.Controls.Add(this.tabpageRouteSettings); this.tabcontrolRouteDetails.Location = new System.Drawing.Point(8, 16); this.tabcontrolRouteDetails.Name = "tabcontrolRouteDetails"; this.tabcontrolRouteDetails.SelectedIndex = 0; this.tabcontrolRouteDetails.Size = new System.Drawing.Size(300, 176); this.tabcontrolRouteDetails.TabIndex = 19; // // tabpageRouteDescription // this.tabpageRouteDescription.Controls.Add(this.textboxRouteDescription); this.tabpageRouteDescription.Controls.Add(this.pictureboxRouteImage); this.tabpageRouteDescription.Location = new System.Drawing.Point(4, 22); this.tabpageRouteDescription.Name = "tabpageRouteDescription"; this.tabpageRouteDescription.Padding = new System.Windows.Forms.Padding(3); this.tabpageRouteDescription.Size = new System.Drawing.Size(292, 150); this.tabpageRouteDescription.TabIndex = 0; this.tabpageRouteDescription.Text = "Description"; this.tabpageRouteDescription.UseVisualStyleBackColor = true; // // textboxRouteDescription // this.textboxRouteDescription.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxRouteDescription.BackColor = System.Drawing.SystemColors.Window; this.textboxRouteDescription.Location = new System.Drawing.Point(168, 8); this.textboxRouteDescription.Multiline = true; this.textboxRouteDescription.Name = "textboxRouteDescription"; this.textboxRouteDescription.ReadOnly = true; this.textboxRouteDescription.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textboxRouteDescription.Size = new System.Drawing.Size(116, 136); this.textboxRouteDescription.TabIndex = 0; // // pictureboxRouteImage // this.pictureboxRouteImage.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.pictureboxRouteImage.Cursor = System.Windows.Forms.Cursors.Hand; this.pictureboxRouteImage.Location = new System.Drawing.Point(8, 8); this.pictureboxRouteImage.Name = "pictureboxRouteImage"; this.pictureboxRouteImage.Size = new System.Drawing.Size(152, 136); this.pictureboxRouteImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureboxRouteImage.TabIndex = 0; this.pictureboxRouteImage.TabStop = false; this.pictureboxRouteImage.Click += new System.EventHandler(this.pictureboxRouteImage_Click); // // tabpageRouteMap // this.tabpageRouteMap.Controls.Add(this.pictureboxRouteMap); this.tabpageRouteMap.Location = new System.Drawing.Point(4, 22); this.tabpageRouteMap.Name = "tabpageRouteMap"; this.tabpageRouteMap.Padding = new System.Windows.Forms.Padding(3); this.tabpageRouteMap.Size = new System.Drawing.Size(292, 150); this.tabpageRouteMap.TabIndex = 1; this.tabpageRouteMap.Text = "Map"; this.tabpageRouteMap.UseVisualStyleBackColor = true; // // pictureboxRouteMap // this.pictureboxRouteMap.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.pictureboxRouteMap.Location = new System.Drawing.Point(8, 8); this.pictureboxRouteMap.Name = "pictureboxRouteMap"; this.pictureboxRouteMap.Size = new System.Drawing.Size(276, 136); this.pictureboxRouteMap.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureboxRouteMap.TabIndex = 0; this.pictureboxRouteMap.TabStop = false; // // tabpageRouteGradient // this.tabpageRouteGradient.Controls.Add(this.pictureboxRouteGradient); this.tabpageRouteGradient.Location = new System.Drawing.Point(4, 22); this.tabpageRouteGradient.Name = "tabpageRouteGradient"; this.tabpageRouteGradient.Padding = new System.Windows.Forms.Padding(3); this.tabpageRouteGradient.Size = new System.Drawing.Size(292, 150); this.tabpageRouteGradient.TabIndex = 2; this.tabpageRouteGradient.Text = "Gradient profile"; this.tabpageRouteGradient.UseVisualStyleBackColor = true; // // pictureboxRouteGradient // this.pictureboxRouteGradient.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.pictureboxRouteGradient.Location = new System.Drawing.Point(8, 7); this.pictureboxRouteGradient.Name = "pictureboxRouteGradient"; this.pictureboxRouteGradient.Size = new System.Drawing.Size(276, 136); this.pictureboxRouteGradient.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureboxRouteGradient.TabIndex = 1; this.pictureboxRouteGradient.TabStop = false; // // tabpageRouteSettings // this.tabpageRouteSettings.Controls.Add(this.panelRouteEncoding); this.tabpageRouteSettings.Controls.Add(this.labelRouteEncodingPreview); this.tabpageRouteSettings.Controls.Add(this.textboxRouteEncodingPreview); this.tabpageRouteSettings.Location = new System.Drawing.Point(4, 22); this.tabpageRouteSettings.Name = "tabpageRouteSettings"; this.tabpageRouteSettings.Padding = new System.Windows.Forms.Padding(3); this.tabpageRouteSettings.Size = new System.Drawing.Size(292, 150); this.tabpageRouteSettings.TabIndex = 3; this.tabpageRouteSettings.Text = "Settings"; this.tabpageRouteSettings.UseVisualStyleBackColor = true; // // panelRouteEncoding // this.panelRouteEncoding.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelRouteEncoding.Controls.Add(this.buttonRouteEncodingLatin1); this.panelRouteEncoding.Controls.Add(this.buttonRouteEncodingBig5); this.panelRouteEncoding.Controls.Add(this.labelRouteEncoding); this.panelRouteEncoding.Controls.Add(this.buttonRouteEncodingShiftJis); this.panelRouteEncoding.Controls.Add(this.comboboxRouteEncoding); this.panelRouteEncoding.ForeColor = System.Drawing.SystemColors.ControlText; this.panelRouteEncoding.Location = new System.Drawing.Point(8, 8); this.panelRouteEncoding.Name = "panelRouteEncoding"; this.panelRouteEncoding.Size = new System.Drawing.Size(276, 48); this.panelRouteEncoding.TabIndex = 7; // // buttonRouteEncodingLatin1 // this.buttonRouteEncodingLatin1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonRouteEncodingLatin1.ForeColor = System.Drawing.SystemColors.ControlText; this.buttonRouteEncodingLatin1.Location = new System.Drawing.Point(84, 24); this.buttonRouteEncodingLatin1.Name = "buttonRouteEncodingLatin1"; this.buttonRouteEncodingLatin1.Size = new System.Drawing.Size(64, 24); this.buttonRouteEncodingLatin1.TabIndex = 2; this.buttonRouteEncodingLatin1.Text = "Latin-1"; this.buttonRouteEncodingLatin1.UseVisualStyleBackColor = true; this.buttonRouteEncodingLatin1.Click += new System.EventHandler(this.buttonRouteEncodingLatin1_Click); // // buttonRouteEncodingBig5 // this.buttonRouteEncodingBig5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonRouteEncodingBig5.ForeColor = System.Drawing.SystemColors.ControlText; this.buttonRouteEncodingBig5.Location = new System.Drawing.Point(212, 24); this.buttonRouteEncodingBig5.Name = "buttonRouteEncodingBig5"; this.buttonRouteEncodingBig5.Size = new System.Drawing.Size(64, 24); this.buttonRouteEncodingBig5.TabIndex = 4; this.buttonRouteEncodingBig5.Text = "Big5"; this.buttonRouteEncodingBig5.UseVisualStyleBackColor = true; this.buttonRouteEncodingBig5.Click += new System.EventHandler(this.buttonRouteEncodingBig5_Click); // // labelRouteEncoding // this.labelRouteEncoding.AutoEllipsis = true; this.labelRouteEncoding.ForeColor = System.Drawing.SystemColors.ControlText; this.labelRouteEncoding.Location = new System.Drawing.Point(0, 0); this.labelRouteEncoding.Name = "labelRouteEncoding"; this.labelRouteEncoding.Size = new System.Drawing.Size(96, 16); this.labelRouteEncoding.TabIndex = 0; this.labelRouteEncoding.Text = "Encoding:"; this.labelRouteEncoding.TextAlign = System.Drawing.ContentAlignment.TopRight; // // buttonRouteEncodingShiftJis // this.buttonRouteEncodingShiftJis.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonRouteEncodingShiftJis.ForeColor = System.Drawing.SystemColors.ControlText; this.buttonRouteEncodingShiftJis.Location = new System.Drawing.Point(148, 24); this.buttonRouteEncodingShiftJis.Name = "buttonRouteEncodingShiftJis"; this.buttonRouteEncodingShiftJis.Size = new System.Drawing.Size(64, 24); this.buttonRouteEncodingShiftJis.TabIndex = 3; this.buttonRouteEncodingShiftJis.Text = "Shift_JIS"; this.buttonRouteEncodingShiftJis.UseVisualStyleBackColor = true; this.buttonRouteEncodingShiftJis.Click += new System.EventHandler(this.buttonRouteEncodingShiftJis_Click); // // comboboxRouteEncoding // this.comboboxRouteEncoding.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.comboboxRouteEncoding.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxRouteEncoding.ForeColor = System.Drawing.SystemColors.WindowText; this.comboboxRouteEncoding.FormattingEnabled = true; this.comboboxRouteEncoding.Location = new System.Drawing.Point(96, 0); this.comboboxRouteEncoding.Name = "comboboxRouteEncoding"; this.comboboxRouteEncoding.Size = new System.Drawing.Size(180, 21); this.comboboxRouteEncoding.TabIndex = 1; this.comboboxRouteEncoding.SelectedIndexChanged += new System.EventHandler(this.comboboxRouteEncoding_SelectedIndexChanged); // // labelRouteEncodingPreview // this.labelRouteEncodingPreview.AutoEllipsis = true; this.labelRouteEncodingPreview.ForeColor = System.Drawing.SystemColors.ControlText; this.labelRouteEncodingPreview.Location = new System.Drawing.Point(8, 64); this.labelRouteEncodingPreview.Name = "labelRouteEncodingPreview"; this.labelRouteEncodingPreview.Size = new System.Drawing.Size(96, 16); this.labelRouteEncodingPreview.TabIndex = 5; this.labelRouteEncodingPreview.Text = "Preview:"; this.labelRouteEncodingPreview.TextAlign = System.Drawing.ContentAlignment.TopRight; // // textboxRouteEncodingPreview // this.textboxRouteEncodingPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxRouteEncodingPreview.BackColor = System.Drawing.SystemColors.Window; this.textboxRouteEncodingPreview.ForeColor = System.Drawing.SystemColors.WindowText; this.textboxRouteEncodingPreview.Location = new System.Drawing.Point(104, 64); this.textboxRouteEncodingPreview.Multiline = true; this.textboxRouteEncodingPreview.Name = "textboxRouteEncodingPreview"; this.textboxRouteEncodingPreview.ReadOnly = true; this.textboxRouteEncodingPreview.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textboxRouteEncodingPreview.Size = new System.Drawing.Size(180, 80); this.textboxRouteEncodingPreview.TabIndex = 6; // // labelRoute // this.labelRoute.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelRoute.AutoEllipsis = true; this.labelRoute.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(107)))), ((int)(((byte)(130)))), ((int)(((byte)(153))))); this.labelRoute.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelRoute.ForeColor = System.Drawing.Color.White; this.labelRoute.Location = new System.Drawing.Point(8, 40); this.labelRoute.Name = "labelRoute"; this.labelRoute.Size = new System.Drawing.Size(624, 24); this.labelRoute.TabIndex = 3; this.labelRoute.Text = "Route"; this.labelRoute.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // labelStartTitleSeparator // this.labelStartTitleSeparator.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelStartTitleSeparator.BackColor = System.Drawing.Color.White; this.labelStartTitleSeparator.Location = new System.Drawing.Point(0, 32); this.labelStartTitleSeparator.Name = "labelStartTitleSeparator"; this.labelStartTitleSeparator.Size = new System.Drawing.Size(640, 2); this.labelStartTitleSeparator.TabIndex = 2; // // labelStartTitle // this.labelStartTitle.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelStartTitle.AutoEllipsis = true; this.labelStartTitle.AutoSize = true; this.labelStartTitle.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(54)))), ((int)(((byte)(137)))), ((int)(((byte)(179))))); this.labelStartTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelStartTitle.ForeColor = System.Drawing.Color.White; this.labelStartTitle.Location = new System.Drawing.Point(8, 8); this.labelStartTitle.Name = "labelStartTitle"; this.labelStartTitle.Size = new System.Drawing.Size(100, 16); this.labelStartTitle.TabIndex = 1; this.labelStartTitle.Text = "Start new game"; // // labelStartTitleBackground // this.labelStartTitleBackground.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelStartTitleBackground.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(54)))), ((int)(((byte)(137)))), ((int)(((byte)(179))))); this.labelStartTitleBackground.Location = new System.Drawing.Point(0, 0); this.labelStartTitleBackground.Name = "labelStartTitleBackground"; this.labelStartTitleBackground.Size = new System.Drawing.Size(640, 32); this.labelStartTitleBackground.TabIndex = 0; // // labelFillerTwo // this.labelFillerTwo.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.labelFillerTwo.BackColor = System.Drawing.Color.Silver; this.labelFillerTwo.Location = new System.Drawing.Point(0, 344); this.labelFillerTwo.Name = "labelFillerTwo"; this.labelFillerTwo.Size = new System.Drawing.Size(160, 128); this.labelFillerTwo.TabIndex = 2; // // panelOptions // this.panelOptions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelOptions.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(243)))), ((int)(((byte)(255)))), ((int)(((byte)(243))))); this.panelOptions.Controls.Add(this.panelOptionsRight); this.panelOptions.Controls.Add(this.panelOptionsLeft); this.panelOptions.Controls.Add(this.pictureboxLanguage); this.panelOptions.Controls.Add(this.comboboxLanguages); this.panelOptions.Controls.Add(this.labelOptionsTitleSeparator); this.panelOptions.Controls.Add(this.labelOptionsTitle); this.panelOptions.Controls.Add(this.labelOptionsTitleBackground); this.panelOptions.Location = new System.Drawing.Point(160, 0); this.panelOptions.Name = "panelOptions"; this.panelOptions.Size = new System.Drawing.Size(640, 584); this.panelOptions.TabIndex = 0; // // panelOptionsRight // this.panelOptionsRight.Controls.Add(this.groupboxDistance); this.panelOptionsRight.Controls.Add(this.groupboxControls); this.panelOptionsRight.Controls.Add(this.groupboxVerbosity); this.panelOptionsRight.Controls.Add(this.groupboxSimulation); this.panelOptionsRight.Controls.Add(this.groupboxSound); this.panelOptionsRight.Location = new System.Drawing.Point(332, 72); this.panelOptionsRight.Name = "panelOptionsRight"; this.panelOptionsRight.Size = new System.Drawing.Size(316, 416); this.panelOptionsRight.TabIndex = 17; // // groupboxDistance // this.groupboxDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxDistance.Controls.Add(this.comboboxMotionBlur); this.groupboxDistance.Controls.Add(this.labelMotionBlur); this.groupboxDistance.Controls.Add(this.labelDistanceUnit); this.groupboxDistance.Controls.Add(this.updownDistance); this.groupboxDistance.Controls.Add(this.labelDistance); this.groupboxDistance.ForeColor = System.Drawing.Color.Black; this.groupboxDistance.Location = new System.Drawing.Point(0, 0); this.groupboxDistance.Name = "groupboxDistance"; this.groupboxDistance.Size = new System.Drawing.Size(316, 80); this.groupboxDistance.TabIndex = 8; this.groupboxDistance.TabStop = false; this.groupboxDistance.Text = "Distance effects"; // // comboboxMotionBlur // this.comboboxMotionBlur.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.comboboxMotionBlur.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxMotionBlur.FormattingEnabled = true; this.comboboxMotionBlur.Location = new System.Drawing.Point(156, 48); this.comboboxMotionBlur.Name = "comboboxMotionBlur"; this.comboboxMotionBlur.Size = new System.Drawing.Size(152, 21); this.comboboxMotionBlur.TabIndex = 4; // // labelMotionBlur // this.labelMotionBlur.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelMotionBlur.AutoEllipsis = true; this.labelMotionBlur.Location = new System.Drawing.Point(8, 48); this.labelMotionBlur.Name = "labelMotionBlur"; this.labelMotionBlur.Size = new System.Drawing.Size(148, 16); this.labelMotionBlur.TabIndex = 3; this.labelMotionBlur.Text = "Motion blur:"; this.labelMotionBlur.TextAlign = System.Drawing.ContentAlignment.TopRight; // // labelDistanceUnit // this.labelDistanceUnit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.labelDistanceUnit.AutoEllipsis = true; this.labelDistanceUnit.Location = new System.Drawing.Point(284, 24); this.labelDistanceUnit.Name = "labelDistanceUnit"; this.labelDistanceUnit.Size = new System.Drawing.Size(24, 16); this.labelDistanceUnit.TabIndex = 2; this.labelDistanceUnit.Text = "m"; // // updownDistance // this.updownDistance.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.updownDistance.Location = new System.Drawing.Point(156, 24); this.updownDistance.Maximum = new decimal(new int[] { 100000, 0, 0, 0}); this.updownDistance.Minimum = new decimal(new int[] { 100, 0, 0, 0}); this.updownDistance.Name = "updownDistance"; this.updownDistance.Size = new System.Drawing.Size(128, 20); this.updownDistance.TabIndex = 1; this.updownDistance.Value = new decimal(new int[] { 600, 0, 0, 0}); // // labelDistance // this.labelDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelDistance.AutoEllipsis = true; this.labelDistance.Location = new System.Drawing.Point(8, 24); this.labelDistance.Name = "labelDistance"; this.labelDistance.Size = new System.Drawing.Size(148, 16); this.labelDistance.TabIndex = 0; this.labelDistance.Text = "Viewing distance:"; this.labelDistance.TextAlign = System.Drawing.ContentAlignment.TopRight; // // groupboxControls // this.groupboxControls.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxControls.Controls.Add(this.trackbarJoystickAxisThreshold); this.groupboxControls.Controls.Add(this.checkboxJoysticksUsed); this.groupboxControls.Controls.Add(this.labelJoystickAxisThreshold); this.groupboxControls.ForeColor = System.Drawing.Color.Black; this.groupboxControls.Location = new System.Drawing.Point(0, 176); this.groupboxControls.Name = "groupboxControls"; this.groupboxControls.Size = new System.Drawing.Size(316, 80); this.groupboxControls.TabIndex = 10; this.groupboxControls.TabStop = false; this.groupboxControls.Text = "Controls"; // // trackbarJoystickAxisThreshold // this.trackbarJoystickAxisThreshold.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.trackbarJoystickAxisThreshold.LargeChange = 10; this.trackbarJoystickAxisThreshold.Location = new System.Drawing.Point(200, 32); this.trackbarJoystickAxisThreshold.Maximum = 100; this.trackbarJoystickAxisThreshold.Name = "trackbarJoystickAxisThreshold"; this.trackbarJoystickAxisThreshold.Size = new System.Drawing.Size(108, 42); this.trackbarJoystickAxisThreshold.TabIndex = 2; this.trackbarJoystickAxisThreshold.TickFrequency = 10; this.trackbarJoystickAxisThreshold.TickStyle = System.Windows.Forms.TickStyle.Both; // // checkboxJoysticksUsed // this.checkboxJoysticksUsed.AutoSize = true; this.checkboxJoysticksUsed.Location = new System.Drawing.Point(8, 24); this.checkboxJoysticksUsed.Name = "checkboxJoysticksUsed"; this.checkboxJoysticksUsed.Size = new System.Drawing.Size(110, 17); this.checkboxJoysticksUsed.TabIndex = 0; this.checkboxJoysticksUsed.Text = "Joysticks enabled"; this.checkboxJoysticksUsed.UseVisualStyleBackColor = true; this.checkboxJoysticksUsed.CheckedChanged += new System.EventHandler(this.checkboxJoysticksUsed_CheckedChanged); // // labelJoystickAxisThreshold // this.labelJoystickAxisThreshold.AutoEllipsis = true; this.labelJoystickAxisThreshold.Location = new System.Drawing.Point(8, 48); this.labelJoystickAxisThreshold.Name = "labelJoystickAxisThreshold"; this.labelJoystickAxisThreshold.Size = new System.Drawing.Size(184, 16); this.labelJoystickAxisThreshold.TabIndex = 1; this.labelJoystickAxisThreshold.Text = "Joystick threshold:"; this.labelJoystickAxisThreshold.TextAlign = System.Drawing.ContentAlignment.TopRight; // // groupboxVerbosity // this.groupboxVerbosity.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxVerbosity.Controls.Add(this.checkboxErrorMessages); this.groupboxVerbosity.Controls.Add(this.checkboxWarningMessages); this.groupboxVerbosity.ForeColor = System.Drawing.Color.Black; this.groupboxVerbosity.Location = new System.Drawing.Point(0, 352); this.groupboxVerbosity.Name = "groupboxVerbosity"; this.groupboxVerbosity.Size = new System.Drawing.Size(316, 64); this.groupboxVerbosity.TabIndex = 12; this.groupboxVerbosity.TabStop = false; this.groupboxVerbosity.Text = "Verbosity"; // // checkboxErrorMessages // this.checkboxErrorMessages.AutoSize = true; this.checkboxErrorMessages.Location = new System.Drawing.Point(8, 40); this.checkboxErrorMessages.Name = "checkboxErrorMessages"; this.checkboxErrorMessages.Size = new System.Drawing.Size(127, 17); this.checkboxErrorMessages.TabIndex = 1; this.checkboxErrorMessages.Text = "Show error messages"; this.checkboxErrorMessages.UseVisualStyleBackColor = true; // // checkboxWarningMessages // this.checkboxWarningMessages.AutoSize = true; this.checkboxWarningMessages.Location = new System.Drawing.Point(8, 24); this.checkboxWarningMessages.Name = "checkboxWarningMessages"; this.checkboxWarningMessages.Size = new System.Drawing.Size(143, 17); this.checkboxWarningMessages.TabIndex = 0; this.checkboxWarningMessages.Text = "Show warning messages"; this.checkboxWarningMessages.UseVisualStyleBackColor = true; // // groupboxSimulation // this.groupboxSimulation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxSimulation.Controls.Add(this.checkboxBlackBox); this.groupboxSimulation.Controls.Add(this.checkboxDerailments); this.groupboxSimulation.Controls.Add(this.checkboxCollisions); this.groupboxSimulation.Controls.Add(this.checkboxToppling); this.groupboxSimulation.ForeColor = System.Drawing.Color.Black; this.groupboxSimulation.Location = new System.Drawing.Point(0, 264); this.groupboxSimulation.Name = "groupboxSimulation"; this.groupboxSimulation.Size = new System.Drawing.Size(316, 80); this.groupboxSimulation.TabIndex = 11; this.groupboxSimulation.TabStop = false; this.groupboxSimulation.Text = "Detail of simulation"; // // checkboxBlackBox // this.checkboxBlackBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.checkboxBlackBox.AutoEllipsis = true; this.checkboxBlackBox.CheckAlign = System.Drawing.ContentAlignment.TopLeft; this.checkboxBlackBox.Location = new System.Drawing.Point(176, 24); this.checkboxBlackBox.Name = "checkboxBlackBox"; this.checkboxBlackBox.Size = new System.Drawing.Size(136, 48); this.checkboxBlackBox.TabIndex = 3; this.checkboxBlackBox.Text = "Enable black box"; this.checkboxBlackBox.TextAlign = System.Drawing.ContentAlignment.TopLeft; this.checkboxBlackBox.UseVisualStyleBackColor = true; // // checkboxDerailments // this.checkboxDerailments.AutoSize = true; this.checkboxDerailments.Location = new System.Drawing.Point(8, 56); this.checkboxDerailments.Name = "checkboxDerailments"; this.checkboxDerailments.Size = new System.Drawing.Size(81, 17); this.checkboxDerailments.TabIndex = 2; this.checkboxDerailments.Text = "Derailments"; this.checkboxDerailments.UseVisualStyleBackColor = true; // // checkboxCollisions // this.checkboxCollisions.AutoSize = true; this.checkboxCollisions.Location = new System.Drawing.Point(8, 40); this.checkboxCollisions.Name = "checkboxCollisions"; this.checkboxCollisions.Size = new System.Drawing.Size(69, 17); this.checkboxCollisions.TabIndex = 1; this.checkboxCollisions.Text = "Collisions"; this.checkboxCollisions.UseVisualStyleBackColor = true; // // checkboxToppling // this.checkboxToppling.AutoSize = true; this.checkboxToppling.Location = new System.Drawing.Point(8, 24); this.checkboxToppling.Name = "checkboxToppling"; this.checkboxToppling.Size = new System.Drawing.Size(67, 17); this.checkboxToppling.TabIndex = 0; this.checkboxToppling.Text = "Toppling"; this.checkboxToppling.UseVisualStyleBackColor = true; // // groupboxSound // this.groupboxSound.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxSound.Controls.Add(this.updownSoundNumber); this.groupboxSound.Controls.Add(this.comboboxSoundRange); this.groupboxSound.Controls.Add(this.labelSoundRange); this.groupboxSound.Controls.Add(this.labelSoundNumber); this.groupboxSound.ForeColor = System.Drawing.Color.Black; this.groupboxSound.Location = new System.Drawing.Point(0, 88); this.groupboxSound.Name = "groupboxSound"; this.groupboxSound.Size = new System.Drawing.Size(316, 80); this.groupboxSound.TabIndex = 9; this.groupboxSound.TabStop = false; this.groupboxSound.Text = "Sound"; // // updownSoundNumber // this.updownSoundNumber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.updownSoundNumber.Location = new System.Drawing.Point(156, 48); this.updownSoundNumber.Minimum = new decimal(new int[] { 8, 0, 0, 0}); this.updownSoundNumber.Name = "updownSoundNumber"; this.updownSoundNumber.Size = new System.Drawing.Size(152, 20); this.updownSoundNumber.TabIndex = 3; this.updownSoundNumber.Value = new decimal(new int[] { 16, 0, 0, 0}); // // comboboxSoundRange // this.comboboxSoundRange.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.comboboxSoundRange.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxSoundRange.FormattingEnabled = true; this.comboboxSoundRange.Location = new System.Drawing.Point(156, 24); this.comboboxSoundRange.Name = "comboboxSoundRange"; this.comboboxSoundRange.Size = new System.Drawing.Size(152, 21); this.comboboxSoundRange.TabIndex = 1; // // labelSoundRange // this.labelSoundRange.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelSoundRange.AutoEllipsis = true; this.labelSoundRange.Location = new System.Drawing.Point(8, 24); this.labelSoundRange.Name = "labelSoundRange"; this.labelSoundRange.Size = new System.Drawing.Size(148, 16); this.labelSoundRange.TabIndex = 0; this.labelSoundRange.Text = "Effective range:"; this.labelSoundRange.TextAlign = System.Drawing.ContentAlignment.TopRight; // // labelSoundNumber // this.labelSoundNumber.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelSoundNumber.Location = new System.Drawing.Point(8, 50); this.labelSoundNumber.Name = "labelSoundNumber"; this.labelSoundNumber.Size = new System.Drawing.Size(148, 14); this.labelSoundNumber.TabIndex = 2; this.labelSoundNumber.Text = "Number of allowed sounds:"; this.labelSoundNumber.TextAlign = System.Drawing.ContentAlignment.TopRight; // // panelOptionsLeft // this.panelOptionsLeft.Controls.Add(this.groupboxDisplayMode); this.panelOptionsLeft.Controls.Add(this.groupboxWindow); this.panelOptionsLeft.Controls.Add(this.groupboxFullscreen); this.panelOptionsLeft.Controls.Add(this.groupboxInterpolation); this.panelOptionsLeft.Location = new System.Drawing.Point(8, 72); this.panelOptionsLeft.Name = "panelOptionsLeft"; this.panelOptionsLeft.Size = new System.Drawing.Size(316, 448); this.panelOptionsLeft.TabIndex = 16; // // groupboxDisplayMode // this.groupboxDisplayMode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxDisplayMode.Controls.Add(this.comboboxVSync); this.groupboxDisplayMode.Controls.Add(this.labelVSync); this.groupboxDisplayMode.Controls.Add(this.radiobuttonFullscreen); this.groupboxDisplayMode.Controls.Add(this.radiobuttonWindow); this.groupboxDisplayMode.ForeColor = System.Drawing.Color.Black; this.groupboxDisplayMode.Location = new System.Drawing.Point(0, 0); this.groupboxDisplayMode.Name = "groupboxDisplayMode"; this.groupboxDisplayMode.Size = new System.Drawing.Size(316, 104); this.groupboxDisplayMode.TabIndex = 4; this.groupboxDisplayMode.TabStop = false; this.groupboxDisplayMode.Text = "Display mode"; // // comboboxVSync // this.comboboxVSync.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.comboboxVSync.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxVSync.FormattingEnabled = true; this.comboboxVSync.Location = new System.Drawing.Point(156, 72); this.comboboxVSync.Name = "comboboxVSync"; this.comboboxVSync.Size = new System.Drawing.Size(152, 21); this.comboboxVSync.TabIndex = 7; // // labelVSync // this.labelVSync.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelVSync.AutoEllipsis = true; this.labelVSync.Location = new System.Drawing.Point(8, 72); this.labelVSync.Name = "labelVSync"; this.labelVSync.Size = new System.Drawing.Size(148, 16); this.labelVSync.TabIndex = 2; this.labelVSync.Text = "Vertical syncronization:"; this.labelVSync.TextAlign = System.Drawing.ContentAlignment.TopRight; // // radiobuttonFullscreen // this.radiobuttonFullscreen.AutoSize = true; this.radiobuttonFullscreen.Location = new System.Drawing.Point(8, 48); this.radiobuttonFullscreen.Name = "radiobuttonFullscreen"; this.radiobuttonFullscreen.Size = new System.Drawing.Size(102, 17); this.radiobuttonFullscreen.TabIndex = 1; this.radiobuttonFullscreen.TabStop = true; this.radiobuttonFullscreen.Text = "Fullscreen mode"; this.radiobuttonFullscreen.UseVisualStyleBackColor = true; // // radiobuttonWindow // this.radiobuttonWindow.AutoSize = true; this.radiobuttonWindow.Checked = true; this.radiobuttonWindow.Location = new System.Drawing.Point(8, 24); this.radiobuttonWindow.Name = "radiobuttonWindow"; this.radiobuttonWindow.Size = new System.Drawing.Size(93, 17); this.radiobuttonWindow.TabIndex = 0; this.radiobuttonWindow.TabStop = true; this.radiobuttonWindow.Text = "Window mode"; this.radiobuttonWindow.UseVisualStyleBackColor = true; // // groupboxWindow // this.groupboxWindow.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxWindow.Controls.Add(this.updownWindowHeight); this.groupboxWindow.Controls.Add(this.labelWindowHeight); this.groupboxWindow.Controls.Add(this.updownWindowWidth); this.groupboxWindow.Controls.Add(this.labelWindowWidth); this.groupboxWindow.ForeColor = System.Drawing.Color.Black; this.groupboxWindow.Location = new System.Drawing.Point(0, 112); this.groupboxWindow.Name = "groupboxWindow"; this.groupboxWindow.Size = new System.Drawing.Size(316, 80); this.groupboxWindow.TabIndex = 5; this.groupboxWindow.TabStop = false; this.groupboxWindow.Text = "Window mode"; // // updownWindowHeight // this.updownWindowHeight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.updownWindowHeight.Location = new System.Drawing.Point(156, 48); this.updownWindowHeight.Maximum = new decimal(new int[] { 1048575, 0, 0, 0}); this.updownWindowHeight.Minimum = new decimal(new int[] { 16, 0, 0, 0}); this.updownWindowHeight.Name = "updownWindowHeight"; this.updownWindowHeight.Size = new System.Drawing.Size(152, 20); this.updownWindowHeight.TabIndex = 3; this.updownWindowHeight.Value = new decimal(new int[] { 600, 0, 0, 0}); // // labelWindowHeight // this.labelWindowHeight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelWindowHeight.AutoEllipsis = true; this.labelWindowHeight.Location = new System.Drawing.Point(8, 48); this.labelWindowHeight.Name = "labelWindowHeight"; this.labelWindowHeight.Size = new System.Drawing.Size(148, 16); this.labelWindowHeight.TabIndex = 2; this.labelWindowHeight.Text = "Height:"; this.labelWindowHeight.TextAlign = System.Drawing.ContentAlignment.TopRight; // // updownWindowWidth // this.updownWindowWidth.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.updownWindowWidth.Location = new System.Drawing.Point(156, 24); this.updownWindowWidth.Maximum = new decimal(new int[] { 1048575, 0, 0, 0}); this.updownWindowWidth.Minimum = new decimal(new int[] { 16, 0, 0, 0}); this.updownWindowWidth.Name = "updownWindowWidth"; this.updownWindowWidth.Size = new System.Drawing.Size(152, 20); this.updownWindowWidth.TabIndex = 1; this.updownWindowWidth.Value = new decimal(new int[] { 960, 0, 0, 0}); // // labelWindowWidth // this.labelWindowWidth.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelWindowWidth.AutoEllipsis = true; this.labelWindowWidth.Location = new System.Drawing.Point(8, 24); this.labelWindowWidth.Name = "labelWindowWidth"; this.labelWindowWidth.Size = new System.Drawing.Size(148, 16); this.labelWindowWidth.TabIndex = 0; this.labelWindowWidth.Text = "Width:"; this.labelWindowWidth.TextAlign = System.Drawing.ContentAlignment.TopRight; // // groupboxFullscreen // this.groupboxFullscreen.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxFullscreen.Controls.Add(this.comboboxFullscreenBits); this.groupboxFullscreen.Controls.Add(this.labelFullscreenBits); this.groupboxFullscreen.Controls.Add(this.updownFullscreenHeight); this.groupboxFullscreen.Controls.Add(this.labelFullscreenHeight); this.groupboxFullscreen.Controls.Add(this.updownFullscreenWidth); this.groupboxFullscreen.Controls.Add(this.labelFullscreenWidth); this.groupboxFullscreen.ForeColor = System.Drawing.Color.Black; this.groupboxFullscreen.Location = new System.Drawing.Point(0, 200); this.groupboxFullscreen.Name = "groupboxFullscreen"; this.groupboxFullscreen.Size = new System.Drawing.Size(316, 104); this.groupboxFullscreen.TabIndex = 6; this.groupboxFullscreen.TabStop = false; this.groupboxFullscreen.Text = "Fullscreen mode"; // // comboboxFullscreenBits // this.comboboxFullscreenBits.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.comboboxFullscreenBits.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxFullscreenBits.FormattingEnabled = true; this.comboboxFullscreenBits.Location = new System.Drawing.Point(156, 72); this.comboboxFullscreenBits.Name = "comboboxFullscreenBits"; this.comboboxFullscreenBits.Size = new System.Drawing.Size(152, 21); this.comboboxFullscreenBits.TabIndex = 5; // // labelFullscreenBits // this.labelFullscreenBits.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelFullscreenBits.AutoEllipsis = true; this.labelFullscreenBits.Location = new System.Drawing.Point(8, 72); this.labelFullscreenBits.Name = "labelFullscreenBits"; this.labelFullscreenBits.Size = new System.Drawing.Size(148, 16); this.labelFullscreenBits.TabIndex = 4; this.labelFullscreenBits.Text = "Bits per pixel:"; this.labelFullscreenBits.TextAlign = System.Drawing.ContentAlignment.TopRight; // // updownFullscreenHeight // this.updownFullscreenHeight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.updownFullscreenHeight.Location = new System.Drawing.Point(156, 48); this.updownFullscreenHeight.Maximum = new decimal(new int[] { 1048575, 0, 0, 0}); this.updownFullscreenHeight.Minimum = new decimal(new int[] { 16, 0, 0, 0}); this.updownFullscreenHeight.Name = "updownFullscreenHeight"; this.updownFullscreenHeight.Size = new System.Drawing.Size(152, 20); this.updownFullscreenHeight.TabIndex = 3; this.updownFullscreenHeight.Value = new decimal(new int[] { 768, 0, 0, 0}); // // labelFullscreenHeight // this.labelFullscreenHeight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelFullscreenHeight.AutoEllipsis = true; this.labelFullscreenHeight.Location = new System.Drawing.Point(8, 48); this.labelFullscreenHeight.Name = "labelFullscreenHeight"; this.labelFullscreenHeight.Size = new System.Drawing.Size(148, 16); this.labelFullscreenHeight.TabIndex = 2; this.labelFullscreenHeight.Text = "Height:"; this.labelFullscreenHeight.TextAlign = System.Drawing.ContentAlignment.TopRight; // // updownFullscreenWidth // this.updownFullscreenWidth.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.updownFullscreenWidth.Location = new System.Drawing.Point(156, 24); this.updownFullscreenWidth.Maximum = new decimal(new int[] { 1048575, 0, 0, 0}); this.updownFullscreenWidth.Minimum = new decimal(new int[] { 16, 0, 0, 0}); this.updownFullscreenWidth.Name = "updownFullscreenWidth"; this.updownFullscreenWidth.Size = new System.Drawing.Size(152, 20); this.updownFullscreenWidth.TabIndex = 1; this.updownFullscreenWidth.Value = new decimal(new int[] { 1024, 0, 0, 0}); // // labelFullscreenWidth // this.labelFullscreenWidth.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelFullscreenWidth.AutoEllipsis = true; this.labelFullscreenWidth.Location = new System.Drawing.Point(8, 24); this.labelFullscreenWidth.Name = "labelFullscreenWidth"; this.labelFullscreenWidth.Size = new System.Drawing.Size(148, 16); this.labelFullscreenWidth.TabIndex = 0; this.labelFullscreenWidth.Text = "Width:"; this.labelFullscreenWidth.TextAlign = System.Drawing.ContentAlignment.TopRight; // // groupboxInterpolation // this.groupboxInterpolation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxInterpolation.Controls.Add(this.labelTransparencyQuality); this.groupboxInterpolation.Controls.Add(this.labelTransparencyPerformance); this.groupboxInterpolation.Controls.Add(this.labelTransparency); this.groupboxInterpolation.Controls.Add(this.updownAnisotropic); this.groupboxInterpolation.Controls.Add(this.labelAnisotropic); this.groupboxInterpolation.Controls.Add(this.comboboxInterpolation); this.groupboxInterpolation.Controls.Add(this.labelInterpolation); this.groupboxInterpolation.Controls.Add(this.trackbarTransparency); this.groupboxInterpolation.ForeColor = System.Drawing.Color.Black; this.groupboxInterpolation.Location = new System.Drawing.Point(0, 312); this.groupboxInterpolation.Name = "groupboxInterpolation"; this.groupboxInterpolation.Size = new System.Drawing.Size(316, 136); this.groupboxInterpolation.TabIndex = 7; this.groupboxInterpolation.TabStop = false; this.groupboxInterpolation.Text = "Interpolation"; // // labelTransparencyQuality // this.labelTransparencyQuality.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.labelTransparencyQuality.AutoEllipsis = true; this.labelTransparencyQuality.Location = new System.Drawing.Point(230, 112); this.labelTransparencyQuality.Name = "labelTransparencyQuality"; this.labelTransparencyQuality.Size = new System.Drawing.Size(76, 16); this.labelTransparencyQuality.TabIndex = 7; this.labelTransparencyQuality.Text = "Quality"; this.labelTransparencyQuality.TextAlign = System.Drawing.ContentAlignment.TopRight; // // labelTransparencyPerformance // this.labelTransparencyPerformance.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.labelTransparencyPerformance.AutoEllipsis = true; this.labelTransparencyPerformance.Location = new System.Drawing.Point(156, 112); this.labelTransparencyPerformance.Name = "labelTransparencyPerformance"; this.labelTransparencyPerformance.Size = new System.Drawing.Size(76, 16); this.labelTransparencyPerformance.TabIndex = 6; this.labelTransparencyPerformance.Text = "Performance"; // // labelTransparency // this.labelTransparency.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelTransparency.AutoEllipsis = true; this.labelTransparency.Location = new System.Drawing.Point(8, 64); this.labelTransparency.Name = "labelTransparency"; this.labelTransparency.Size = new System.Drawing.Size(148, 16); this.labelTransparency.TabIndex = 4; this.labelTransparency.Text = "Transparency:"; this.labelTransparency.TextAlign = System.Drawing.ContentAlignment.TopRight; // // updownAnisotropic // this.updownAnisotropic.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.updownAnisotropic.Enabled = false; this.updownAnisotropic.Location = new System.Drawing.Point(156, 40); this.updownAnisotropic.Maximum = new decimal(new int[] { 16, 0, 0, 0}); this.updownAnisotropic.Name = "updownAnisotropic"; this.updownAnisotropic.Size = new System.Drawing.Size(152, 20); this.updownAnisotropic.TabIndex = 3; // // labelAnisotropic // this.labelAnisotropic.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelAnisotropic.AutoEllipsis = true; this.labelAnisotropic.Enabled = false; this.labelAnisotropic.Location = new System.Drawing.Point(8, 40); this.labelAnisotropic.Name = "labelAnisotropic"; this.labelAnisotropic.Size = new System.Drawing.Size(148, 16); this.labelAnisotropic.TabIndex = 2; this.labelAnisotropic.Text = "Level of anisotropic filtering:"; this.labelAnisotropic.TextAlign = System.Drawing.ContentAlignment.TopRight; // // comboboxInterpolation // this.comboboxInterpolation.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.comboboxInterpolation.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxInterpolation.FormattingEnabled = true; this.comboboxInterpolation.Location = new System.Drawing.Point(156, 16); this.comboboxInterpolation.Name = "comboboxInterpolation"; this.comboboxInterpolation.Size = new System.Drawing.Size(152, 21); this.comboboxInterpolation.TabIndex = 1; this.comboboxInterpolation.SelectedIndexChanged += new System.EventHandler(this.comboboxInterpolation_SelectedIndexChanged); // // labelInterpolation // this.labelInterpolation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelInterpolation.AutoEllipsis = true; this.labelInterpolation.Location = new System.Drawing.Point(8, 16); this.labelInterpolation.Name = "labelInterpolation"; this.labelInterpolation.Size = new System.Drawing.Size(148, 16); this.labelInterpolation.TabIndex = 0; this.labelInterpolation.Text = "Mode:"; this.labelInterpolation.TextAlign = System.Drawing.ContentAlignment.TopRight; // // trackbarTransparency // this.trackbarTransparency.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.trackbarTransparency.Location = new System.Drawing.Point(156, 64); this.trackbarTransparency.Maximum = 2; this.trackbarTransparency.Name = "trackbarTransparency"; this.trackbarTransparency.Size = new System.Drawing.Size(152, 42); this.trackbarTransparency.TabIndex = 5; this.trackbarTransparency.TickStyle = System.Windows.Forms.TickStyle.Both; // // pictureboxLanguage // this.pictureboxLanguage.Location = new System.Drawing.Point(8, 40); this.pictureboxLanguage.Name = "pictureboxLanguage"; this.pictureboxLanguage.Size = new System.Drawing.Size(32, 20); this.pictureboxLanguage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureboxLanguage.TabIndex = 15; this.pictureboxLanguage.TabStop = false; // // comboboxLanguages // this.comboboxLanguages.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxLanguages.FormattingEnabled = true; this.comboboxLanguages.Location = new System.Drawing.Point(48, 40); this.comboboxLanguages.Name = "comboboxLanguages"; this.comboboxLanguages.Size = new System.Drawing.Size(216, 21); this.comboboxLanguages.TabIndex = 3; this.comboboxLanguages.SelectedIndexChanged += new System.EventHandler(this.comboboxLanguages_SelectedIndexChanged); // // labelOptionsTitleSeparator // this.labelOptionsTitleSeparator.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelOptionsTitleSeparator.BackColor = System.Drawing.Color.White; this.labelOptionsTitleSeparator.Location = new System.Drawing.Point(0, 32); this.labelOptionsTitleSeparator.Name = "labelOptionsTitleSeparator"; this.labelOptionsTitleSeparator.Size = new System.Drawing.Size(640, 2); this.labelOptionsTitleSeparator.TabIndex = 2; // // labelOptionsTitle // this.labelOptionsTitle.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelOptionsTitle.AutoEllipsis = true; this.labelOptionsTitle.AutoSize = true; this.labelOptionsTitle.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(122)))), ((int)(((byte)(153)))), ((int)(((byte)(61))))); this.labelOptionsTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelOptionsTitle.ForeColor = System.Drawing.Color.White; this.labelOptionsTitle.Location = new System.Drawing.Point(8, 8); this.labelOptionsTitle.Name = "labelOptionsTitle"; this.labelOptionsTitle.Size = new System.Drawing.Size(54, 16); this.labelOptionsTitle.TabIndex = 1; this.labelOptionsTitle.Text = "Options"; // // labelOptionsTitleBackground // this.labelOptionsTitleBackground.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelOptionsTitleBackground.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(122)))), ((int)(((byte)(153)))), ((int)(((byte)(61))))); this.labelOptionsTitleBackground.Location = new System.Drawing.Point(0, 0); this.labelOptionsTitleBackground.Name = "labelOptionsTitleBackground"; this.labelOptionsTitleBackground.Size = new System.Drawing.Size(640, 32); this.labelOptionsTitleBackground.TabIndex = 0; // // labelFillerThree // this.labelFillerThree.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelFillerThree.BackColor = System.Drawing.Color.Silver; this.labelFillerThree.Location = new System.Drawing.Point(0, 536); this.labelFillerThree.Name = "labelFillerThree"; this.labelFillerThree.Size = new System.Drawing.Size(160, 48); this.labelFillerThree.TabIndex = 4; // // panelPanels // this.panelPanels.BackColor = System.Drawing.Color.Gray; this.panelPanels.Controls.Add(this.labelPanelsBottom); this.panelPanels.Controls.Add(this.radiobuttonGetAddOns); this.panelPanels.Controls.Add(this.radiobuttonOptions); this.panelPanels.Controls.Add(this.radiobuttonControls); this.panelPanels.Controls.Add(this.radiobuttonReview); this.panelPanels.Controls.Add(this.radiobuttonStart); this.panelPanels.Controls.Add(this.labelPanelsTop); this.panelPanels.Location = new System.Drawing.Point(0, 160); this.panelPanels.Name = "panelPanels"; this.panelPanels.Size = new System.Drawing.Size(160, 184); this.panelPanels.TabIndex = 0; // // labelPanelsBottom // this.labelPanelsBottom.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelPanelsBottom.BackColor = System.Drawing.Color.White; this.labelPanelsBottom.Location = new System.Drawing.Point(0, 182); this.labelPanelsBottom.Name = "labelPanelsBottom"; this.labelPanelsBottom.Size = new System.Drawing.Size(160, 2); this.labelPanelsBottom.TabIndex = 1; // // radiobuttonGetAddOns // this.radiobuttonGetAddOns.AutoSize = true; this.radiobuttonGetAddOns.Location = new System.Drawing.Point(8, 80); this.radiobuttonGetAddOns.Name = "radiobuttonGetAddOns"; this.radiobuttonGetAddOns.Size = new System.Drawing.Size(84, 17); this.radiobuttonGetAddOns.TabIndex = 2; this.radiobuttonGetAddOns.TabStop = true; this.radiobuttonGetAddOns.Text = "Get Add-ons"; this.radiobuttonGetAddOns.UseVisualStyleBackColor = true; this.radiobuttonGetAddOns.CheckedChanged += new System.EventHandler(this.RadiobuttonGetAddOnsCheckedChanged); // // radiobuttonOptions // this.radiobuttonOptions.AutoSize = true; this.radiobuttonOptions.Location = new System.Drawing.Point(8, 144); this.radiobuttonOptions.Name = "radiobuttonOptions"; this.radiobuttonOptions.Size = new System.Drawing.Size(61, 17); this.radiobuttonOptions.TabIndex = 4; this.radiobuttonOptions.TabStop = true; this.radiobuttonOptions.Text = "Options"; this.radiobuttonOptions.UseVisualStyleBackColor = true; this.radiobuttonOptions.CheckedChanged += new System.EventHandler(this.radiobuttonOptions_CheckedChanged); // // radiobuttonControls // this.radiobuttonControls.AutoSize = true; this.radiobuttonControls.Location = new System.Drawing.Point(8, 112); this.radiobuttonControls.Name = "radiobuttonControls"; this.radiobuttonControls.Size = new System.Drawing.Size(113, 17); this.radiobuttonControls.TabIndex = 3; this.radiobuttonControls.TabStop = true; this.radiobuttonControls.Text = "Customize controls"; this.radiobuttonControls.UseVisualStyleBackColor = true; this.radiobuttonControls.CheckedChanged += new System.EventHandler(this.radiobuttonControls_CheckedChanged); // // radiobuttonReview // this.radiobuttonReview.AutoSize = true; this.radiobuttonReview.Location = new System.Drawing.Point(8, 48); this.radiobuttonReview.Name = "radiobuttonReview"; this.radiobuttonReview.Size = new System.Drawing.Size(109, 17); this.radiobuttonReview.TabIndex = 1; this.radiobuttonReview.TabStop = true; this.radiobuttonReview.Text = "Review last game"; this.radiobuttonReview.UseVisualStyleBackColor = true; this.radiobuttonReview.CheckedChanged += new System.EventHandler(this.radiobuttonReview_CheckedChanged); // // radiobuttonStart // this.radiobuttonStart.AutoSize = true; this.radiobuttonStart.Location = new System.Drawing.Point(8, 16); this.radiobuttonStart.Name = "radiobuttonStart"; this.radiobuttonStart.Size = new System.Drawing.Size(99, 17); this.radiobuttonStart.TabIndex = 0; this.radiobuttonStart.TabStop = true; this.radiobuttonStart.Text = "Start new game"; this.radiobuttonStart.UseVisualStyleBackColor = true; this.radiobuttonStart.CheckedChanged += new System.EventHandler(this.radiobuttonStart_CheckedChanged); // // labelPanelsTop // this.labelPanelsTop.BackColor = System.Drawing.Color.White; this.labelPanelsTop.Location = new System.Drawing.Point(0, 0); this.labelPanelsTop.Name = "labelPanelsTop"; this.labelPanelsTop.Size = new System.Drawing.Size(160, 2); this.labelPanelsTop.TabIndex = 0; // // panelReview // this.panelReview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelReview.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(243)))), ((int)(((byte)(247))))); this.panelReview.Controls.Add(this.comboboxBlackBoxFormat); this.panelReview.Controls.Add(this.labelBlackBoxFormat); this.panelReview.Controls.Add(this.groupboxReviewDateTime); this.panelReview.Controls.Add(this.buttonBlackBoxExport); this.panelReview.Controls.Add(this.labelBlackBox); this.panelReview.Controls.Add(this.groupboxScore); this.panelReview.Controls.Add(this.groupboxReviewTrain); this.panelReview.Controls.Add(this.groupboxReviewRoute); this.panelReview.Controls.Add(this.labelConditions); this.panelReview.Controls.Add(this.groupboxRating); this.panelReview.Controls.Add(this.labelScore); this.panelReview.Controls.Add(this.labelReviewTitleSeparator); this.panelReview.Controls.Add(this.labelReviewTitle); this.panelReview.Controls.Add(this.labelReviewTitleBackground); this.panelReview.Location = new System.Drawing.Point(160, 0); this.panelReview.Name = "panelReview"; this.panelReview.Size = new System.Drawing.Size(640, 584); this.panelReview.TabIndex = 10; // // comboboxBlackBoxFormat // this.comboboxBlackBoxFormat.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.comboboxBlackBoxFormat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxBlackBoxFormat.FormattingEnabled = true; this.comboboxBlackBoxFormat.Location = new System.Drawing.Point(104, 552); this.comboboxBlackBoxFormat.Name = "comboboxBlackBoxFormat"; this.comboboxBlackBoxFormat.Size = new System.Drawing.Size(144, 21); this.comboboxBlackBoxFormat.TabIndex = 12; // // labelBlackBoxFormat // this.labelBlackBoxFormat.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelBlackBoxFormat.AutoEllipsis = true; this.labelBlackBoxFormat.ForeColor = System.Drawing.Color.Black; this.labelBlackBoxFormat.Location = new System.Drawing.Point(8, 552); this.labelBlackBoxFormat.Name = "labelBlackBoxFormat"; this.labelBlackBoxFormat.Size = new System.Drawing.Size(96, 16); this.labelBlackBoxFormat.TabIndex = 11; this.labelBlackBoxFormat.Text = "Format:"; this.labelBlackBoxFormat.TextAlign = System.Drawing.ContentAlignment.TopRight; // // groupboxReviewDateTime // this.groupboxReviewDateTime.Controls.Add(this.labelReviewTimeValue); this.groupboxReviewDateTime.Controls.Add(this.labelReviewTimeCaption); this.groupboxReviewDateTime.Controls.Add(this.labelReviewDateValue); this.groupboxReviewDateTime.Controls.Add(this.labelReviewDateCaption); this.groupboxReviewDateTime.ForeColor = System.Drawing.Color.Black; this.groupboxReviewDateTime.Location = new System.Drawing.Point(440, 72); this.groupboxReviewDateTime.Name = "groupboxReviewDateTime"; this.groupboxReviewDateTime.Size = new System.Drawing.Size(200, 56); this.groupboxReviewDateTime.TabIndex = 6; this.groupboxReviewDateTime.TabStop = false; this.groupboxReviewDateTime.Text = "Date and time"; // // labelReviewTimeValue // this.labelReviewTimeValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelReviewTimeValue.AutoEllipsis = true; this.labelReviewTimeValue.Location = new System.Drawing.Point(104, 32); this.labelReviewTimeValue.Name = "labelReviewTimeValue"; this.labelReviewTimeValue.Size = new System.Drawing.Size(88, 16); this.labelReviewTimeValue.TabIndex = 3; this.labelReviewTimeValue.Text = "?"; // // labelReviewTimeCaption // this.labelReviewTimeCaption.AutoEllipsis = true; this.labelReviewTimeCaption.Location = new System.Drawing.Point(8, 32); this.labelReviewTimeCaption.Name = "labelReviewTimeCaption"; this.labelReviewTimeCaption.Size = new System.Drawing.Size(96, 16); this.labelReviewTimeCaption.TabIndex = 2; this.labelReviewTimeCaption.Text = "Time:"; this.labelReviewTimeCaption.TextAlign = System.Drawing.ContentAlignment.TopRight; // // labelReviewDateValue // this.labelReviewDateValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelReviewDateValue.AutoEllipsis = true; this.labelReviewDateValue.Location = new System.Drawing.Point(104, 16); this.labelReviewDateValue.Name = "labelReviewDateValue"; this.labelReviewDateValue.Size = new System.Drawing.Size(88, 16); this.labelReviewDateValue.TabIndex = 1; this.labelReviewDateValue.Text = "?"; // // labelReviewDateCaption // this.labelReviewDateCaption.AutoEllipsis = true; this.labelReviewDateCaption.Location = new System.Drawing.Point(8, 16); this.labelReviewDateCaption.Name = "labelReviewDateCaption"; this.labelReviewDateCaption.Size = new System.Drawing.Size(96, 16); this.labelReviewDateCaption.TabIndex = 0; this.labelReviewDateCaption.Text = "Date:"; this.labelReviewDateCaption.TextAlign = System.Drawing.ContentAlignment.TopRight; // // buttonBlackBoxExport // this.buttonBlackBoxExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonBlackBoxExport.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonBlackBoxExport.Location = new System.Drawing.Point(256, 552); this.buttonBlackBoxExport.Name = "buttonBlackBoxExport"; this.buttonBlackBoxExport.Size = new System.Drawing.Size(120, 24); this.buttonBlackBoxExport.TabIndex = 13; this.buttonBlackBoxExport.Text = "Export..."; this.buttonBlackBoxExport.UseVisualStyleBackColor = false; this.buttonBlackBoxExport.Click += new System.EventHandler(this.buttonBlackBoxExport_Click); // // labelBlackBox // this.labelBlackBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelBlackBox.AutoEllipsis = true; this.labelBlackBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(153)))), ((int)(((byte)(107)))), ((int)(((byte)(130))))); this.labelBlackBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelBlackBox.ForeColor = System.Drawing.Color.White; this.labelBlackBox.Location = new System.Drawing.Point(8, 520); this.labelBlackBox.Name = "labelBlackBox"; this.labelBlackBox.Size = new System.Drawing.Size(624, 24); this.labelBlackBox.TabIndex = 10; this.labelBlackBox.Text = "Black box"; this.labelBlackBox.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // groupboxScore // this.groupboxScore.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxScore.Controls.Add(this.checkboxScorePenalties); this.groupboxScore.Controls.Add(this.buttonScoreExport); this.groupboxScore.Controls.Add(this.listviewScore); this.groupboxScore.Location = new System.Drawing.Point(272, 176); this.groupboxScore.Name = "groupboxScore"; this.groupboxScore.Size = new System.Drawing.Size(360, 336); this.groupboxScore.TabIndex = 0; this.groupboxScore.TabStop = false; this.groupboxScore.Text = "Log"; // // checkboxScorePenalties // this.checkboxScorePenalties.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.checkboxScorePenalties.AutoSize = true; this.checkboxScorePenalties.Location = new System.Drawing.Point(8, 304); this.checkboxScorePenalties.Name = "checkboxScorePenalties"; this.checkboxScorePenalties.Size = new System.Drawing.Size(120, 17); this.checkboxScorePenalties.TabIndex = 1; this.checkboxScorePenalties.Text = "Show penalties only"; this.checkboxScorePenalties.UseVisualStyleBackColor = true; this.checkboxScorePenalties.CheckedChanged += new System.EventHandler(this.checkboxScorePenalties_CheckedChanged); // // buttonScoreExport // this.buttonScoreExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonScoreExport.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonScoreExport.Location = new System.Drawing.Point(232, 304); this.buttonScoreExport.Name = "buttonScoreExport"; this.buttonScoreExport.Size = new System.Drawing.Size(120, 24); this.buttonScoreExport.TabIndex = 2; this.buttonScoreExport.Text = "Export..."; this.buttonScoreExport.UseVisualStyleBackColor = false; this.buttonScoreExport.Click += new System.EventHandler(this.buttonScoreExport_Click); // // listviewScore // this.listviewScore.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.listviewScore.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.columnheaderScoreTime, this.columnheaderScorePosition, this.columnheaderScoreValue, this.columnheaderScoreCumulative, this.columnheaderScoreText}); this.listviewScore.FullRowSelect = true; this.listviewScore.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; this.listviewScore.HideSelection = false; this.listviewScore.LabelWrap = false; this.listviewScore.Location = new System.Drawing.Point(8, 16); this.listviewScore.MultiSelect = false; this.listviewScore.Name = "listviewScore"; this.listviewScore.ShowGroups = false; this.listviewScore.Size = new System.Drawing.Size(344, 280); this.listviewScore.TabIndex = 0; this.listviewScore.UseCompatibleStateImageBehavior = false; this.listviewScore.View = System.Windows.Forms.View.Details; // // columnheaderScoreTime // this.columnheaderScoreTime.Text = "Time"; this.columnheaderScoreTime.Width = 72; // // columnheaderScorePosition // this.columnheaderScorePosition.Text = "Position (m)"; this.columnheaderScorePosition.Width = 79; // // columnheaderScoreValue // this.columnheaderScoreValue.Text = "Value"; this.columnheaderScoreValue.Width = 66; // // columnheaderScoreCumulative // this.columnheaderScoreCumulative.Text = "Cumulative"; this.columnheaderScoreCumulative.Width = 88; // // columnheaderScoreText // this.columnheaderScoreText.Text = "Reason"; this.columnheaderScoreText.Width = 178; // // groupboxReviewTrain // this.groupboxReviewTrain.Controls.Add(this.labelReviewTrainValue); this.groupboxReviewTrain.Controls.Add(this.labelReviewTrainCaption); this.groupboxReviewTrain.ForeColor = System.Drawing.Color.Black; this.groupboxReviewTrain.Location = new System.Drawing.Point(224, 72); this.groupboxReviewTrain.Name = "groupboxReviewTrain"; this.groupboxReviewTrain.Size = new System.Drawing.Size(208, 56); this.groupboxReviewTrain.TabIndex = 5; this.groupboxReviewTrain.TabStop = false; this.groupboxReviewTrain.Text = "Train"; // // labelReviewTrainValue // this.labelReviewTrainValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelReviewTrainValue.AutoEllipsis = true; this.labelReviewTrainValue.Location = new System.Drawing.Point(8, 32); this.labelReviewTrainValue.Name = "labelReviewTrainValue"; this.labelReviewTrainValue.Size = new System.Drawing.Size(192, 16); this.labelReviewTrainValue.TabIndex = 1; this.labelReviewTrainValue.Text = "?"; // // labelReviewTrainCaption // this.labelReviewTrainCaption.AutoSize = true; this.labelReviewTrainCaption.Location = new System.Drawing.Point(8, 16); this.labelReviewTrainCaption.Name = "labelReviewTrainCaption"; this.labelReviewTrainCaption.Size = new System.Drawing.Size(39, 13); this.labelReviewTrainCaption.TabIndex = 0; this.labelReviewTrainCaption.Text = "Folder:"; // // groupboxReviewRoute // this.groupboxReviewRoute.Controls.Add(this.labelReviewRouteValue); this.groupboxReviewRoute.Controls.Add(this.labelReviewRouteCaption); this.groupboxReviewRoute.ForeColor = System.Drawing.Color.Black; this.groupboxReviewRoute.Location = new System.Drawing.Point(8, 72); this.groupboxReviewRoute.Name = "groupboxReviewRoute"; this.groupboxReviewRoute.Size = new System.Drawing.Size(208, 56); this.groupboxReviewRoute.TabIndex = 4; this.groupboxReviewRoute.TabStop = false; this.groupboxReviewRoute.Text = "Route"; // // labelReviewRouteValue // this.labelReviewRouteValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelReviewRouteValue.AutoEllipsis = true; this.labelReviewRouteValue.Location = new System.Drawing.Point(8, 32); this.labelReviewRouteValue.Name = "labelReviewRouteValue"; this.labelReviewRouteValue.Size = new System.Drawing.Size(192, 16); this.labelReviewRouteValue.TabIndex = 1; this.labelReviewRouteValue.Text = "?"; // // labelReviewRouteCaption // this.labelReviewRouteCaption.AutoSize = true; this.labelReviewRouteCaption.Location = new System.Drawing.Point(8, 16); this.labelReviewRouteCaption.Name = "labelReviewRouteCaption"; this.labelReviewRouteCaption.Size = new System.Drawing.Size(26, 13); this.labelReviewRouteCaption.TabIndex = 0; this.labelReviewRouteCaption.Text = "File:"; // // labelConditions // this.labelConditions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelConditions.AutoEllipsis = true; this.labelConditions.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(153)))), ((int)(((byte)(107)))), ((int)(((byte)(130))))); this.labelConditions.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelConditions.ForeColor = System.Drawing.Color.White; this.labelConditions.Location = new System.Drawing.Point(8, 40); this.labelConditions.Name = "labelConditions"; this.labelConditions.Size = new System.Drawing.Size(624, 24); this.labelConditions.TabIndex = 3; this.labelConditions.Text = "Conditions"; this.labelConditions.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // groupboxRating // this.groupboxRating.Controls.Add(this.labelRatingRatioValue); this.groupboxRating.Controls.Add(this.labelRatingModeValue); this.groupboxRating.Controls.Add(this.labelRatingModeCaption); this.groupboxRating.Controls.Add(this.labelRatingRatioCaption); this.groupboxRating.Controls.Add(this.labelRatingMaximumValue); this.groupboxRating.Controls.Add(this.labelRatingMaximumCaption); this.groupboxRating.Controls.Add(this.labelRatingAchievedValue); this.groupboxRating.Controls.Add(this.labelRatingAchievedCaption); this.groupboxRating.Controls.Add(this.labelRatingDescription); this.groupboxRating.Controls.Add(this.labelRatingColor); this.groupboxRating.ForeColor = System.Drawing.Color.Black; this.groupboxRating.Location = new System.Drawing.Point(8, 176); this.groupboxRating.Name = "groupboxRating"; this.groupboxRating.Size = new System.Drawing.Size(256, 128); this.groupboxRating.TabIndex = 8; this.groupboxRating.TabStop = false; this.groupboxRating.Text = "Rating"; // // labelRatingRatioValue // this.labelRatingRatioValue.AutoEllipsis = true; this.labelRatingRatioValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelRatingRatioValue.Location = new System.Drawing.Point(128, 104); this.labelRatingRatioValue.Name = "labelRatingRatioValue"; this.labelRatingRatioValue.Size = new System.Drawing.Size(120, 16); this.labelRatingRatioValue.TabIndex = 9; this.labelRatingRatioValue.Text = "?"; // // labelRatingModeValue // this.labelRatingModeValue.AutoEllipsis = true; this.labelRatingModeValue.Location = new System.Drawing.Point(128, 24); this.labelRatingModeValue.Name = "labelRatingModeValue"; this.labelRatingModeValue.Size = new System.Drawing.Size(120, 16); this.labelRatingModeValue.TabIndex = 1; this.labelRatingModeValue.Text = "?"; // // labelRatingModeCaption // this.labelRatingModeCaption.AutoEllipsis = true; this.labelRatingModeCaption.Location = new System.Drawing.Point(8, 24); this.labelRatingModeCaption.Name = "labelRatingModeCaption"; this.labelRatingModeCaption.Size = new System.Drawing.Size(120, 16); this.labelRatingModeCaption.TabIndex = 0; this.labelRatingModeCaption.Text = "Mode:"; this.labelRatingModeCaption.TextAlign = System.Drawing.ContentAlignment.TopRight; // // labelRatingRatioCaption // this.labelRatingRatioCaption.AutoEllipsis = true; this.labelRatingRatioCaption.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelRatingRatioCaption.Location = new System.Drawing.Point(8, 104); this.labelRatingRatioCaption.Name = "labelRatingRatioCaption"; this.labelRatingRatioCaption.Size = new System.Drawing.Size(120, 16); this.labelRatingRatioCaption.TabIndex = 8; this.labelRatingRatioCaption.Text = "Ratio:"; this.labelRatingRatioCaption.TextAlign = System.Drawing.ContentAlignment.TopRight; // // labelRatingMaximumValue // this.labelRatingMaximumValue.AutoEllipsis = true; this.labelRatingMaximumValue.Location = new System.Drawing.Point(128, 88); this.labelRatingMaximumValue.Name = "labelRatingMaximumValue"; this.labelRatingMaximumValue.Size = new System.Drawing.Size(120, 16); this.labelRatingMaximumValue.TabIndex = 7; this.labelRatingMaximumValue.Text = "?"; // // labelRatingMaximumCaption // this.labelRatingMaximumCaption.AutoEllipsis = true; this.labelRatingMaximumCaption.Location = new System.Drawing.Point(8, 88); this.labelRatingMaximumCaption.Name = "labelRatingMaximumCaption"; this.labelRatingMaximumCaption.Size = new System.Drawing.Size(120, 16); this.labelRatingMaximumCaption.TabIndex = 6; this.labelRatingMaximumCaption.Text = "Maximum:"; this.labelRatingMaximumCaption.TextAlign = System.Drawing.ContentAlignment.TopRight; // // labelRatingAchievedValue // this.labelRatingAchievedValue.AutoEllipsis = true; this.labelRatingAchievedValue.Location = new System.Drawing.Point(128, 72); this.labelRatingAchievedValue.Name = "labelRatingAchievedValue"; this.labelRatingAchievedValue.Size = new System.Drawing.Size(120, 16); this.labelRatingAchievedValue.TabIndex = 5; this.labelRatingAchievedValue.Text = "?"; // // labelRatingAchievedCaption // this.labelRatingAchievedCaption.AutoEllipsis = true; this.labelRatingAchievedCaption.Location = new System.Drawing.Point(8, 72); this.labelRatingAchievedCaption.Name = "labelRatingAchievedCaption"; this.labelRatingAchievedCaption.Size = new System.Drawing.Size(120, 16); this.labelRatingAchievedCaption.TabIndex = 4; this.labelRatingAchievedCaption.Text = "Achieved:"; this.labelRatingAchievedCaption.TextAlign = System.Drawing.ContentAlignment.TopRight; // // labelRatingDescription // this.labelRatingDescription.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelRatingDescription.AutoEllipsis = true; this.labelRatingDescription.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.labelRatingDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelRatingDescription.Location = new System.Drawing.Point(56, 40); this.labelRatingDescription.Name = "labelRatingDescription"; this.labelRatingDescription.Size = new System.Drawing.Size(192, 24); this.labelRatingDescription.TabIndex = 3; this.labelRatingDescription.Text = "?"; this.labelRatingDescription.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // labelRatingColor // this.labelRatingColor.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelRatingColor.BackColor = System.Drawing.Color.Gray; this.labelRatingColor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.labelRatingColor.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelRatingColor.ForeColor = System.Drawing.Color.White; this.labelRatingColor.Location = new System.Drawing.Point(8, 40); this.labelRatingColor.Name = "labelRatingColor"; this.labelRatingColor.Size = new System.Drawing.Size(40, 24); this.labelRatingColor.TabIndex = 2; this.labelRatingColor.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // labelScore // this.labelScore.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelScore.AutoEllipsis = true; this.labelScore.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(153)))), ((int)(((byte)(107)))), ((int)(((byte)(130))))); this.labelScore.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelScore.ForeColor = System.Drawing.Color.White; this.labelScore.Location = new System.Drawing.Point(8, 144); this.labelScore.Name = "labelScore"; this.labelScore.Size = new System.Drawing.Size(624, 24); this.labelScore.TabIndex = 7; this.labelScore.Text = "Score"; this.labelScore.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // labelReviewTitleSeparator // this.labelReviewTitleSeparator.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelReviewTitleSeparator.BackColor = System.Drawing.Color.White; this.labelReviewTitleSeparator.Location = new System.Drawing.Point(0, 32); this.labelReviewTitleSeparator.Name = "labelReviewTitleSeparator"; this.labelReviewTitleSeparator.Size = new System.Drawing.Size(640, 2); this.labelReviewTitleSeparator.TabIndex = 2; // // labelReviewTitle // this.labelReviewTitle.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelReviewTitle.AutoEllipsis = true; this.labelReviewTitle.AutoSize = true; this.labelReviewTitle.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(179)))), ((int)(((byte)(71)))), ((int)(((byte)(143))))); this.labelReviewTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelReviewTitle.ForeColor = System.Drawing.Color.White; this.labelReviewTitle.Location = new System.Drawing.Point(8, 8); this.labelReviewTitle.Name = "labelReviewTitle"; this.labelReviewTitle.Size = new System.Drawing.Size(115, 16); this.labelReviewTitle.TabIndex = 1; this.labelReviewTitle.Text = "Review last game"; // // labelReviewTitleBackground // this.labelReviewTitleBackground.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelReviewTitleBackground.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(179)))), ((int)(((byte)(71)))), ((int)(((byte)(143))))); this.labelReviewTitleBackground.Location = new System.Drawing.Point(0, 0); this.labelReviewTitleBackground.Name = "labelReviewTitleBackground"; this.labelReviewTitleBackground.Size = new System.Drawing.Size(640, 32); this.labelReviewTitleBackground.TabIndex = 0; // // timerEvents // this.timerEvents.Enabled = true; this.timerEvents.Tick += new System.EventHandler(this.timerEvents_Tick); // // panelControls // this.panelControls.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelControls.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(247)))), ((int)(((byte)(230))))); this.panelControls.Controls.Add(this.buttonControlsExport); this.panelControls.Controls.Add(this.buttonControlsImport); this.panelControls.Controls.Add(this.buttonControlDown); this.panelControls.Controls.Add(this.buttonControlUp); this.panelControls.Controls.Add(this.buttonControlRemove); this.panelControls.Controls.Add(this.buttonControlAdd); this.panelControls.Controls.Add(this.groupboxJoysticks); this.panelControls.Controls.Add(this.listviewControls); this.panelControls.Controls.Add(this.labelControlsTitleSeparator); this.panelControls.Controls.Add(this.labelControlsTitle); this.panelControls.Controls.Add(this.labelControlsTitleBackground); this.panelControls.Controls.Add(this.groupboxControl); this.panelControls.Location = new System.Drawing.Point(160, 0); this.panelControls.Name = "panelControls"; this.panelControls.Size = new System.Drawing.Size(640, 584); this.panelControls.TabIndex = 13; // // buttonControlsExport // this.buttonControlsExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonControlsExport.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonControlsExport.Location = new System.Drawing.Point(320, 232); this.buttonControlsExport.Name = "buttonControlsExport"; this.buttonControlsExport.Size = new System.Drawing.Size(96, 24); this.buttonControlsExport.TabIndex = 7; this.buttonControlsExport.Text = "Export..."; this.buttonControlsExport.UseVisualStyleBackColor = true; this.buttonControlsExport.Click += new System.EventHandler(this.buttonControlsExport_Click); // // buttonControlsImport // this.buttonControlsImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonControlsImport.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonControlsImport.Location = new System.Drawing.Point(216, 232); this.buttonControlsImport.Name = "buttonControlsImport"; this.buttonControlsImport.Size = new System.Drawing.Size(96, 24); this.buttonControlsImport.TabIndex = 6; this.buttonControlsImport.Text = "Import..."; this.buttonControlsImport.UseVisualStyleBackColor = true; this.buttonControlsImport.Click += new System.EventHandler(this.buttonControlsImport_Click); // // buttonControlDown // this.buttonControlDown.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonControlDown.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonControlDown.Location = new System.Drawing.Point(528, 232); this.buttonControlDown.Name = "buttonControlDown"; this.buttonControlDown.Size = new System.Drawing.Size(96, 24); this.buttonControlDown.TabIndex = 9; this.buttonControlDown.Text = "Move down"; this.buttonControlDown.UseVisualStyleBackColor = true; this.buttonControlDown.Click += new System.EventHandler(this.buttonControlDown_Click); // // buttonControlUp // this.buttonControlUp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonControlUp.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonControlUp.Location = new System.Drawing.Point(424, 232); this.buttonControlUp.Name = "buttonControlUp"; this.buttonControlUp.Size = new System.Drawing.Size(96, 24); this.buttonControlUp.TabIndex = 8; this.buttonControlUp.Text = "Move up"; this.buttonControlUp.UseVisualStyleBackColor = true; this.buttonControlUp.Click += new System.EventHandler(this.buttonControlUp_Click); // // buttonControlRemove // this.buttonControlRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonControlRemove.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonControlRemove.Location = new System.Drawing.Point(112, 232); this.buttonControlRemove.Name = "buttonControlRemove"; this.buttonControlRemove.Size = new System.Drawing.Size(96, 24); this.buttonControlRemove.TabIndex = 5; this.buttonControlRemove.Text = "Remove"; this.buttonControlRemove.UseVisualStyleBackColor = true; this.buttonControlRemove.Click += new System.EventHandler(this.buttonControlRemove_Click); // // buttonControlAdd // this.buttonControlAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonControlAdd.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonControlAdd.Location = new System.Drawing.Point(8, 232); this.buttonControlAdd.Name = "buttonControlAdd"; this.buttonControlAdd.Size = new System.Drawing.Size(96, 24); this.buttonControlAdd.TabIndex = 4; this.buttonControlAdd.Text = "Add"; this.buttonControlAdd.UseVisualStyleBackColor = true; this.buttonControlAdd.Click += new System.EventHandler(this.buttonControlAdd_Click); // // groupboxJoysticks // this.groupboxJoysticks.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxJoysticks.Controls.Add(this.pictureboxJoysticks); this.groupboxJoysticks.ForeColor = System.Drawing.Color.Black; this.groupboxJoysticks.Location = new System.Drawing.Point(8, 408); this.groupboxJoysticks.Name = "groupboxJoysticks"; this.groupboxJoysticks.Size = new System.Drawing.Size(624, 168); this.groupboxJoysticks.TabIndex = 11; this.groupboxJoysticks.TabStop = false; this.groupboxJoysticks.Text = "Attached joysticks"; // // pictureboxJoysticks // this.pictureboxJoysticks.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.pictureboxJoysticks.Location = new System.Drawing.Point(8, 16); this.pictureboxJoysticks.Name = "pictureboxJoysticks"; this.pictureboxJoysticks.Size = new System.Drawing.Size(608, 144); this.pictureboxJoysticks.TabIndex = 27; this.pictureboxJoysticks.TabStop = false; this.pictureboxJoysticks.Paint += new System.Windows.Forms.PaintEventHandler(this.pictureboxJoysticks_Paint); // // listviewControls // this.listviewControls.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.listviewControls.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.columnheaderControlsCommands, this.columnheaderType, this.columnheaderControlsDescription, this.columnheaderControlsAssignment}); this.listviewControls.FullRowSelect = true; this.listviewControls.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; this.listviewControls.HideSelection = false; this.listviewControls.LabelWrap = false; this.listviewControls.Location = new System.Drawing.Point(8, 40); this.listviewControls.MultiSelect = false; this.listviewControls.Name = "listviewControls"; this.listviewControls.ShowGroups = false; this.listviewControls.Size = new System.Drawing.Size(624, 184); this.listviewControls.TabIndex = 3; this.listviewControls.UseCompatibleStateImageBehavior = false; this.listviewControls.View = System.Windows.Forms.View.Details; this.listviewControls.SelectedIndexChanged += new System.EventHandler(this.listviewControls_SelectedIndexChanged); // // columnheaderControlsCommands // this.columnheaderControlsCommands.Text = "Command"; this.columnheaderControlsCommands.Width = 155; // // columnheaderType // this.columnheaderType.Text = "Type"; // // columnheaderControlsDescription // this.columnheaderControlsDescription.Text = "Description"; this.columnheaderControlsDescription.Width = 373; // // columnheaderControlsAssignment // this.columnheaderControlsAssignment.Text = "Assignment"; this.columnheaderControlsAssignment.Width = 172; // // labelControlsTitleSeparator // this.labelControlsTitleSeparator.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelControlsTitleSeparator.BackColor = System.Drawing.Color.White; this.labelControlsTitleSeparator.Location = new System.Drawing.Point(0, 32); this.labelControlsTitleSeparator.Name = "labelControlsTitleSeparator"; this.labelControlsTitleSeparator.Size = new System.Drawing.Size(640, 2); this.labelControlsTitleSeparator.TabIndex = 2; // // labelControlsTitle // this.labelControlsTitle.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelControlsTitle.AutoEllipsis = true; this.labelControlsTitle.AutoSize = true; this.labelControlsTitle.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(179)))), ((int)(((byte)(137)))), ((int)(((byte)(54))))); this.labelControlsTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelControlsTitle.ForeColor = System.Drawing.Color.White; this.labelControlsTitle.Location = new System.Drawing.Point(8, 8); this.labelControlsTitle.Name = "labelControlsTitle"; this.labelControlsTitle.Size = new System.Drawing.Size(120, 16); this.labelControlsTitle.TabIndex = 1; this.labelControlsTitle.Text = "Customize controls"; // // labelControlsTitleBackground // this.labelControlsTitleBackground.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelControlsTitleBackground.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(179)))), ((int)(((byte)(137)))), ((int)(((byte)(54))))); this.labelControlsTitleBackground.Location = new System.Drawing.Point(0, 0); this.labelControlsTitleBackground.Name = "labelControlsTitleBackground"; this.labelControlsTitleBackground.Size = new System.Drawing.Size(640, 32); this.labelControlsTitleBackground.TabIndex = 0; // // groupboxControl // this.groupboxControl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxControl.Controls.Add(this.comboboxCommand); this.groupboxControl.Controls.Add(this.labelCommand); this.groupboxControl.Controls.Add(this.panelJoystick); this.groupboxControl.Controls.Add(this.panelKeyboard); this.groupboxControl.Controls.Add(this.radiobuttonJoystick); this.groupboxControl.Controls.Add(this.radiobuttonKeyboard); this.groupboxControl.Enabled = false; this.groupboxControl.ForeColor = System.Drawing.Color.Black; this.groupboxControl.Location = new System.Drawing.Point(8, 272); this.groupboxControl.Name = "groupboxControl"; this.groupboxControl.Size = new System.Drawing.Size(624, 128); this.groupboxControl.TabIndex = 10; this.groupboxControl.TabStop = false; this.groupboxControl.Text = "Currently selected control"; // // comboboxCommand // this.comboboxCommand.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.comboboxCommand.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxCommand.FormattingEnabled = true; this.comboboxCommand.Location = new System.Drawing.Point(88, 24); this.comboboxCommand.Name = "comboboxCommand"; this.comboboxCommand.Size = new System.Drawing.Size(528, 21); this.comboboxCommand.TabIndex = 1; this.comboboxCommand.SelectedIndexChanged += new System.EventHandler(this.comboboxCommand_SelectedIndexChanged); // // labelCommand // this.labelCommand.AutoEllipsis = true; this.labelCommand.Location = new System.Drawing.Point(8, 24); this.labelCommand.Name = "labelCommand"; this.labelCommand.Size = new System.Drawing.Size(80, 16); this.labelCommand.TabIndex = 0; this.labelCommand.Text = "Command:"; this.labelCommand.TextAlign = System.Drawing.ContentAlignment.TopRight; // // panelJoystick // this.panelJoystick.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelJoystick.Controls.Add(this.labelJoystickAssignmentCaption); this.panelJoystick.Controls.Add(this.textboxJoystickGrab); this.panelJoystick.Controls.Add(this.labelJoystickAssignmentValue); this.panelJoystick.Enabled = false; this.panelJoystick.Location = new System.Drawing.Point(264, 72); this.panelJoystick.Name = "panelJoystick"; this.panelJoystick.Size = new System.Drawing.Size(352, 48); this.panelJoystick.TabIndex = 4; // // labelJoystickAssignmentCaption // this.labelJoystickAssignmentCaption.AutoEllipsis = true; this.labelJoystickAssignmentCaption.Location = new System.Drawing.Point(0, 0); this.labelJoystickAssignmentCaption.Name = "labelJoystickAssignmentCaption"; this.labelJoystickAssignmentCaption.Size = new System.Drawing.Size(192, 16); this.labelJoystickAssignmentCaption.TabIndex = 0; this.labelJoystickAssignmentCaption.Text = "Assignment:"; // // textboxJoystickGrab // this.textboxJoystickGrab.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxJoystickGrab.Location = new System.Drawing.Point(192, 0); this.textboxJoystickGrab.Multiline = true; this.textboxJoystickGrab.Name = "textboxJoystickGrab"; this.textboxJoystickGrab.ReadOnly = true; this.textboxJoystickGrab.Size = new System.Drawing.Size(160, 48); this.textboxJoystickGrab.TabIndex = 10; this.textboxJoystickGrab.Text = "Joystick grab"; this.textboxJoystickGrab.Enter += new System.EventHandler(this.textboxJoystickGrab_Enter); this.textboxJoystickGrab.Leave += new System.EventHandler(this.textboxJoystickGrab_Leave); // // labelJoystickAssignmentValue // this.labelJoystickAssignmentValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.labelJoystickAssignmentValue.Location = new System.Drawing.Point(0, 16); this.labelJoystickAssignmentValue.Name = "labelJoystickAssignmentValue"; this.labelJoystickAssignmentValue.Size = new System.Drawing.Size(192, 32); this.labelJoystickAssignmentValue.TabIndex = 1; // // panelKeyboard // this.panelKeyboard.Controls.Add(this.comboboxKeyboardKey); this.panelKeyboard.Controls.Add(this.labelKeyboardKey); this.panelKeyboard.Controls.Add(this.checkboxKeyboardAlt); this.panelKeyboard.Controls.Add(this.checkboxKeyboardCtrl); this.panelKeyboard.Controls.Add(this.checkboxKeyboardShift); this.panelKeyboard.Controls.Add(this.labelKeyboardModifier); this.panelKeyboard.Enabled = false; this.panelKeyboard.Location = new System.Drawing.Point(8, 72); this.panelKeyboard.Name = "panelKeyboard"; this.panelKeyboard.Size = new System.Drawing.Size(248, 48); this.panelKeyboard.TabIndex = 5; // // comboboxKeyboardKey // this.comboboxKeyboardKey.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.comboboxKeyboardKey.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboboxKeyboardKey.FormattingEnabled = true; this.comboboxKeyboardKey.Location = new System.Drawing.Point(80, 0); this.comboboxKeyboardKey.Name = "comboboxKeyboardKey"; this.comboboxKeyboardKey.Size = new System.Drawing.Size(168, 21); this.comboboxKeyboardKey.TabIndex = 1; this.comboboxKeyboardKey.SelectedIndexChanged += new System.EventHandler(this.comboboxKeyboardKey_SelectedIndexChanged); // // labelKeyboardKey // this.labelKeyboardKey.AutoEllipsis = true; this.labelKeyboardKey.Location = new System.Drawing.Point(0, 0); this.labelKeyboardKey.Name = "labelKeyboardKey"; this.labelKeyboardKey.Size = new System.Drawing.Size(80, 16); this.labelKeyboardKey.TabIndex = 0; this.labelKeyboardKey.Text = "Key:"; this.labelKeyboardKey.TextAlign = System.Drawing.ContentAlignment.TopRight; // // checkboxKeyboardAlt // this.checkboxKeyboardAlt.AutoSize = true; this.checkboxKeyboardAlt.Location = new System.Drawing.Point(192, 24); this.checkboxKeyboardAlt.Name = "checkboxKeyboardAlt"; this.checkboxKeyboardAlt.Size = new System.Drawing.Size(38, 17); this.checkboxKeyboardAlt.TabIndex = 5; this.checkboxKeyboardAlt.Text = "Alt"; this.checkboxKeyboardAlt.UseVisualStyleBackColor = true; this.checkboxKeyboardAlt.CheckedChanged += new System.EventHandler(this.checkboxKeyboardAlt_CheckedChanged); // // checkboxKeyboardCtrl // this.checkboxKeyboardCtrl.AutoSize = true; this.checkboxKeyboardCtrl.Location = new System.Drawing.Point(136, 24); this.checkboxKeyboardCtrl.Name = "checkboxKeyboardCtrl"; this.checkboxKeyboardCtrl.Size = new System.Drawing.Size(41, 17); this.checkboxKeyboardCtrl.TabIndex = 4; this.checkboxKeyboardCtrl.Text = "Ctrl"; this.checkboxKeyboardCtrl.UseVisualStyleBackColor = true; this.checkboxKeyboardCtrl.CheckedChanged += new System.EventHandler(this.checkboxKeyboardCtrl_CheckedChanged); // // checkboxKeyboardShift // this.checkboxKeyboardShift.AutoSize = true; this.checkboxKeyboardShift.Location = new System.Drawing.Point(80, 24); this.checkboxKeyboardShift.Name = "checkboxKeyboardShift"; this.checkboxKeyboardShift.Size = new System.Drawing.Size(47, 17); this.checkboxKeyboardShift.TabIndex = 3; this.checkboxKeyboardShift.Text = "Shift"; this.checkboxKeyboardShift.UseVisualStyleBackColor = true; this.checkboxKeyboardShift.CheckedChanged += new System.EventHandler(this.checkboxKeyboardShift_CheckedChanged); // // labelKeyboardModifier // this.labelKeyboardModifier.AutoEllipsis = true; this.labelKeyboardModifier.Location = new System.Drawing.Point(0, 24); this.labelKeyboardModifier.Name = "labelKeyboardModifier"; this.labelKeyboardModifier.Size = new System.Drawing.Size(80, 16); this.labelKeyboardModifier.TabIndex = 2; this.labelKeyboardModifier.Text = "Modifiers:"; this.labelKeyboardModifier.TextAlign = System.Drawing.ContentAlignment.TopRight; // // radiobuttonJoystick // this.radiobuttonJoystick.AutoSize = true; this.radiobuttonJoystick.Location = new System.Drawing.Point(272, 48); this.radiobuttonJoystick.Name = "radiobuttonJoystick"; this.radiobuttonJoystick.Size = new System.Drawing.Size(66, 17); this.radiobuttonJoystick.TabIndex = 3; this.radiobuttonJoystick.TabStop = true; this.radiobuttonJoystick.Text = "Joystick:"; this.radiobuttonJoystick.UseVisualStyleBackColor = true; this.radiobuttonJoystick.CheckedChanged += new System.EventHandler(this.radiobuttonJoystick_CheckedChanged); // // radiobuttonKeyboard // this.radiobuttonKeyboard.AutoSize = true; this.radiobuttonKeyboard.Location = new System.Drawing.Point(8, 48); this.radiobuttonKeyboard.Name = "radiobuttonKeyboard"; this.radiobuttonKeyboard.Size = new System.Drawing.Size(73, 17); this.radiobuttonKeyboard.TabIndex = 2; this.radiobuttonKeyboard.TabStop = true; this.radiobuttonKeyboard.Text = "Keyboard:"; this.radiobuttonKeyboard.UseVisualStyleBackColor = true; this.radiobuttonKeyboard.CheckedChanged += new System.EventHandler(this.radiobuttonKeyboard_CheckedChanged); // // panelInfo // this.panelInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.panelInfo.BackColor = System.Drawing.Color.Gray; this.panelInfo.Controls.Add(this.labelInfoCenter); this.panelInfo.Controls.Add(this.linkUpdates); this.panelInfo.Controls.Add(this.linkHomepage); this.panelInfo.Controls.Add(this.labelVersion); this.panelInfo.Controls.Add(this.labelInfoBottom); this.panelInfo.Controls.Add(this.labelInfoTop); this.panelInfo.Location = new System.Drawing.Point(0, 472); this.panelInfo.Name = "panelInfo"; this.panelInfo.Size = new System.Drawing.Size(160, 64); this.panelInfo.TabIndex = 3; // // labelInfoCenter // this.labelInfoCenter.BackColor = System.Drawing.Color.White; this.labelInfoCenter.Location = new System.Drawing.Point(0, 24); this.labelInfoCenter.Name = "labelInfoCenter"; this.labelInfoCenter.Size = new System.Drawing.Size(160, 2); this.labelInfoCenter.TabIndex = 2; this.labelInfoCenter.Visible = false; // // linkUpdates // this.linkUpdates.ActiveLinkColor = System.Drawing.Color.Crimson; this.linkUpdates.AutoEllipsis = true; this.linkUpdates.DisabledLinkColor = System.Drawing.Color.Silver; this.linkUpdates.LinkBehavior = System.Windows.Forms.LinkBehavior.AlwaysUnderline; this.linkUpdates.LinkColor = System.Drawing.Color.Gold; this.linkUpdates.Location = new System.Drawing.Point(8, 40); this.linkUpdates.Name = "linkUpdates"; this.linkUpdates.Size = new System.Drawing.Size(128, 16); this.linkUpdates.TabIndex = 4; this.linkUpdates.TabStop = true; this.linkUpdates.Text = "Check for updates"; this.linkUpdates.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; this.linkUpdates.VisitedLinkColor = System.Drawing.Color.Gold; this.linkUpdates.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkUpdates_LinkClicked); // // linkHomepage // this.linkHomepage.ActiveLinkColor = System.Drawing.Color.Crimson; this.linkHomepage.AutoEllipsis = true; this.linkHomepage.DisabledLinkColor = System.Drawing.Color.Silver; this.linkHomepage.LinkBehavior = System.Windows.Forms.LinkBehavior.AlwaysUnderline; this.linkHomepage.LinkColor = System.Drawing.Color.Gold; this.linkHomepage.Location = new System.Drawing.Point(8, 24); this.linkHomepage.Name = "linkHomepage"; this.linkHomepage.Size = new System.Drawing.Size(128, 16); this.linkHomepage.TabIndex = 3; this.linkHomepage.TabStop = true; this.linkHomepage.Text = "Visit official homepage"; this.linkHomepage.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; this.linkHomepage.VisitedLinkColor = System.Drawing.Color.Gold; this.linkHomepage.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkHomepage_LinkClicked); // // labelVersion // this.labelVersion.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelVersion.AutoEllipsis = true; this.labelVersion.BackColor = System.Drawing.Color.Gray; this.labelVersion.ForeColor = System.Drawing.Color.White; this.labelVersion.Location = new System.Drawing.Point(8, 8); this.labelVersion.Name = "labelVersion"; this.labelVersion.Size = new System.Drawing.Size(144, 16); this.labelVersion.TabIndex = 1; this.labelVersion.Text = "v0.0.0.0"; this.labelVersion.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // labelInfoBottom // this.labelInfoBottom.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.labelInfoBottom.BackColor = System.Drawing.Color.White; this.labelInfoBottom.Location = new System.Drawing.Point(0, 62); this.labelInfoBottom.Name = "labelInfoBottom"; this.labelInfoBottom.Size = new System.Drawing.Size(160, 2); this.labelInfoBottom.TabIndex = 5; // // labelInfoTop // this.labelInfoTop.BackColor = System.Drawing.Color.White; this.labelInfoTop.Location = new System.Drawing.Point(0, 0); this.labelInfoTop.Name = "labelInfoTop"; this.labelInfoTop.Size = new System.Drawing.Size(160, 2); this.labelInfoTop.TabIndex = 0; // // panelGetAddOns // this.panelGetAddOns.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelGetAddOns.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(243)))), ((int)(((byte)(243))))); this.panelGetAddOns.Controls.Add(this.panelPackages); this.panelGetAddOns.Controls.Add(this.progressbarDownloading); this.panelGetAddOns.Controls.Add(this.labelDownloading); this.panelGetAddOns.Controls.Add(this.label18); this.panelGetAddOns.Controls.Add(this.labelGetAddOnsTitle); this.panelGetAddOns.Controls.Add(this.labelGetAddOnsBackground); this.panelGetAddOns.Location = new System.Drawing.Point(160, 0); this.panelGetAddOns.Name = "panelGetAddOns"; this.panelGetAddOns.Size = new System.Drawing.Size(640, 584); this.panelGetAddOns.TabIndex = 14; // // panelPackages // this.panelPackages.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelPackages.Controls.Add(this.checkboxFilterNoWIPs); this.panelPackages.Controls.Add(this.groupboxPackage); this.panelPackages.Controls.Add(this.treeviewPackages); this.panelPackages.Controls.Add(this.checkboxFilterUpdates); this.panelPackages.Controls.Add(this.checkboxFilterSharedLibraries); this.panelPackages.Controls.Add(this.checkboxFilterLibraries); this.panelPackages.Controls.Add(this.checkboxFilterTrains); this.panelPackages.Controls.Add(this.checkboxFilterRoutes); this.panelPackages.Controls.Add(this.textboxFilter); this.panelPackages.Controls.Add(this.labelFilter); this.panelPackages.Enabled = false; this.panelPackages.Location = new System.Drawing.Point(8, 40); this.panelPackages.Name = "panelPackages"; this.panelPackages.Size = new System.Drawing.Size(624, 480); this.panelPackages.TabIndex = 0; // // checkboxFilterNoWIPs // this.checkboxFilterNoWIPs.AutoSize = true; this.checkboxFilterNoWIPs.Location = new System.Drawing.Point(424, 24); this.checkboxFilterNoWIPs.Name = "checkboxFilterNoWIPs"; this.checkboxFilterNoWIPs.Size = new System.Drawing.Size(69, 17); this.checkboxFilterNoWIPs.TabIndex = 6; this.checkboxFilterNoWIPs.Text = "No WIPs"; this.checkboxFilterNoWIPs.UseVisualStyleBackColor = true; this.checkboxFilterNoWIPs.CheckedChanged += new System.EventHandler(this.CheckboxFilterNoWIPsCheckedChanged); // // groupboxPackage // this.groupboxPackage.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.groupboxPackage.Controls.Add(this.linkPackageHomepage); this.groupboxPackage.Controls.Add(this.buttonScreenshotNext); this.groupboxPackage.Controls.Add(this.buttonScreenshotPrevious); this.groupboxPackage.Controls.Add(this.pictureboxScreenshot); this.groupboxPackage.Controls.Add(this.labelPackageInformation); this.groupboxPackage.Controls.Add(this.textboxPackageDescription); this.groupboxPackage.Controls.Add(this.buttonPackageInstall); this.groupboxPackage.Controls.Add(this.buttonPackageRemove); this.groupboxPackage.Location = new System.Drawing.Point(0, 288); this.groupboxPackage.Name = "groupboxPackage"; this.groupboxPackage.Size = new System.Drawing.Size(624, 192); this.groupboxPackage.TabIndex = 1; this.groupboxPackage.TabStop = false; this.groupboxPackage.Text = "Package details"; // // linkPackageHomepage // this.linkPackageHomepage.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.linkPackageHomepage.Location = new System.Drawing.Point(216, 152); this.linkPackageHomepage.Name = "linkPackageHomepage"; this.linkPackageHomepage.Size = new System.Drawing.Size(192, 32); this.linkPackageHomepage.TabIndex = 4; this.linkPackageHomepage.UseMnemonic = false; this.linkPackageHomepage.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.LinkPackageHomepageLinkClicked); // // buttonScreenshotNext // this.buttonScreenshotNext.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonScreenshotNext.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonScreenshotNext.Enabled = false; this.buttonScreenshotNext.Location = new System.Drawing.Point(520, 160); this.buttonScreenshotNext.Name = "buttonScreenshotNext"; this.buttonScreenshotNext.Size = new System.Drawing.Size(96, 24); this.buttonScreenshotNext.TabIndex = 6; this.buttonScreenshotNext.Text = "Next"; this.buttonScreenshotNext.UseVisualStyleBackColor = true; this.buttonScreenshotNext.Click += new System.EventHandler(this.ButtonScreenshotNextClick); // // buttonScreenshotPrevious // this.buttonScreenshotPrevious.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonScreenshotPrevious.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonScreenshotPrevious.Enabled = false; this.buttonScreenshotPrevious.Location = new System.Drawing.Point(416, 160); this.buttonScreenshotPrevious.Name = "buttonScreenshotPrevious"; this.buttonScreenshotPrevious.Size = new System.Drawing.Size(96, 24); this.buttonScreenshotPrevious.TabIndex = 5; this.buttonScreenshotPrevious.Text = "Previous"; this.buttonScreenshotPrevious.UseVisualStyleBackColor = true; this.buttonScreenshotPrevious.Click += new System.EventHandler(this.ButtonScreenshotPreviousClick); // // pictureboxScreenshot // this.pictureboxScreenshot.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Right))); this.pictureboxScreenshot.Cursor = System.Windows.Forms.Cursors.Default; this.pictureboxScreenshot.Location = new System.Drawing.Point(416, 16); this.pictureboxScreenshot.Name = "pictureboxScreenshot"; this.pictureboxScreenshot.Size = new System.Drawing.Size(200, 136); this.pictureboxScreenshot.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureboxScreenshot.TabIndex = 10; this.pictureboxScreenshot.TabStop = false; this.pictureboxScreenshot.Click += new System.EventHandler(this.PictureboxScreenshotClick); // // labelPackageInformation // this.labelPackageInformation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.labelPackageInformation.Location = new System.Drawing.Point(8, 16); this.labelPackageInformation.Name = "labelPackageInformation"; this.labelPackageInformation.Size = new System.Drawing.Size(200, 136); this.labelPackageInformation.TabIndex = 0; this.labelPackageInformation.UseMnemonic = false; // // textboxPackageDescription // this.textboxPackageDescription.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxPackageDescription.BackColor = System.Drawing.SystemColors.Window; this.textboxPackageDescription.Location = new System.Drawing.Point(216, 16); this.textboxPackageDescription.Multiline = true; this.textboxPackageDescription.Name = "textboxPackageDescription"; this.textboxPackageDescription.ReadOnly = true; this.textboxPackageDescription.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textboxPackageDescription.Size = new System.Drawing.Size(192, 136); this.textboxPackageDescription.TabIndex = 3; // // buttonPackageInstall // this.buttonPackageInstall.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonPackageInstall.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonPackageInstall.Enabled = false; this.buttonPackageInstall.Location = new System.Drawing.Point(8, 160); this.buttonPackageInstall.Name = "buttonPackageInstall"; this.buttonPackageInstall.Size = new System.Drawing.Size(96, 24); this.buttonPackageInstall.TabIndex = 1; this.buttonPackageInstall.Text = "Install"; this.buttonPackageInstall.UseVisualStyleBackColor = true; this.buttonPackageInstall.Click += new System.EventHandler(this.ButtonPackageInstallClick); // // buttonPackageRemove // this.buttonPackageRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.buttonPackageRemove.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonPackageRemove.Enabled = false; this.buttonPackageRemove.Location = new System.Drawing.Point(112, 160); this.buttonPackageRemove.Name = "buttonPackageRemove"; this.buttonPackageRemove.Size = new System.Drawing.Size(96, 24); this.buttonPackageRemove.TabIndex = 2; this.buttonPackageRemove.Text = "Remove"; this.buttonPackageRemove.UseVisualStyleBackColor = true; this.buttonPackageRemove.Click += new System.EventHandler(this.ButtonPackageRemoveClick); // // treeviewPackages // this.treeviewPackages.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.treeviewPackages.HideSelection = false; this.treeviewPackages.Location = new System.Drawing.Point(0, 64); this.treeviewPackages.Name = "treeviewPackages"; this.treeviewPackages.Size = new System.Drawing.Size(624, 216); this.treeviewPackages.TabIndex = 8; this.treeviewPackages.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.TreeviewPackagesAfterSelect); // // checkboxFilterUpdates // this.checkboxFilterUpdates.AutoSize = true; this.checkboxFilterUpdates.Location = new System.Drawing.Point(424, 40); this.checkboxFilterUpdates.Name = "checkboxFilterUpdates"; this.checkboxFilterUpdates.Size = new System.Drawing.Size(88, 17); this.checkboxFilterUpdates.TabIndex = 7; this.checkboxFilterUpdates.Text = "Only updates"; this.checkboxFilterUpdates.UseVisualStyleBackColor = true; this.checkboxFilterUpdates.CheckedChanged += new System.EventHandler(this.CheckboxFilterUpdatesCheckedChanged); // // checkboxFilterSharedLibraries // this.checkboxFilterSharedLibraries.AutoSize = true; this.checkboxFilterSharedLibraries.Location = new System.Drawing.Point(184, 40); this.checkboxFilterSharedLibraries.Name = "checkboxFilterSharedLibraries"; this.checkboxFilterSharedLibraries.Size = new System.Drawing.Size(98, 17); this.checkboxFilterSharedLibraries.TabIndex = 5; this.checkboxFilterSharedLibraries.Text = "Shared libraries"; this.checkboxFilterSharedLibraries.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; this.checkboxFilterSharedLibraries.UseVisualStyleBackColor = true; this.checkboxFilterSharedLibraries.CheckedChanged += new System.EventHandler(this.CheckboxFilterSharedLibrariesCheckedChanged); // // checkboxFilterLibraries // this.checkboxFilterLibraries.AutoSize = true; this.checkboxFilterLibraries.Location = new System.Drawing.Point(184, 24); this.checkboxFilterLibraries.Name = "checkboxFilterLibraries"; this.checkboxFilterLibraries.Size = new System.Drawing.Size(65, 17); this.checkboxFilterLibraries.TabIndex = 4; this.checkboxFilterLibraries.Text = "Libraries"; this.checkboxFilterLibraries.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; this.checkboxFilterLibraries.UseVisualStyleBackColor = true; this.checkboxFilterLibraries.CheckedChanged += new System.EventHandler(this.CheckboxFilterLibrariesCheckedChanged); // // checkboxFilterTrains // this.checkboxFilterTrains.AutoSize = true; this.checkboxFilterTrains.Checked = true; this.checkboxFilterTrains.CheckState = System.Windows.Forms.CheckState.Checked; this.checkboxFilterTrains.Location = new System.Drawing.Point(64, 40); this.checkboxFilterTrains.Name = "checkboxFilterTrains"; this.checkboxFilterTrains.Size = new System.Drawing.Size(55, 17); this.checkboxFilterTrains.TabIndex = 3; this.checkboxFilterTrains.Text = "Trains"; this.checkboxFilterTrains.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; this.checkboxFilterTrains.UseVisualStyleBackColor = true; this.checkboxFilterTrains.CheckedChanged += new System.EventHandler(this.CheckboxFilterTrainsCheckedChanged); // // checkboxFilterRoutes // this.checkboxFilterRoutes.AutoSize = true; this.checkboxFilterRoutes.Checked = true; this.checkboxFilterRoutes.CheckState = System.Windows.Forms.CheckState.Checked; this.checkboxFilterRoutes.Location = new System.Drawing.Point(64, 24); this.checkboxFilterRoutes.Name = "checkboxFilterRoutes"; this.checkboxFilterRoutes.Size = new System.Drawing.Size(60, 17); this.checkboxFilterRoutes.TabIndex = 2; this.checkboxFilterRoutes.Text = "Routes"; this.checkboxFilterRoutes.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; this.checkboxFilterRoutes.UseVisualStyleBackColor = true; this.checkboxFilterRoutes.CheckedChanged += new System.EventHandler(this.CheckboxFilterRoutesCheckedChanged); // // textboxFilter // this.textboxFilter.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textboxFilter.Location = new System.Drawing.Point(64, 0); this.textboxFilter.Name = "textboxFilter"; this.textboxFilter.Size = new System.Drawing.Size(560, 20); this.textboxFilter.TabIndex = 1; this.textboxFilter.TextChanged += new System.EventHandler(this.TextboxFilterTextChanged); // // labelFilter // this.labelFilter.Location = new System.Drawing.Point(0, 0); this.labelFilter.Name = "labelFilter"; this.labelFilter.Size = new System.Drawing.Size(64, 16); this.labelFilter.TabIndex = 0; this.labelFilter.Text = "Filter:"; this.labelFilter.TextAlign = System.Drawing.ContentAlignment.TopRight; // // progressbarDownloading // this.progressbarDownloading.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.progressbarDownloading.Location = new System.Drawing.Point(8, 560); this.progressbarDownloading.Name = "progressbarDownloading"; this.progressbarDownloading.Size = new System.Drawing.Size(624, 16); this.progressbarDownloading.TabIndex = 3; // // labelDownloading // this.labelDownloading.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelDownloading.Location = new System.Drawing.Point(8, 528); this.labelDownloading.Name = "labelDownloading"; this.labelDownloading.Size = new System.Drawing.Size(624, 32); this.labelDownloading.TabIndex = 2; // // label18 // this.label18.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.label18.BackColor = System.Drawing.Color.White; this.label18.Location = new System.Drawing.Point(0, 32); this.label18.Name = "label18"; this.label18.Size = new System.Drawing.Size(640, 2); this.label18.TabIndex = 2; // // labelGetAddOnsTitle // this.labelGetAddOnsTitle.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelGetAddOnsTitle.AutoEllipsis = true; this.labelGetAddOnsTitle.AutoSize = true; this.labelGetAddOnsTitle.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(179)))), ((int)(((byte)(54)))), ((int)(((byte)(54))))); this.labelGetAddOnsTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelGetAddOnsTitle.ForeColor = System.Drawing.Color.White; this.labelGetAddOnsTitle.Location = new System.Drawing.Point(8, 8); this.labelGetAddOnsTitle.Name = "labelGetAddOnsTitle"; this.labelGetAddOnsTitle.Size = new System.Drawing.Size(83, 16); this.labelGetAddOnsTitle.TabIndex = 1; this.labelGetAddOnsTitle.Text = "Get Add-ons"; // // labelGetAddOnsBackground // this.labelGetAddOnsBackground.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelGetAddOnsBackground.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(179)))), ((int)(((byte)(54)))), ((int)(((byte)(54))))); this.labelGetAddOnsBackground.Location = new System.Drawing.Point(0, 0); this.labelGetAddOnsBackground.Name = "labelGetAddOnsBackground"; this.labelGetAddOnsBackground.Size = new System.Drawing.Size(640, 32); this.labelGetAddOnsBackground.TabIndex = 0; // // timerInstall // this.timerInstall.Interval = 250; this.timerInstall.Tick += new System.EventHandler(this.TimerInstallTick); // // timerFilter // this.timerFilter.Interval = 250; this.timerFilter.Tick += new System.EventHandler(this.TimerFilterTick); // // formMain // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.BackColor = System.Drawing.Color.White; this.ClientSize = new System.Drawing.Size(800, 584); this.Controls.Add(this.labelVerticalSeparator); this.Controls.Add(this.panelInfo); this.Controls.Add(this.panelPanels); this.Controls.Add(this.buttonClose); this.Controls.Add(this.pictureboxLogo); this.Controls.Add(this.labelFillerOne); this.Controls.Add(this.labelFillerTwo); this.Controls.Add(this.labelFillerThree); this.Controls.Add(this.panelStart); this.Controls.Add(this.panelGetAddOns); this.Controls.Add(this.panelOptions); this.Controls.Add(this.panelControls); this.Controls.Add(this.panelReview); this.KeyPreview = true; this.Name = "formMain"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "openBVE"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.formMain_FormClosing); this.Load += new System.EventHandler(this.formMain_Load); this.Shown += new System.EventHandler(this.formMain_Shown); this.Resize += new System.EventHandler(this.formMain_Resize); ((System.ComponentModel.ISupportInitialize)(this.pictureboxLogo)).EndInit(); this.panelStart.ResumeLayout(false); this.panelStart.PerformLayout(); this.groupboxTrainSelection.ResumeLayout(false); this.tabcontrolTrainSelection.ResumeLayout(false); this.tabpageTrainManaged.ResumeLayout(false); this.tabpageTrainManaged.PerformLayout(); this.tabpageTrainBrowse.ResumeLayout(false); this.tabpageTrainBrowse.PerformLayout(); this.tabpageTrainRecently.ResumeLayout(false); this.tabpageTrainDefault.ResumeLayout(false); this.tabpageTrainDefault.PerformLayout(); this.groupboxTrainDetails.ResumeLayout(false); this.tabcontrolTrainDetails.ResumeLayout(false); this.tabpageTrainDescription.ResumeLayout(false); this.tabpageTrainDescription.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxTrainImage)).EndInit(); this.tabpageTrainSettings.ResumeLayout(false); this.tabpageTrainSettings.PerformLayout(); this.panelTrainEncoding.ResumeLayout(false); this.groupboxRouteSelection.ResumeLayout(false); this.tabcontrolRouteSelection.ResumeLayout(false); this.tabpageRouteManaged.ResumeLayout(false); this.tabpageRouteManaged.PerformLayout(); this.tabpageRouteBrowse.ResumeLayout(false); this.tabpageRouteBrowse.PerformLayout(); this.tabpageRouteRecently.ResumeLayout(false); this.groupboxRouteDetails.ResumeLayout(false); this.tabcontrolRouteDetails.ResumeLayout(false); this.tabpageRouteDescription.ResumeLayout(false); this.tabpageRouteDescription.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxRouteImage)).EndInit(); this.tabpageRouteMap.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.pictureboxRouteMap)).EndInit(); this.tabpageRouteGradient.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.pictureboxRouteGradient)).EndInit(); this.tabpageRouteSettings.ResumeLayout(false); this.tabpageRouteSettings.PerformLayout(); this.panelRouteEncoding.ResumeLayout(false); this.panelOptions.ResumeLayout(false); this.panelOptions.PerformLayout(); this.panelOptionsRight.ResumeLayout(false); this.groupboxDistance.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.updownDistance)).EndInit(); this.groupboxControls.ResumeLayout(false); this.groupboxControls.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.trackbarJoystickAxisThreshold)).EndInit(); this.groupboxVerbosity.ResumeLayout(false); this.groupboxVerbosity.PerformLayout(); this.groupboxSimulation.ResumeLayout(false); this.groupboxSimulation.PerformLayout(); this.groupboxSound.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.updownSoundNumber)).EndInit(); this.panelOptionsLeft.ResumeLayout(false); this.groupboxDisplayMode.ResumeLayout(false); this.groupboxDisplayMode.PerformLayout(); this.groupboxWindow.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.updownWindowHeight)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.updownWindowWidth)).EndInit(); this.groupboxFullscreen.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.updownFullscreenHeight)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.updownFullscreenWidth)).EndInit(); this.groupboxInterpolation.ResumeLayout(false); this.groupboxInterpolation.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.updownAnisotropic)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackbarTransparency)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxLanguage)).EndInit(); this.panelPanels.ResumeLayout(false); this.panelPanels.PerformLayout(); this.panelReview.ResumeLayout(false); this.panelReview.PerformLayout(); this.groupboxReviewDateTime.ResumeLayout(false); this.groupboxScore.ResumeLayout(false); this.groupboxScore.PerformLayout(); this.groupboxReviewTrain.ResumeLayout(false); this.groupboxReviewTrain.PerformLayout(); this.groupboxReviewRoute.ResumeLayout(false); this.groupboxReviewRoute.PerformLayout(); this.groupboxRating.ResumeLayout(false); this.panelControls.ResumeLayout(false); this.panelControls.PerformLayout(); this.groupboxJoysticks.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.pictureboxJoysticks)).EndInit(); this.groupboxControl.ResumeLayout(false); this.groupboxControl.PerformLayout(); this.panelJoystick.ResumeLayout(false); this.panelJoystick.PerformLayout(); this.panelKeyboard.ResumeLayout(false); this.panelKeyboard.PerformLayout(); this.panelInfo.ResumeLayout(false); this.panelGetAddOns.ResumeLayout(false); this.panelGetAddOns.PerformLayout(); this.panelPackages.ResumeLayout(false); this.panelPackages.PerformLayout(); this.groupboxPackage.ResumeLayout(false); this.groupboxPackage.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureboxScreenshot)).EndInit(); this.ResumeLayout(false); } private System.Windows.Forms.CheckBox checkboxFilterNoWIPs; private System.Windows.Forms.LinkLabel linkPackageHomepage; private System.Windows.Forms.PictureBox pictureboxScreenshot; private System.Windows.Forms.Button buttonScreenshotPrevious; private System.Windows.Forms.Button buttonScreenshotNext; private System.Windows.Forms.TextBox textboxTrainFilter; private System.Windows.Forms.TreeView treeviewTrainAddOns; private System.Windows.Forms.TreeView treeviewRouteAddOns; private System.Windows.Forms.ListView listviewRouteFiles; private System.Windows.Forms.TextBox textboxRouteFilter; private System.Windows.Forms.Label labelPackageInformation; private System.Windows.Forms.TextBox textboxPackageDescription; private System.Windows.Forms.GroupBox groupboxPackage; private System.Windows.Forms.TreeView treeviewPackages; private System.Windows.Forms.CheckBox checkboxTrainDefault; private System.Windows.Forms.Timer timerFilter; private System.Windows.Forms.CheckBox checkboxFilterRoutes; private System.Windows.Forms.CheckBox checkboxFilterTrains; private System.Windows.Forms.CheckBox checkboxFilterLibraries; private System.Windows.Forms.CheckBox checkboxFilterSharedLibraries; private System.Windows.Forms.CheckBox checkboxFilterUpdates; private System.Windows.Forms.Timer timerInstall; private System.Windows.Forms.Panel panelPackages; private System.Windows.Forms.Button buttonPackageRemove; private System.Windows.Forms.Button buttonPackageInstall; private System.Windows.Forms.Label labelFilter; private System.Windows.Forms.TextBox textboxFilter; private System.Windows.Forms.ProgressBar progressbarDownloading; private System.Windows.Forms.Label labelDownloading; private System.Windows.Forms.Label labelGetAddOnsTitle; private System.Windows.Forms.Label labelGetAddOnsBackground; private System.Windows.Forms.Label label18; private System.Windows.Forms.Panel panelGetAddOns; private System.Windows.Forms.RadioButton radiobuttonGetAddOns; private System.Windows.Forms.TabPage tabpageRouteManaged; private System.Windows.Forms.TabPage tabpageTrainManaged; private System.Windows.Forms.Label labelTransparencyPerformance; private System.Windows.Forms.Label labelTransparencyQuality; private System.Windows.Forms.TrackBar trackbarTransparency; private System.Windows.Forms.Panel panelOptionsLeft; private System.Windows.Forms.Panel panelOptionsRight; private System.Windows.Forms.ComboBox comboboxVSync; private System.Windows.Forms.Label labelVSync; private System.Windows.Forms.CheckBox checkboxWarningMessages; private System.Windows.Forms.CheckBox checkboxErrorMessages; private System.Windows.Forms.GroupBox groupboxVerbosity; #endregion private System.Windows.Forms.Label labelFillerOne; private System.Windows.Forms.PictureBox pictureboxLogo; private System.Windows.Forms.Label labelVerticalSeparator; private System.Windows.Forms.Button buttonClose; private System.Windows.Forms.Panel panelStart; private System.Windows.Forms.Label labelFillerTwo; private System.Windows.Forms.Panel panelOptions; private System.Windows.Forms.Label labelStartTitle; private System.Windows.Forms.Label labelStartTitleBackground; private System.Windows.Forms.Label labelOptionsTitle; private System.Windows.Forms.Label labelOptionsTitleBackground; private System.Windows.Forms.Label labelFillerThree; private System.Windows.Forms.GroupBox groupboxFullscreen; private System.Windows.Forms.GroupBox groupboxWindow; private System.Windows.Forms.GroupBox groupboxDisplayMode; private System.Windows.Forms.RadioButton radiobuttonFullscreen; private System.Windows.Forms.RadioButton radiobuttonWindow; private System.Windows.Forms.Label labelOptionsTitleSeparator; private System.Windows.Forms.Label labelStartTitleSeparator; private System.Windows.Forms.Panel panelPanels; private System.Windows.Forms.Label labelPanelsBottom; private System.Windows.Forms.Label labelPanelsTop; private System.Windows.Forms.NumericUpDown updownWindowWidth; private System.Windows.Forms.Label labelWindowWidth; private System.Windows.Forms.NumericUpDown updownWindowHeight; private System.Windows.Forms.Label labelWindowHeight; private System.Windows.Forms.PictureBox pictureboxLanguage; private System.Windows.Forms.ComboBox comboboxLanguages; private System.Windows.Forms.GroupBox groupboxDistance; private System.Windows.Forms.NumericUpDown updownDistance; private System.Windows.Forms.Label labelDistance; private System.Windows.Forms.GroupBox groupboxInterpolation; private System.Windows.Forms.ComboBox comboboxInterpolation; private System.Windows.Forms.Label labelInterpolation; private System.Windows.Forms.NumericUpDown updownAnisotropic; private System.Windows.Forms.Label labelAnisotropic; private System.Windows.Forms.Label labelDistanceUnit; private System.Windows.Forms.ComboBox comboboxMotionBlur; private System.Windows.Forms.Label labelMotionBlur; private System.Windows.Forms.Panel panelReview; private System.Windows.Forms.Label labelReviewTitleSeparator; private System.Windows.Forms.Label labelReviewTitle; private System.Windows.Forms.Label labelReviewTitleBackground; private System.Windows.Forms.GroupBox groupboxRouteSelection; private System.Windows.Forms.TabControl tabcontrolRouteSelection; private System.Windows.Forms.TabPage tabpageRouteBrowse; private System.Windows.Forms.TabPage tabpageRouteRecently; private System.Windows.Forms.GroupBox groupboxRouteDetails; private System.Windows.Forms.Label labelRoute; private System.Windows.Forms.TextBox textboxRouteFolder; private System.Windows.Forms.Button buttonStart; private System.Windows.Forms.Label labelStart; private System.Windows.Forms.Label labelTrain; private System.Windows.Forms.TabControl tabcontrolRouteDetails; private System.Windows.Forms.TabPage tabpageRouteDescription; private System.Windows.Forms.TextBox textboxRouteDescription; private System.Windows.Forms.PictureBox pictureboxRouteImage; private System.Windows.Forms.TabPage tabpageRouteMap; private System.Windows.Forms.TabPage tabpageRouteGradient; private System.Windows.Forms.TabPage tabpageRouteSettings; private System.Windows.Forms.GroupBox groupboxTrainSelection; private System.Windows.Forms.TabControl tabcontrolTrainSelection; private System.Windows.Forms.TabPage tabpageTrainBrowse; private System.Windows.Forms.TextBox textboxTrainFolder; private System.Windows.Forms.TabPage tabpageTrainRecently; private System.Windows.Forms.GroupBox groupboxTrainDetails; private System.Windows.Forms.TabControl tabcontrolTrainDetails; private System.Windows.Forms.TabPage tabpageTrainDescription; private System.Windows.Forms.TextBox textboxTrainDescription; private System.Windows.Forms.PictureBox pictureboxTrainImage; private System.Windows.Forms.TabPage tabpageTrainSettings; private System.Windows.Forms.PictureBox pictureboxRouteMap; private System.Windows.Forms.ComboBox comboboxRouteEncoding; private System.Windows.Forms.Label labelRouteEncoding; private System.Windows.Forms.TextBox textboxRouteEncodingPreview; private System.Windows.Forms.Label labelRouteEncodingPreview; private System.Windows.Forms.ListView listviewTrainFolders; private System.Windows.Forms.ListView listviewTrainRecently; private System.Windows.Forms.ListView listviewRouteRecently; private System.Windows.Forms.TabPage tabpageTrainDefault; private System.Windows.Forms.Label labelTrainEncodingPreview; private System.Windows.Forms.TextBox textboxTrainEncodingPreview; private System.Windows.Forms.ComboBox comboboxTrainEncoding; private System.Windows.Forms.Label labelTrainEncoding; private System.Windows.Forms.ComboBox comboboxFullscreenBits; private System.Windows.Forms.Label labelFullscreenBits; private System.Windows.Forms.NumericUpDown updownFullscreenHeight; private System.Windows.Forms.Label labelFullscreenHeight; private System.Windows.Forms.NumericUpDown updownFullscreenWidth; private System.Windows.Forms.Label labelFullscreenWidth; private System.Windows.Forms.Timer timerEvents; private System.Windows.Forms.Panel panelControls; private System.Windows.Forms.ListView listviewControls; private System.Windows.Forms.ColumnHeader columnheaderControlsCommands; private System.Windows.Forms.ColumnHeader columnheaderControlsDescription; private System.Windows.Forms.ColumnHeader columnheaderControlsAssignment; private System.Windows.Forms.Label labelControlsTitleSeparator; private System.Windows.Forms.Label labelControlsTitle; private System.Windows.Forms.Label labelControlsTitleBackground; private System.Windows.Forms.GroupBox groupboxControl; private System.Windows.Forms.RadioButton radiobuttonJoystick; private System.Windows.Forms.RadioButton radiobuttonKeyboard; private System.Windows.Forms.Panel panelKeyboard; private System.Windows.Forms.ComboBox comboboxKeyboardKey; private System.Windows.Forms.Label labelKeyboardKey; private System.Windows.Forms.CheckBox checkboxKeyboardAlt; private System.Windows.Forms.CheckBox checkboxKeyboardCtrl; private System.Windows.Forms.CheckBox checkboxKeyboardShift; private System.Windows.Forms.Label labelKeyboardModifier; private System.Windows.Forms.Panel panelJoystick; private System.Windows.Forms.ComboBox comboboxCommand; private System.Windows.Forms.Label labelCommand; private System.Windows.Forms.Label labelJoystickAssignmentValue; private System.Windows.Forms.GroupBox groupboxJoysticks; private System.Windows.Forms.PictureBox pictureboxJoysticks; private System.Windows.Forms.TextBox textboxJoystickGrab; private System.Windows.Forms.Panel panelInfo; private System.Windows.Forms.LinkLabel linkHomepage; private System.Windows.Forms.Label labelVersion; private System.Windows.Forms.Label labelInfoBottom; private System.Windows.Forms.Label labelInfoTop; private System.Windows.Forms.Button buttonControlDown; private System.Windows.Forms.Button buttonControlUp; private System.Windows.Forms.Button buttonControlRemove; private System.Windows.Forms.Button buttonControlAdd; private System.Windows.Forms.ColumnHeader columnheaderType; private System.Windows.Forms.Label labelJoystickAssignmentCaption; private System.Windows.Forms.GroupBox groupboxControls; private System.Windows.Forms.CheckBox checkboxJoysticksUsed; private System.Windows.Forms.Label labelJoystickAxisThreshold; private System.Windows.Forms.TrackBar trackbarJoystickAxisThreshold; private System.Windows.Forms.GroupBox groupboxSimulation; private System.Windows.Forms.CheckBox checkboxDerailments; private System.Windows.Forms.CheckBox checkboxCollisions; private System.Windows.Forms.CheckBox checkboxToppling; private System.Windows.Forms.GroupBox groupboxSound; private System.Windows.Forms.NumericUpDown updownSoundNumber; private System.Windows.Forms.Label labelSoundNumber; private System.Windows.Forms.ComboBox comboboxSoundRange; private System.Windows.Forms.Label labelSoundRange; private System.Windows.Forms.PictureBox pictureboxRouteGradient; private System.Windows.Forms.GroupBox groupboxRating; private System.Windows.Forms.Label labelRatingDescription; private System.Windows.Forms.Label labelRatingColor; private System.Windows.Forms.Label labelScore; private System.Windows.Forms.Label labelConditions; private System.Windows.Forms.GroupBox groupboxReviewTrain; private System.Windows.Forms.Label labelReviewTrainValue; private System.Windows.Forms.Label labelReviewTrainCaption; private System.Windows.Forms.GroupBox groupboxReviewRoute; private System.Windows.Forms.Label labelReviewRouteValue; private System.Windows.Forms.Label labelReviewRouteCaption; private System.Windows.Forms.GroupBox groupboxScore; private System.Windows.Forms.ListView listviewScore; private System.Windows.Forms.ColumnHeader columnheaderScoreTime; private System.Windows.Forms.ColumnHeader columnheaderScorePosition; private System.Windows.Forms.ColumnHeader columnheaderScoreValue; private System.Windows.Forms.ColumnHeader columnheaderScoreText; private System.Windows.Forms.Button buttonScoreExport; private System.Windows.Forms.Label labelBlackBox; private System.Windows.Forms.Button buttonBlackBoxExport; private System.Windows.Forms.GroupBox groupboxReviewDateTime; private System.Windows.Forms.Label labelReviewTimeValue; private System.Windows.Forms.Label labelReviewTimeCaption; private System.Windows.Forms.Label labelReviewDateValue; private System.Windows.Forms.Label labelReviewDateCaption; private System.Windows.Forms.ComboBox comboboxBlackBoxFormat; private System.Windows.Forms.Label labelBlackBoxFormat; private System.Windows.Forms.Label labelRatingRatioValue; private System.Windows.Forms.Label labelRatingRatioCaption; private System.Windows.Forms.Label labelRatingMaximumValue; private System.Windows.Forms.Label labelRatingMaximumCaption; private System.Windows.Forms.Label labelRatingAchievedValue; private System.Windows.Forms.Label labelRatingAchievedCaption; private System.Windows.Forms.Label labelRatingModeValue; private System.Windows.Forms.Label labelRatingModeCaption; private System.Windows.Forms.ColumnHeader columnheaderScoreCumulative; private System.Windows.Forms.ComboBox comboboxMode; private System.Windows.Forms.Label labelMode; private System.Windows.Forms.CheckBox checkboxScorePenalties; private System.Windows.Forms.CheckBox checkboxBlackBox; private System.Windows.Forms.Button buttonControlsExport; private System.Windows.Forms.Button buttonControlsImport; private System.Windows.Forms.Label labelTransparency; private System.Windows.Forms.LinkLabel linkUpdates; private System.Windows.Forms.RadioButton radiobuttonStart; private System.Windows.Forms.RadioButton radiobuttonOptions; private System.Windows.Forms.RadioButton radiobuttonControls; private System.Windows.Forms.RadioButton radiobuttonReview; private System.Windows.Forms.Button buttonRouteEncodingLatin1; private System.Windows.Forms.Button buttonRouteEncodingBig5; private System.Windows.Forms.Button buttonRouteEncodingShiftJis; private System.Windows.Forms.Button buttonTrainEncodingBig5; private System.Windows.Forms.Button buttonTrainEncodingShiftJis; private System.Windows.Forms.Button buttonTrainEncodingLatin1; private System.Windows.Forms.Panel panelRouteEncoding; private System.Windows.Forms.Panel panelTrainEncoding; private System.Windows.Forms.Label labelInfoCenter; } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formMain.GetAddOns.cs000066400000000000000000001141211171674032100234460ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Drawing; using System.Text; using System.Threading; using System.Windows.Forms; namespace OpenBve { internal partial class formMain : Form { // --- members --- /* * There are three kinds of asynchronous operations used in this file. * These operations are mutually exclusive, meaning only one of these * operations may be executed at a time. In order to check whether any * of these operations are performed, check the CurrentDatabaseThread, * CurrentInstallThreads and CurrentRemoveThread members. If one of * these members is not a null reference, the respective operation is * in operation. You can also check the IsBusy function. * */ /// The thread that currently downloads the database, or a null reference. private Thread CurrentDatabaseThread = null; /// The current database, or a null reference. private ManagedContent.Database CurrentDatabase = null; /// The threads that currently download and install packages, or a null reference. private Thread[] CurrentInstallThreads = null; /// The packages that are left to be downloaded and installed, or a null reference. private ManagedContent.Version[] CurrentInstallPackages = null; /// The total size of the download operation. private int CurrentDownloadTotalSize = 0; /// The current size of the download operation. private int CurrentDownloadCurrentSize = 0; /// The thread that currently removes packages, or a null reference. private Thread CurrentRemoveThread = null; /// The current list of screenshots. private string[] Screenshots = null; /// The currently displayed screenshot (or thumbnail). private int ScreenshotIndex = 0; /// The number of days screenshots and thumbnails are cached. private const int NumberOfDaysScreenshotsAreCached = 14; // --- retrieve database --- /// Updates the Get add-ons screen when entered. private void EnterGetAddOns() { if (!IsBusy()) { labelDownloading.Text = Interface.GetInterfaceString("getaddons_connect"); progressbarDownloading.Style = ProgressBarStyle.Marquee; panelPackages.Enabled = false; CurrentDatabase = null; CurrentDatabaseThread = new Thread(ConnectToServer); CurrentDatabaseThread.IsBackground = true; CurrentDatabaseThread.Start(); } } /// Checks whether any of the asynchronous operations is currenly in operation. /// A boolean indicating whether any of the asynchronous operations is currenly in operation. private bool IsBusy() { return CurrentDatabaseThread != null | CurrentInstallThreads != null | CurrentRemoveThread != null; } /// Connects to the server and populates the database fields. Intended to be executed from a worker thread. private void ConnectToServer() { string[] urls = new string[] { "http://trainsimframework.org/common/packages.dat" }; string[] names = new string[] { "trainsimframework.org" }; string directory = System.IO.Path.Combine(Program.FileSystem.SettingsFolder, "Cache"); string file = System.IO.Path.Combine(directory, "packages.dat"); if (System.IO.File.Exists(file)) { try { if ((DateTime.Now - System.IO.File.GetLastWriteTime(file)).TotalMinutes < 10.0) { byte[] bytes = System.IO.File.ReadAllBytes(file); CurrentDatabase = ManagedContent.Database.Load(bytes); this.Invoke(new ThreadStart( () => { labelDownloading.Text = string.Empty; } )); } } catch { } } string error = null; if (CurrentDatabase == null) { for (int i = 0; i < urls.Length; i++) { this.Invoke(new ThreadStart( () => { labelDownloading.Text = Interface.GetInterfaceString("getaddons_connect") + "\n" + names[i]; } )); int size = 0; byte[] bytes; if (Internet.TryDownloadBytesFromUrl(urls[i], out bytes, ref size)) { try { CurrentDatabase = ManagedContent.Database.Load(bytes); this.Invoke(new ThreadStart( () => { labelDownloading.Text = string.Empty; } )); try { System.IO.Directory.CreateDirectory(directory); } catch { } try { System.IO.File.WriteAllBytes(file, bytes); } catch { } break; } catch (Exception ex) { error = ex.Message; } } } } CurrentDatabaseThread = null; this.Invoke(new ThreadStart( () => { progressbarDownloading.Style = ProgressBarStyle.Blocks; if (CurrentDatabase != null) { panelPackages.Enabled = true; ManagedContent.Version[] updates = GetAvailableUpdates(); if (updates != null && updates.Length != 0) { textboxFilter.Text = string.Empty; checkboxFilterRoutes.Checked = true; checkboxFilterTrains.Checked = true; checkboxFilterLibraries.Checked = true; checkboxFilterSharedLibraries.Checked = true; checkboxFilterNoWIPs.Checked = false; checkboxFilterUpdates.Checked = true; timerFilter.Enabled = false; ShowDatabase(true); TreeviewPackagesAfterSelect(null, null); labelDownloading.Text = Interface.GetInterfaceString("getaddons_updates"); if (MessageBox.Show(Interface.GetInterfaceString("getaddons_updates_install"), Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { ButtonPackageInstallClick(updates, null); } else { checkboxFilterLibraries.Checked = false; checkboxFilterSharedLibraries.Checked = false; checkboxFilterUpdates.Checked = false; timerFilter.Enabled = false; ShowDatabase(false); } } else { ShowDatabase(false); } } else if (error != null) { labelDownloading.Text = Interface.GetInterfaceString("getaddons_connect_error") + "\n" + error; } else { labelDownloading.Text = Interface.GetInterfaceString("getaddons_connect_failure"); } } )); } // --- show database --- /// Shows the list of available packages. private void ShowDatabase(bool expand) { if (CurrentDatabase != null & !IsBusy()) { ClearPackageDetails(); string[] keywords = textboxFilter.Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); /* * Collect packages we want to display. * */ List packages = new List(); if (CurrentDatabase != null) { string filter = textboxFilter.Text; bool routes = checkboxFilterRoutes.Checked; bool trains = checkboxFilterTrains.Checked; bool libraries = checkboxFilterLibraries.Checked; bool sharedLibraries = checkboxFilterSharedLibraries.Checked; bool noWIPs = checkboxFilterNoWIPs.Checked; bool onlyUpdates = checkboxFilterUpdates.Checked; foreach (ManagedContent.Package package in CurrentDatabase.Packages) { if (!onlyUpdates || !ManagedContent.IsInstalledPackageProtected(package.Name)) { ManagedContent.Version latestVersion = package.Versions[package.Versions.Length - 1]; if (!noWIPs || ManagedContent.CompareVersions(latestVersion.Number, "1.0") >= 0 && !latestVersion.GetMetadata("wip", null, "false").Equals("true", StringComparison.OrdinalIgnoreCase)) { string type = latestVersion.GetMetadata("type", null, null); if (type == "route" & routes | type == "train" & trains | type == "library" & libraries | type == "shared library" & sharedLibraries) { string currentVersion = ManagedContent.GetInstalledPackageVersion(package.Name); if (!onlyUpdates || currentVersion != null && ManagedContent.CompareVersions(latestVersion.Number, currentVersion) > 0) { bool add = true; if (keywords.Length != 0) { for (int i = 0; i < keywords.Length; i++) { if (package.Name.IndexOf(keywords[i], StringComparison.OrdinalIgnoreCase) < 0 && !latestVersion.ContainsKeyword(keywords[i])) { add = false; break; } } } if (add) { packages.Add(latestVersion); } } } } } } } /* * Group the list of packages by country and city, then display. * */ treeviewPackages.BeginUpdate(); treeviewPackages.Nodes.Clear(); string otherCountries = Interface.GetInterfaceString("getaddons_other_countries"); string otherCities = Interface.GetInterfaceString("getaddons_other_cities"); string otherOperators = Interface.GetInterfaceString("getaddons_other_operators"); foreach (ManagedContent.Version package in packages) { string type = package.GetMetadata("type", null, string.Empty); if (string.Equals(type, "shared library", StringComparison.OrdinalIgnoreCase)) { type = "library"; } string country = package.GetMetadata("country", CurrentLanguageCode, otherCountries); string flag = GetFlagFromEnUsCountry(package.GetMetadata("country", "en-US", null), "folder"); string city = package.GetMetadata("city", CurrentLanguageCode, otherCities); string operatorx = package.GetMetadata("operator", CurrentLanguageCode, otherOperators); string caption = package.GetMetadata("caption", CurrentLanguageCode, package.Name); string currentVersion = ManagedContent.GetInstalledPackageVersion(package.Name); TreeNode node; node = treeviewPackages.Nodes.Add(country); node.ImageKey = flag; node.SelectedImageKey = flag; node = node.Nodes.Add(city); node.ImageKey = "folder"; node.SelectedImageKey = "folder"; node = node.Nodes.Add(operatorx); node.ImageKey = "folder"; node.SelectedImageKey = "folder"; node = node.Nodes.Add(caption); string imageKey; if (ManagedContent.IsInstalledPackageProtected(package.Name)) { imageKey = type + "_protected"; } else if (currentVersion == null) { imageKey = type + "_notinstalled"; } else if (ManagedContent.CompareVersions(currentVersion, package.Number) < 0) { imageKey = type + "_outdatedversion"; } else { imageKey = type + "_latestversion"; } node.ImageKey = imageKey; node.SelectedImageKey = imageKey; node.Tag = package; } Group(treeviewPackages.Nodes); if (keywords.Length == 0) { foreach (TreeNode node in treeviewPackages.Nodes) { Flatten(node.Nodes); } } else { Flatten(treeviewPackages.Nodes); } treeviewPackages.Sort(); if (expand) { treeviewPackages.ExpandAll(); } treeviewPackages.EndUpdate(); buttonPackageInstall.Enabled = false; buttonPackageRemove.Enabled = false; } } /// Gets the flag code for the specified country. /// The country in en-US. /// The fallback in case the flag is not defined. /// The flag. private static string GetFlagFromEnUsCountry(string country, string fallback) { if (country != null) { switch (country.ToLowerInvariant()) { case "austria": return "AT"; case "belgium": return "BE"; case "brazil": return "BR"; case "switzerland": return "CH"; case "china": return "CN"; case "czech republic": return "CZ"; case "germany": return "DE"; case "spain": return "ES"; case "france": return "FR"; case "united kingdom": return "GB"; case "hong kong": return "HK"; case "hungary": return "HU"; case "italy": return "IT"; case "japan": return "JP"; case "south korea": return "KR"; case "netherlands": return "NL"; case "poland": return "PL"; case "portugal": return "PT"; case "romania": return "RO"; case "russia": return "RU"; case "singapore": return "SG"; case "taiwan": return "TW"; case "united states": return "US"; default: return fallback; } } else { return fallback; } } private void Group(TreeNodeCollection collection) { /* Group folders that have same text */ for (int i = 1; i < collection.Count; i++) { if (collection[i].Tag == null) { for (int j = 0; j < i; j++) { if (collection[j].Tag == null) { if (collection[i].Text == collection[j].Text) { TreeNodeCollection elements = collection[i].Nodes; collection.RemoveAt(i); foreach (TreeNode node in elements) { collection[j].Nodes.Add(node); } i--; break; } } } } } /* Recursion */ foreach (TreeNode node in collection) { Group(node.Nodes); } } private void Flatten(TreeNodeCollection collection) { /* Recursion */ foreach (TreeNode node in collection) { Flatten(node.Nodes); } /* Flatten out folders that contain only one element */ for (int i = 0; i < collection.Count; i++) { if (collection[i].Nodes.Count == 1) { TreeNode element = collection[i].Nodes[0]; collection.RemoveAt(i); collection.Add(element); i--; } } /* Remove empty folders from the collection */ for (int i = 0; i < collection.Count; i++) { if (collection[i].Tag == null && collection[i].Nodes.Count == 0) { collection.RemoveAt(i); i--; } } /* Flatten out the only element if it is a folder */ if (collection.Count == 1 && collection[0].Tag == null) { TreeNodeCollection elements = collection[0].Nodes; collection.RemoveAt(0); foreach (TreeNode node in elements) { collection.Add(node); } } } // --- functions --- /// Gets a textual description of a file size. /// The size in bytes. /// The textual description of the size. private string GetStringFromSize(int size) { if (size < 1024) { return size.ToString() + " B"; } else if (size < 1048576) { return ((double)size / 1024.0).ToString("0.0") + " KiB"; } else if (size < 1073741824) { return ((double)size / 1048576.0).ToString("0.0") + " MiB"; } else { return ((double)size / 1073741824.0).ToString("0.0") + " GiB"; } } /// Checks whether updates are available for any of the installed add-ons. /// Whether updates are available for any of the installed add-ons. private bool AreUpdatesAvailable() { if (CurrentDatabase != null) { foreach (ManagedContent.Package package in CurrentDatabase.Packages) { string currentVersion = ManagedContent.GetInstalledPackageVersion(package.Name); if (currentVersion != null) { if (!ManagedContent.IsInstalledPackageProtected(package.Name)) { if (ManagedContent.CompareVersions(package.Versions[package.Versions.Length - 1].Number, currentVersion) > 0) { return true; } } } } return false; } else { return false; } } /// Gets a list of available updates. /// The list of the latest packages. private ManagedContent.Version[] GetAvailableUpdates() { if (CurrentDatabase != null) { List updates = new List(); foreach (ManagedContent.Package package in CurrentDatabase.Packages) { string currentVersion = ManagedContent.GetInstalledPackageVersion(package.Name); if (currentVersion != null) { if (!ManagedContent.IsInstalledPackageProtected(package.Name)) { if (ManagedContent.CompareVersions(package.Versions[package.Versions.Length - 1].Number, currentVersion) > 0) { updates.Add(package.Versions[package.Versions.Length - 1]); } } } } return updates.ToArray(); } else { return null; } } // --- events --- private void LinkPackageHomepageLinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { if (e.Link.LinkData is string) { try { System.Diagnostics.Process.Start((string)e.Link.LinkData); } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } } } private void ClearPackageDetails() { buttonPackageInstall.Enabled = false; buttonPackageRemove.Enabled = false; labelPackageInformation.Text = string.Empty; textboxPackageDescription.Text = string.Empty; linkPackageHomepage.Links.Clear(); linkPackageHomepage.Text = string.Empty; groupboxPackage.Enabled = false; pictureboxScreenshot.Image = null; pictureboxScreenshot.Cursor = Cursors.Default; buttonScreenshotPrevious.Enabled = false; buttonScreenshotNext.Enabled = false; Screenshots = null; ScreenshotIndex = 0; } private void TreeviewPackagesAfterSelect(object sender, TreeViewEventArgs e) { if (treeviewPackages.SelectedNode == null || treeviewPackages.SelectedNode.Tag == null) { ClearPackageDetails(); } else { ManagedContent.Version version = (ManagedContent.Version)treeviewPackages.SelectedNode.Tag; string currentVersion = ManagedContent.GetInstalledPackageVersion(version.Name); string caption = version.GetMetadata("caption", CurrentLanguageCode, null); string country = version.GetMetadata("country", CurrentLanguageCode, null); string city = version.GetMetadata("city", CurrentLanguageCode, null); string operatorx = version.GetMetadata("operator", CurrentLanguageCode, null); bool fictional = string.Equals(version.GetMetadata("fictional", null, string.Empty), "true", StringComparison.OrdinalIgnoreCase); string author = version.GetMetadata("author", CurrentLanguageCode, null); StringBuilder builder = new StringBuilder(); if (caption != null) { builder.AppendLine(caption); } if (country != null) { builder.Append(Interface.GetInterfaceString("getaddons_package_country")); builder.AppendLine(country); } if (city != null) { builder.Append(Interface.GetInterfaceString("getaddons_package_city")); builder.AppendLine(city); } if (operatorx != null) { builder.Append(Interface.GetInterfaceString("getaddons_package_operator")); builder.AppendLine(operatorx); } if (fictional) { builder.AppendLine(Interface.GetInterfaceString("getaddons_package_fictional")); } builder.AppendLine(); builder.AppendLine(version.Name); if (author != null) { builder.Append(Interface.GetInterfaceString("getaddons_package_author")); builder.AppendLine(author); } builder.Append(Interface.GetInterfaceString("getaddons_package_version_latest")); builder.AppendLine(version.Number); if (currentVersion != null) { builder.Append(Interface.GetInterfaceString("getaddons_package_version_installed")); builder.AppendLine(currentVersion); } labelPackageInformation.Text = builder.ToString().Trim(); textboxPackageDescription.Text = version.GetMetadata("description", CurrentLanguageCode, string.Empty).Replace(@"\n", "\x0D\x0A"); string[] links = version.GetMetadata("links", CurrentLanguageCode, string.Empty).Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (links.Length != 0) { builder = new StringBuilder(); string[] labels = version.GetMetadata("labels", CurrentLanguageCode, string.Empty).Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); linkPackageHomepage.Links.Clear(); for (int i = 0; i < links.Length; i++) { if (i != 0) { builder.Append(' '); } string label; if (i < labels.Length) { label = labels[i].Trim(); } else { try { label = System.IO.Path.GetFileNameWithoutExtension(links[i]); } catch { label = "Link" + (i + 1).ToString(); } } linkPackageHomepage.Links.Add(builder.Length, label.Length, links[i].Trim()); builder.Append(label); } linkPackageHomepage.Text = builder.ToString(); } else { linkPackageHomepage.Links.Clear(); linkPackageHomepage.Text = string.Empty; } groupboxPackage.Enabled = true; if (ManagedContent.IsInstalledPackageProtected(version.Name)) { buttonPackageInstall.Enabled = false; buttonPackageRemove.Enabled = false; } else { if (currentVersion == null) { buttonPackageInstall.Enabled = true; buttonPackageRemove.Enabled = false; } else if (ManagedContent.CompareVersions(currentVersion, version.Number) < 0) { buttonPackageInstall.Enabled = true; buttonPackageRemove.Enabled = true; } else { buttonPackageInstall.Enabled = false; buttonPackageRemove.Enabled = true; } } /* Prepare screenshots and thumbnails */ string[] screenshots = version.GetMetadata("screenshots", null, string.Empty).Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] thumbs = version.GetMetadata("thumbnails", null, string.Empty).Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (thumbs.Length < screenshots.Length) { int count = thumbs.Length; Array.Resize(ref thumbs, screenshots.Length); for (int i = count; i < screenshots.Length; i++) { thumbs[i] = screenshots[i]; } } else if (thumbs.Length > screenshots.Length) { int count = screenshots.Length; Array.Resize(ref screenshots, thumbs.Length); for (int i = count; i < thumbs.Length; i++) { screenshots[i] = thumbs[i]; } } for (int i = 0; i < screenshots.Length; i++) { screenshots[i] = screenshots[i].Trim(); thumbs[i] = thumbs[i].Trim(); } pictureboxScreenshot.Image = null; pictureboxScreenshot.Cursor = screenshots.Length >= 1 ? Cursors.Hand : Cursors.Default; buttonScreenshotPrevious.Enabled = false; buttonScreenshotNext.Enabled = screenshots.Length >= 2; Screenshots = screenshots; ScreenshotIndex = 0; string tempDirectory = System.IO.Path.Combine(Program.FileSystem.SettingsFolder, "Cache"); for (int i = 0; i < thumbs.Length; i++) { ParameterizedThreadStart callback; if (i == 0) { callback = ShowThumbnailAsynchronous; } else { callback = null; } Internet.DownloadAndSaveAsynchronous(thumbs[i], System.IO.Path.Combine(tempDirectory, version.Name + "_" + version.Number + "_" + i.ToString() + "_thumb"), NumberOfDaysScreenshotsAreCached, callback); } } } private void PictureboxScreenshotClick(object sender, EventArgs e) { if (Screenshots != null && ScreenshotIndex < Screenshots.Length && treeviewPackages.SelectedNode != null && treeviewPackages.SelectedNode.Tag != null) { ManagedContent.Version version = (ManagedContent.Version)treeviewPackages.SelectedNode.Tag; string tempDirectory = System.IO.Path.Combine(Program.FileSystem.SettingsFolder, "Cache"); for (int i = 0; i < Screenshots.Length; i++) { ParameterizedThreadStart callback; if (i == ScreenshotIndex) { callback = (object data) => { string file = (string)data; try { formImage.ShowImageDialog(Image.FromFile(file)); } catch { System.Media.SystemSounds.Exclamation.Play(); } }; } else { callback = null; } Internet.DownloadAndSaveAsynchronous(Screenshots[i], System.IO.Path.Combine(tempDirectory, version.Name + "_" + version.Number + "_" + i.ToString()), NumberOfDaysScreenshotsAreCached, callback); } } } private void ButtonScreenshotPreviousClick(object sender, EventArgs e) { if (ScreenshotIndex > 0) { ScreenshotIndex--; ShowThumbnail(); } } private void ButtonScreenshotNextClick(object sender, EventArgs e) { if (Screenshots != null && ScreenshotIndex < Screenshots.Length - 1) { ScreenshotIndex++; ShowThumbnail(); } } /// Shows the current thumbnail. Can be called from any thread. private void ShowThumbnailAsynchronous(object data) { this.Invoke(new ThreadStart(ShowThumbnail)); } /// Shows the current thumbnail. Must only be called from the main thread. private void ShowThumbnail() { if (Screenshots == null || ScreenshotIndex >= Screenshots.Length || treeviewPackages.SelectedNode == null || treeviewPackages.SelectedNode.Tag == null) { pictureboxScreenshot.Image = null; buttonScreenshotPrevious.Enabled = false; buttonScreenshotNext.Enabled = false; } else { ManagedContent.Version version = (ManagedContent.Version)treeviewPackages.SelectedNode.Tag; string directory = System.IO.Path.Combine(Program.FileSystem.SettingsFolder, "Cache"); string file = System.IO.Path.Combine(directory, version.Name + "_" + version.Number + "_" + ScreenshotIndex.ToString() + "_thumb"); if (System.IO.File.Exists(file)) { try { pictureboxScreenshot.Image = Image.FromFile(file); } catch { pictureboxScreenshot.Image = null; } } buttonScreenshotPrevious.Enabled = ScreenshotIndex > 0; buttonScreenshotNext.Enabled = ScreenshotIndex < Screenshots.Length - 1; } } // --- install --- /// Raised when the Install button is clicked. /// The sender. /// The event args. private void ButtonPackageInstallClick(object sender, EventArgs e) { if (CurrentDatabase != null & !IsBusy()) { if ((treeviewPackages.SelectedNode != null && treeviewPackages.SelectedNode.Tag != null) || sender is ManagedContent.Version[]) { /* * Build a list of all selected packages. * */ List packages; if (sender is ManagedContent.Version[]) { packages = new List((ManagedContent.Version[])sender); } else { packages = new List(); packages.Add((ManagedContent.Version)treeviewPackages.SelectedNode.Tag); } /* * Go through the list of packages and add * their suggestions if the user confirms. * */ List suggestions = CurrentDatabase.GetSuggestions(packages); if (suggestions.Count != 0) { System.Text.StringBuilder builder = new System.Text.StringBuilder(); foreach (ManagedContent.Dependency suggestion in suggestions) { builder.Append(suggestion.Name).Append(" (").Append(suggestion.Version).AppendLine(")"); ManagedContent.Version version = CurrentDatabase.Dereference(suggestion.Name, suggestion.Version); if (version != null) { string caption = version.GetMetadata("caption", CurrentLanguageCode, null); if (caption != null) { builder.AppendLine(caption); } } builder.AppendLine(); } switch (MessageBox.Show(Interface.GetInterfaceString("getaddons_suggest_1") + "\n\n" + builder.ToString() + Interface.GetInterfaceString("getaddons_suggest_2"), Interface.GetInterfaceString("getaddons_package_install"), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question)) { case DialogResult.Yes: foreach (ManagedContent.Dependency suggestion in suggestions) { ManagedContent.Version version = CurrentDatabase.Dereference(suggestion.Name, suggestion.Version); if (version != null) { packages.Add(version); } } break; case DialogResult.Cancel: return; } } /* * Go through the list of packages and add * their dependencies. * */ if (!CurrentDatabase.AddDependencies(packages)) { if (MessageBox.Show("Some dependencies were not found. This indicates a bug the server that compiled the list of add-ons.", Interface.GetInterfaceString("getaddons_package_install"), MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.Cancel) { return; } } if (packages.Count == 0) { return; } /* * Calculate total size of download. * */ CurrentDownloadCurrentSize = 0; CurrentDownloadTotalSize = 0; foreach (ManagedContent.Version package in packages) { if (package.Sources.Length != 0) { CurrentDownloadTotalSize += package.Sources[0].Size; } } /* * Ask for final confirmation. * */ if (MessageBox.Show(Interface.GetInterfaceString("getaddons_confirmation").Replace("[size]", GetStringFromSize(CurrentDownloadTotalSize)), Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { return; } /* * Prepare job for asynchronous download. * */ List unsuccessful = new List(); ThreadStart job = new ThreadStart( () => { while (true) { ManagedContent.Version version = null; lock (this) { if (CurrentInstallPackages.Length != 0) { version = CurrentInstallPackages[CurrentInstallPackages.Length - 1]; Array.Resize(ref CurrentInstallPackages, CurrentInstallPackages.Length - 1); } else { break; } } if (version != null) { if (!CurrentDatabase.InstallPackage(version, ref CurrentDownloadCurrentSize)) { lock (this) { unsuccessful.Add(version.Name + " (" + version.Number + ")"); } } } } bool finalize; lock (this) { for (int i = 0; i < CurrentInstallThreads.Length; i++) { if (CurrentInstallThreads[i] == Thread.CurrentThread) { CurrentInstallThreads[i] = CurrentInstallThreads[CurrentInstallThreads.Length - 1]; Array.Resize(ref CurrentInstallThreads, CurrentInstallThreads.Length - 1); } } finalize = CurrentInstallThreads.Length == 0; } if (finalize) { this.Invoke(new ThreadStart( () => { timerInstall.Enabled = false; progressbarDownloading.Value = 0; progressbarDownloading.Style = ProgressBarStyle.Blocks; if (unsuccessful.Count == 0) { labelDownloading.Text = string.Empty; } else { labelDownloading.Text = Interface.GetInterfaceString("getaddons_package_install_failure") + "\n" + string.Join(", ", unsuccessful.ToArray()); } panelPackages.Enabled = true; CurrentInstallThreads = null; bool noMoreUpdates = checkboxFilterUpdates.Checked && !AreUpdatesAvailable(); if (noMoreUpdates) { checkboxFilterRoutes.Checked = true; checkboxFilterTrains.Checked = true; checkboxFilterLibraries.Checked = false; checkboxFilterSharedLibraries.Checked = false; checkboxFilterUpdates.Checked = false; timerFilter.Enabled = false; } ShowDatabase(false); if (noMoreUpdates && unsuccessful.Count == 0) { labelDownloading.Text = Interface.GetInterfaceString("getaddons_updates_nomore"); } TextboxRouteFilterTextChanged(null, null); TextboxTrainFilterTextChanged(null, null); Program.SetPackageLookupDirectories(); } )); } } ); /* * Start asynchronous download. * */ const int numberOfParallelDownloads = 2; labelDownloading.Text = Interface.GetInterfaceString("getaddons_package_install_progress"); progressbarDownloading.Style = ProgressBarStyle.Continuous; panelPackages.Enabled = false; timerInstall.Enabled = true; CurrentInstallPackages = packages.ToArray(); CurrentInstallThreads = new Thread[numberOfParallelDownloads]; lock (this) { for (int i = 0; i < CurrentInstallThreads.Length; i++) { CurrentInstallThreads[i] = new Thread(job); CurrentInstallThreads[i].IsBackground = true; CurrentInstallThreads[i].Start(); } } } } else { System.Media.SystemSounds.Asterisk.Play(); } } // --- remove --- /// Raised when the Remove button is clicked. /// The sender. /// The event args. private void ButtonPackageRemoveClick(object sender, EventArgs e) { if (CurrentDatabase != null & !IsBusy()) { if (treeviewPackages.SelectedNode != null && treeviewPackages.SelectedNode.Tag != null) { /* * Build a list of all selected packages * that are not protected. * */ List packages = new List(); packages.Add(((ManagedContent.Version)treeviewPackages.SelectedNode.Tag).Name); /* * Add all dependent and redundant items. * */ List results = ManagedContent.GetDependentAndRedundantPackages(packages); if (results.Count != 0) { System.Text.StringBuilder builder = new System.Text.StringBuilder(); foreach (string result in results) { string number = ManagedContent.GetInstalledPackageVersion(result); if (number != null) { builder.Append(result).Append(" (").Append(number).AppendLine(")"); } else { builder.AppendLine(result); } builder.AppendLine(); } switch (MessageBox.Show(Interface.GetInterfaceString("getaddons_redundant_1") + "\n\n" + builder.ToString() + Interface.GetInterfaceString("getaddons_redundant_2"), Interface.GetInterfaceString("getaddons_remove"), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question)) { case DialogResult.Yes: packages.AddRange(results); break; case DialogResult.Cancel: return; } } /* * Remove the packages. * */ labelDownloading.Text = Interface.GetInterfaceString("getaddons_package_remove_progress"); progressbarDownloading.Style = ProgressBarStyle.Marquee; panelPackages.Enabled = false; CurrentRemoveThread = new Thread( () => { List unsuccessful = new List(); foreach (string package in packages) { if (!ManagedContent.RemovePackage(package)) { unsuccessful.Add(package); } } this.Invoke(new ThreadStart( () => { if (unsuccessful.Count == 0) { labelDownloading.Text = string.Empty; } else { labelDownloading.Text = Interface.GetInterfaceString("getaddons_package_remove_failure") + "\n" + string.Join(", ", unsuccessful.ToArray()); } progressbarDownloading.Style = ProgressBarStyle.Blocks; panelPackages.Enabled = true; CurrentRemoveThread = null; ShowDatabase(false); Program.SetPackageLookupDirectories(); TextboxRouteFilterTextChanged(null, null); TextboxTrainFilterTextChanged(null, null); } )); } ); CurrentRemoveThread.IsBackground = true; CurrentRemoveThread.Start(); } } else { System.Media.SystemSounds.Asterisk.Play(); } } // --- cache --- private void ClearCache(string directory, double days) { if (System.IO.Directory.Exists(directory)) { string[] files = System.IO.Directory.GetFiles(directory); foreach (string file in files) { try { DateTime lastWrite = System.IO.File.GetLastWriteTime(file); TimeSpan span = DateTime.Now - lastWrite; if (span.TotalDays > days) { System.IO.File.Delete(file); } } catch { } } } } // --- events --- /// Raised every once in a while to update the download progress. /// The sender. /// The event args. private void TimerInstallTick(object sender, EventArgs e) { if (CurrentDownloadCurrentSize < CurrentDownloadTotalSize) { double fraction = CurrentDownloadTotalSize != 0.0 ? (double)CurrentDownloadCurrentSize / (double)CurrentDownloadTotalSize : 0.0; if (fraction < 0.0) fraction = 0.0; if (fraction > 1.0) fraction = 1.0; progressbarDownloading.Value = progressbarDownloading.Minimum + (int)Math.Floor(fraction * (progressbarDownloading.Maximum - progressbarDownloading.Minimum)); labelDownloading.Text = Interface.GetInterfaceString("getaddons_package_install_download") + "\n" + GetStringFromSize(CurrentDownloadCurrentSize) + " / " + GetStringFromSize(CurrentDownloadTotalSize); } else { progressbarDownloading.Value = progressbarDownloading.Maximum; labelDownloading.Text = Interface.GetInterfaceString("getaddons_package_install_progress"); } } private void TextboxFilterTextChanged(object sender, EventArgs e) { timerFilter.Enabled = false; timerFilter.Enabled = true; } private void CheckboxFilterRoutesCheckedChanged(object sender, EventArgs e) { timerFilter.Enabled = false; timerFilter.Enabled = true; } private void CheckboxFilterTrainsCheckedChanged(object sender, EventArgs e) { timerFilter.Enabled = false; timerFilter.Enabled = true; } private void CheckboxFilterLibrariesCheckedChanged(object sender, EventArgs e) { timerFilter.Enabled = false; timerFilter.Enabled = true; } private void CheckboxFilterSharedLibrariesCheckedChanged(object sender, EventArgs e) { timerFilter.Enabled = false; timerFilter.Enabled = true; } private void CheckboxFilterNoWIPsCheckedChanged(object sender, EventArgs e) { timerFilter.Enabled = false; timerFilter.Enabled = true; } private void CheckboxFilterUpdatesCheckedChanged(object sender, EventArgs e) { timerFilter.Enabled = false; timerFilter.Enabled = true; } private void TimerFilterTick(object sender, EventArgs e) { timerFilter.Enabled = false; ShowDatabase(false); } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formMain.Options.cs000066400000000000000000000041201171674032100232660ustar00rootroot00000000000000using System; using System.Drawing; using System.Windows.Forms; namespace OpenBve { internal partial class formMain : Form { // ======= // options // ======= // language private void comboboxLanguages_SelectedIndexChanged(object sender, EventArgs e) { if (this.Tag != null) return; int i = comboboxLanguages.SelectedIndex; if (i >= 0 & i < LanguageFiles.Length) { string Code = System.IO.Path.GetFileNameWithoutExtension(LanguageFiles[i]); string Folder = Program.FileSystem.GetDataFolder("Flags"); #if !DEBUG try { #endif Interface.LoadLanguage(LanguageFiles[i]); #if !DEBUG } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } #endif #if !DEBUG try { #endif string Flag = Interface.GetInterfaceString("language_flag"); string File = OpenBveApi.Path.CombineFile(Folder, Flag); if (!System.IO.File.Exists(File)) { File = OpenBveApi.Path.CombineFile(Folder, "unknown.png"); } if (System.IO.File.Exists(File)) { pictureboxLanguage.Image = Image.FromFile(File); } else { pictureboxLanguage.Image = null; } CurrentLanguageCode = Code; #if !DEBUG } catch { } #endif ApplyLanguage(); TextboxRouteFilterTextChanged(null, null); TextboxTrainFilterTextChanged(null, null); } } // interpolation private void comboboxInterpolation_SelectedIndexChanged(object sender, EventArgs e) { int i = comboboxInterpolation.SelectedIndex; bool q = i == (int)Interface.InterpolationMode.AnisotropicFiltering; labelAnisotropic.Enabled = q; updownAnisotropic.Enabled = q; q = i != (int)Interface.InterpolationMode.NearestNeighbor & i != (int)Interface.InterpolationMode.Bilinear; } // ======= // options // ======= // joysticks enabled private void checkboxJoysticksUsed_CheckedChanged(object sender, EventArgs e) { groupboxJoysticks.Enabled = checkboxJoysticksUsed.Checked; } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formMain.Review.cs000066400000000000000000000055561171674032100231120ustar00rootroot00000000000000using System; using System.Drawing; using System.Windows.Forms; namespace OpenBve { internal partial class formMain : Form { // ================ // review last game // ================ // score save private void buttonScoreExport_Click(object sender, EventArgs e) { SaveFileDialog Dialog = new SaveFileDialog(); Dialog.OverwritePrompt = true; Dialog.Filter = Interface.GetInterfaceString("dialog_textfiles") + "|*.txt|" + Interface.GetInterfaceString("dialog_allfiles") + "|*"; if (Dialog.ShowDialog() == DialogResult.OK) { try { Interface.ExportScore(Dialog.FileName); } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } } } // score penalties private void checkboxScorePenalties_CheckedChanged(object sender, EventArgs e) { ShowScoreLog(checkboxScorePenalties.Checked); } // black box export private void buttonBlackBoxExport_Click(object sender, EventArgs e) { SaveFileDialog Dialog = new SaveFileDialog(); Dialog.OverwritePrompt = true; if (comboboxBlackBoxFormat.SelectedIndex == 0) { Dialog.Filter = Interface.GetInterfaceString("dialog_csvfiles") + "|*.txt|" + Interface.GetInterfaceString("dialog_allfiles") + "|*"; } else { Dialog.Filter = Interface.GetInterfaceString("dialog_textfiles") + "|*.txt|" + Interface.GetInterfaceString("dialog_allfiles") + "|*"; } if (Dialog.ShowDialog() == DialogResult.OK) { try { Interface.ExportBlackBox(Dialog.FileName, (Interface.BlackBoxFormat)comboboxBlackBoxFormat.SelectedIndex); } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } } } // show score log private void ShowScoreLog(bool PenaltiesOnly) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; listviewScore.Items.Clear(); int sum = 0; for (int i = 0; i < Game.ScoreLogCount; i++) { sum += Game.ScoreLogs[i].Value; if (!PenaltiesOnly | Game.ScoreLogs[i].Value < 0) { double x = Game.ScoreLogs[i].Time; int h = (int)Math.Floor(x / 3600.0); x -= 3600.0 * (double)h; int m = (int)Math.Floor(x / 60.0); x -= 60.0 * (double)m; int s = (int)Math.Floor(x); ListViewItem Item = listviewScore.Items.Add(h.ToString("00", Culture) + ":" + m.ToString("00", Culture) + ":" + s.ToString("00", Culture)); Item.SubItems.Add(Game.ScoreLogs[i].Position.ToString("0", Culture)); Item.SubItems.Add(Game.ScoreLogs[i].Value.ToString(Culture)); Item.SubItems.Add(sum.ToString(Culture)); Item.SubItems.Add(Interface.GetScoreText(Game.ScoreLogs[i].TextToken)); } } listviewScore.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize); } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formMain.Start.cs000066400000000000000000001115401171674032100227350ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Text; using System.Windows.Forms; namespace OpenBve { internal partial class formMain : Form { // --- route add-ons --- private void TextboxRouteFilterTextChanged(object sender, EventArgs e) { treeviewRouteAddOns.BeginUpdate(); treeviewRouteAddOns.Nodes.Clear(); string[] keywords = textboxRouteFilter.Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string lookupDirectory in Program.FileSystem.ManagedContentFolders) { string[] packageDirectories = Directory.GetDirectories(lookupDirectory); foreach (string packageDirectory in packageDirectories) { string file = OpenBveApi.Path.CombineFile(packageDirectory, "package.cfg"); if (File.Exists(file)) { string[] lines = File.ReadAllLines(file, Encoding.UTF8); List pairsList = new List(); foreach (string line in lines) { int equals = line.IndexOf('='); if (equals >= 0) { string key = line.Substring(0, equals).Trim().ToLowerInvariant(); string value = line.Substring(equals + 1).Trim(); pairsList.Add(new ManagedContent.KeyValuePair(key, value)); } } ManagedContent.KeyValuePair[] pairs = pairsList.ToArray(); string type = ManagedContent.GetMetadata(pairs, "type", null, null); if (type != null && type.Equals("route", StringComparison.OrdinalIgnoreCase)) { string country = ManagedContent.GetMetadata(pairs, "country", CurrentLanguageCode, Interface.GetInterfaceString("getaddons_other_countries")); string flag = GetFlagFromEnUsCountry(ManagedContent.GetMetadata(pairs, "country", "en-US", null), "folder"); string city = ManagedContent.GetMetadata(pairs, "city", CurrentLanguageCode, Interface.GetInterfaceString("getaddons_other_cities")); string operatorx = ManagedContent.GetMetadata(pairs, "operator", CurrentLanguageCode, Interface.GetInterfaceString("getaddons_other_operators")); string caption = ManagedContent.GetMetadata(pairs, "caption", CurrentLanguageCode, System.IO.Path.GetFileName(packageDirectory)); string[] metadata = new string[] { country, city, operatorx, caption }; string entry = ManagedContent.GetMetadata(pairs, "entry", null, string.Empty); string path = OpenBveApi.Path.CombineDirectory(packageDirectory, entry); TreeNode node = treeviewRouteAddOns.Nodes.Add(country); node.ImageKey = flag; node.SelectedImageKey = flag; node = node.Nodes.Add(city); node.ImageKey = "folder"; node.SelectedImageKey = "folder"; node = node.Nodes.Add(operatorx); node.ImageKey = "folder"; node.SelectedImageKey = "folder"; node = node.Nodes.Add(caption); node.ImageKey = "folder"; node.SelectedImageKey = "folder"; AddRoutes(node.Nodes, path, keywords, metadata); } } } } Group(treeviewRouteAddOns.Nodes); if (keywords.Length == 0) { foreach (TreeNode node in treeviewRouteAddOns.Nodes) { Flatten(node.Nodes); } } else { Flatten(treeviewRouteAddOns.Nodes); } treeviewRouteAddOns.Sort(); treeviewRouteAddOns.EndUpdate(); } private void AddRoutes(TreeNodeCollection collection, string path, string[] keywords, string[] metadata) { if (Directory.Exists(path)) { string[] directories = Directory.GetDirectories(path); foreach (string directory in directories) { TreeNode node = collection.Add(Path.GetFileName(directory)); node.ImageKey = "folder"; node.SelectedImageKey = "folder"; string title = Path.GetFileNameWithoutExtension(directory); bool found = false; foreach (string keyword in keywords) { if (title.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) { found = true; break; } } AddRoutes(node.Nodes, directory, found ? new string[] { } : keywords, metadata); } string[] files = Directory.GetFiles(path); foreach (string file in files) { if (file.EndsWith(".csv", StringComparison.OrdinalIgnoreCase) | file.EndsWith(".rw", StringComparison.OrdinalIgnoreCase)) { string title = Path.GetFileNameWithoutExtension(file); bool add = true; foreach (string keyword in keywords) { add = false; foreach (string tag in metadata) { if (tag.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) { add = true; break; } } if (!add) { if (title.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) { add = true; } else { break; } } } if (add) { TreeNode node = collection.Add(title); node.Tag = file; node.ImageKey = "route"; node.SelectedImageKey = "route"; } } } } } private void TreeviewRouteAddOnsAfterSelect(object sender, TreeViewEventArgs e) { if (treeviewRouteAddOns.SelectedNode != null && treeviewRouteAddOns.SelectedNode.Tag != null) { Result.RouteFile = (string)treeviewRouteAddOns.SelectedNode.Tag; ShowRoute(false); } } // --- train add-ons --- private void TextboxTrainFilterTextChanged(object sender, EventArgs e) { treeviewTrainAddOns.BeginUpdate(); treeviewTrainAddOns.Nodes.Clear(); string[] keywords = textboxTrainFilter.Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string lookupDirectory in Program.FileSystem.ManagedContentFolders) { string[] packageDirectories = Directory.GetDirectories(lookupDirectory); foreach (string packageDirectory in packageDirectories) { string file = OpenBveApi.Path.CombineFile(packageDirectory, "package.cfg"); if (File.Exists(file)) { string[] lines = File.ReadAllLines(file, Encoding.UTF8); List pairsList = new List(); foreach (string line in lines) { int equals = line.IndexOf('='); if (equals >= 0) { string key = line.Substring(0, equals).Trim().ToLowerInvariant(); string value = line.Substring(equals + 1).Trim(); pairsList.Add(new ManagedContent.KeyValuePair(key, value)); } } ManagedContent.KeyValuePair[] pairs = pairsList.ToArray(); string type = ManagedContent.GetMetadata(pairs, "type", null, null); if (type != null && type.Equals("train", StringComparison.OrdinalIgnoreCase)) { string country = ManagedContent.GetMetadata(pairs, "country", CurrentLanguageCode, Interface.GetInterfaceString("getaddons_other_countries")); string flag = GetFlagFromEnUsCountry(ManagedContent.GetMetadata(pairs, "country", "en-US", null), "folder"); string city = ManagedContent.GetMetadata(pairs, "city", CurrentLanguageCode, Interface.GetInterfaceString("getaddons_other_cities")); string operatorx = ManagedContent.GetMetadata(pairs, "operator", CurrentLanguageCode, Interface.GetInterfaceString("getaddons_other_operators")); string caption = ManagedContent.GetMetadata(pairs, "caption", CurrentLanguageCode, System.IO.Path.GetFileName(packageDirectory)); string[] metadata = new string[] { country, city, operatorx, caption }; string entry = ManagedContent.GetMetadata(pairs, "entry", null, string.Empty); TreeNode node; node = treeviewTrainAddOns.Nodes.Add(country); node.ImageKey = flag; node.SelectedImageKey = flag; node = node.Nodes.Add(city); node.ImageKey = "folder"; node.SelectedImageKey = "folder"; node = node.Nodes.Add(operatorx); node.ImageKey = "folder"; node.SelectedImageKey = "folder"; node = node.Nodes.Add(caption); node.ImageKey = "folder"; node.SelectedImageKey = "folder"; string path = OpenBveApi.Path.CombineDirectory(packageDirectory, entry); AddTrains(node.Nodes, path, keywords, metadata); } } } } Group(treeviewTrainAddOns.Nodes); if (keywords.Length == 0) { foreach (TreeNode node in treeviewTrainAddOns.Nodes) { Flatten(node.Nodes); } } else { Flatten(treeviewTrainAddOns.Nodes); } treeviewTrainAddOns.Sort(); treeviewTrainAddOns.EndUpdate(); } private void AddTrains(TreeNodeCollection collection, string path, string[] keywords, string[] metadata) { if (Directory.Exists(path)) { string[] directories = Directory.GetDirectories(path); foreach (string directory in directories) { if (File.Exists(OpenBveApi.Path.CombineFile(directory, "train.dat"))) { string title = Path.GetFileName(directory); bool add = true; foreach (string keyword in keywords) { add = false; foreach (string tag in metadata) { if (tag.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) { add = true; break; } } if (!add) { if (title.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) { add = true; } else { break; } } } if (add) { TreeNode node = collection.Add(title); node.Tag = directory; node.ImageKey = "train"; node.SelectedImageKey = "train"; } } else { TreeNode node = collection.Add(Path.GetFileName(directory)); node.ImageKey = "folder"; node.SelectedImageKey = "folder"; string title = Path.GetFileNameWithoutExtension(directory); bool found = false; foreach (string keyword in keywords) { if (title.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) { found = true; break; } } AddTrains(node.Nodes, directory, found ? new string[] { } : keywords, metadata); } } } } private void TreeviewTrainAddOnsAfterSelect(object sender, TreeViewEventArgs e) { if (treeviewTrainAddOns.SelectedNode != null && treeviewTrainAddOns.SelectedNode.Tag != null) { Result.TrainFolder = (string)treeviewTrainAddOns.SelectedNode.Tag; ShowTrain(false); if (checkboxTrainDefault.Checked) checkboxTrainDefault.Checked = false; } } // =============== // route selection // =============== // route folder private void textboxRouteFolder_TextChanged(object sender, EventArgs e) { string Folder = textboxRouteFolder.Text; try { if (Folder.Length == 0) { // drives listviewRouteFiles.Items.Clear(); try { // MoMA says that GetDrives is flagged with [MonoTodo] System.IO.DriveInfo[] driveInfos = System.IO.DriveInfo.GetDrives(); for (int i = 0; i < driveInfos.Length; i++) { ListViewItem Item = listviewRouteFiles.Items.Add(driveInfos[i].Name); Item.ImageKey = "folder"; Item.Tag = driveInfos[i].RootDirectory.FullName; listviewRouteFiles.Tag = null; } } catch { } } else if (System.IO.Directory.Exists(Folder)) { listviewRouteFiles.Items.Clear(); // parent try { System.IO.DirectoryInfo Info = System.IO.Directory.GetParent(Folder); if (Info != null) { ListViewItem Item = listviewRouteFiles.Items.Add(".."); Item.ImageKey = "parent"; Item.Tag = Info.FullName; listviewRouteFiles.Tag = Info.FullName; } else { ListViewItem Item = listviewRouteFiles.Items.Add(".."); Item.ImageKey = "parent"; Item.Tag = ""; listviewRouteFiles.Tag = ""; } } catch { } // folders try { string[] Folders = System.IO.Directory.GetDirectories(Folder); Array.Sort(Folders); for (int i = 0; i < Folders.Length; i++) { System.IO.DirectoryInfo info = new System.IO.DirectoryInfo(Folders[i]); if ((info.Attributes & System.IO.FileAttributes.Hidden) == 0) { string Name = System.IO.Path.GetFileName(Folders[i]); if (Name.Length != 0 && Name[0] != '.') { ListViewItem Item = listviewRouteFiles.Items.Add(Name); Item.ImageKey = "folder"; Item.Tag = Folders[i]; } } } } catch { } // files try { string[] Files = System.IO.Directory.GetFiles(Folder); Array.Sort(Files); for (int i = 0; i < Files.Length; i++) { string Extension = System.IO.Path.GetExtension(Files[i]).ToLowerInvariant(); switch (Extension) { case ".rw": case ".csv": string Name = System.IO.Path.GetFileName(Files[i]); if (Name.Length != 0 && Name[0] != '.') { ListViewItem Item = listviewRouteFiles.Items.Add(Name); if (Extension == ".csv") { try { string text = System.IO.File.ReadAllText(Files[i], Encoding.UTF8); if (text.IndexOf("With Track", StringComparison.OrdinalIgnoreCase) >= 0 | text.IndexOf("Track.", StringComparison.OrdinalIgnoreCase) >= 0 | text.IndexOf("$Include", StringComparison.OrdinalIgnoreCase) >= 0) { Item.ImageKey = "route"; } } catch { } } else { Item.ImageKey = "route"; } Item.Tag = Files[i]; } break; } } } catch { } } } catch { } listviewRouteFiles.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent); } // route files private void listviewRouteFiles_SelectedIndexChanged(object sender, EventArgs e) { if (listviewRouteFiles.SelectedItems.Count == 1) { string t = listviewRouteFiles.SelectedItems[0].Tag as string; if (t != null) { if (System.IO.File.Exists(t)) { Result.RouteFile = t; ShowRoute(false); } } } } private void listviewRouteFiles_DoubleClick(object sender, EventArgs e) { if (listviewRouteFiles.SelectedItems.Count == 1) { string t = listviewRouteFiles.SelectedItems[0].Tag as string; if (t != null) { if (t.Length == 0 || System.IO.Directory.Exists(t)) { textboxRouteFolder.Text = t; } } } } private void listviewRouteFiles_KeyDown(object sender, KeyEventArgs e) { switch (e.KeyCode) { case Keys.Return: listviewRouteFiles_DoubleClick(null, null); break; case Keys.Back: string t = listviewRouteFiles.Tag as string; if (t != null) { if (t.Length == 0 || System.IO.Directory.Exists(t)) { textboxRouteFolder.Text = t; } } break; } } // route recently private void listviewRouteRecently_SelectedIndexChanged(object sender, EventArgs e) { if (listviewRouteRecently.SelectedItems.Count == 1) { string t = listviewRouteRecently.SelectedItems[0].Tag as string; if (t != null) { if (System.IO.File.Exists(t)) { Result.RouteFile = t; ShowRoute(false); } } } } // ============= // route details // ============= // route image private void pictureboxRouteImage_Click(object sender, EventArgs e) { if (pictureboxRouteImage.Image != null) { formImage.ShowImageDialog(pictureboxRouteImage.Image); } } // route encoding private void comboboxRouteEncoding_SelectedIndexChanged(object sender, EventArgs e) { if (comboboxRouteEncoding.Tag == null) { int i = comboboxRouteEncoding.SelectedIndex; if (i >= 0 & i < EncodingCodepages.Length) { Result.RouteEncoding = System.Text.Encoding.GetEncoding(EncodingCodepages[i]); if (i == 0) { // remove from cache for (int j = 0; j < Interface.CurrentOptions.RouteEncodings.Length; j++) { if (Interface.CurrentOptions.RouteEncodings[j].Value == Result.RouteFile) { Interface.CurrentOptions.RouteEncodings[j] = Interface.CurrentOptions.RouteEncodings[Interface.CurrentOptions.RouteEncodings.Length - 1]; Array.Resize(ref Interface.CurrentOptions.RouteEncodings, Interface.CurrentOptions.RouteEncodings.Length - 1); break; } } } else { // add to cache int j; for (j = 0; j < Interface.CurrentOptions.RouteEncodings.Length; j++) { if (Interface.CurrentOptions.RouteEncodings[j].Value == Result.RouteFile) { Interface.CurrentOptions.RouteEncodings[j].Codepage = EncodingCodepages[i]; break; } } if (j == Interface.CurrentOptions.RouteEncodings.Length) { Array.Resize(ref Interface.CurrentOptions.RouteEncodings, j + 1); Interface.CurrentOptions.RouteEncodings[j].Codepage = EncodingCodepages[i]; Interface.CurrentOptions.RouteEncodings[j].Value = Result.RouteFile; } } ShowRoute(true); } } } private void buttonRouteEncodingLatin1_Click(object sender, EventArgs e) { for (int i = 1; i < EncodingCodepages.Length; i++) { if (EncodingCodepages[i] == 1252) { comboboxRouteEncoding.SelectedIndex = i; return; } } System.Media.SystemSounds.Hand.Play(); } private void buttonRouteEncodingShiftJis_Click(object sender, EventArgs e) { for (int i = 1; i < EncodingCodepages.Length; i++) { if (EncodingCodepages[i] == 932) { comboboxRouteEncoding.SelectedIndex = i; return; } } System.Media.SystemSounds.Hand.Play(); } private void buttonRouteEncodingBig5_Click(object sender, EventArgs e) { for (int i = 1; i < EncodingCodepages.Length; i++) { if (EncodingCodepages[i] == 950) { comboboxRouteEncoding.SelectedIndex = i; return; } } System.Media.SystemSounds.Hand.Play(); } // =============== // train selection // =============== // train folder private void textboxTrainFolder_TextChanged(object sender, EventArgs e) { string Folder = textboxTrainFolder.Text; try { if (Folder.Length == 0) { // drives listviewTrainFolders.Items.Clear(); try { // MoMA says that GetDrives is flagged with [MonoTodo] System.IO.DriveInfo[] driveInfos = System.IO.DriveInfo.GetDrives(); for (int i = 0; i < driveInfos.Length; i++) { ListViewItem Item = listviewTrainFolders.Items.Add(driveInfos[i].Name); Item.ImageKey = "folder"; Item.Tag = driveInfos[i].RootDirectory.FullName; listviewTrainFolders.Tag = null; } } catch { } } else if (System.IO.Directory.Exists(Folder)) { listviewTrainFolders.Items.Clear(); // parent try { System.IO.DirectoryInfo Info = System.IO.Directory.GetParent(Folder); if (Info != null) { ListViewItem Item = listviewTrainFolders.Items.Add(".."); Item.ImageKey = "parent"; Item.Tag = Info.FullName; listviewTrainFolders.Tag = Info.FullName; } else { ListViewItem Item = listviewTrainFolders.Items.Add(".."); Item.ImageKey = "parent"; Item.Tag = ""; listviewTrainFolders.Tag = ""; } } catch { } // folders try { string[] Folders = System.IO.Directory.GetDirectories(Folder); Array.Sort(Folders); for (int i = 0; i < Folders.Length; i++) { try { System.IO.DirectoryInfo info = new System.IO.DirectoryInfo(Folders[i]); if ((info.Attributes & System.IO.FileAttributes.Hidden) == 0) { string Name = System.IO.Path.GetFileName(Folders[i]); if (Name.Length != 0 && Name[0] != '.') { string File = OpenBveApi.Path.CombineFile(Folders[i], "train.dat"); ListViewItem Item = listviewTrainFolders.Items.Add(Name); if (System.IO.File.Exists(File)) { Item.ImageKey = "train"; } else { Item.ImageKey = "folder"; } Item.Tag = Folders[i]; } } } catch { } } } catch { } } } catch { } listviewTrainFolders.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent); } // train folders private void listviewTrainFolders_SelectedIndexChanged(object sender, EventArgs e) { if (listviewTrainFolders.SelectedItems.Count == 1) { string t = listviewTrainFolders.SelectedItems[0].Tag as string; if (t != null) { if (System.IO.Directory.Exists(t)) { string File = OpenBveApi.Path.CombineFile(t, "train.dat"); if (System.IO.File.Exists(File)) { Result.TrainFolder = t; ShowTrain(false); if (checkboxTrainDefault.Checked) checkboxTrainDefault.Checked = false; } } } } } private void listviewTrainFolders_DoubleClick(object sender, EventArgs e) { if (listviewTrainFolders.SelectedItems.Count == 1) { string t = listviewTrainFolders.SelectedItems[0].Tag as string; if (t != null) { if (t.Length == 0 || System.IO.Directory.Exists(t)) { textboxTrainFolder.Text = t; } } } } private void listviewTrainFolders_KeyDown(object sender, KeyEventArgs e) { switch (e.KeyCode) { case Keys.Return: listviewTrainFolders_DoubleClick(null, null); break; case Keys.Back: string t = listviewTrainFolders.Tag as string; if (t != null) { if (t.Length == 0 || System.IO.Directory.Exists(t)) { textboxTrainFolder.Text = t; } } break; } } // train recently private void listviewTrainRecently_SelectedIndexChanged(object sender, EventArgs e) { if (listviewTrainRecently.SelectedItems.Count == 1) { string t = listviewTrainRecently.SelectedItems[0].Tag as string; if (t != null) { if (System.IO.Directory.Exists(t)) { string File = OpenBveApi.Path.CombineFile(t, "train.dat"); if (System.IO.File.Exists(File)) { Result.TrainFolder = t; ShowTrain(false); if (checkboxTrainDefault.Checked) checkboxTrainDefault.Checked = false; } } } } } // train default void CheckboxTrainDefaultCheckedChanged(object sender, System.EventArgs e) { if (checkboxTrainDefault.Checked) { if (listviewTrainFolders.SelectedItems.Count == 1) { listviewTrainFolders.SelectedItems[0].Selected = false; } if (listviewTrainRecently.SelectedItems.Count == 1) { listviewTrainRecently.SelectedItems[0].Selected = false; } ShowDefaultTrain(); } } // ============= // train details // ============= // train image private void pictureboxTrainImage_Click(object sender, EventArgs e) { if (pictureboxTrainImage.Image != null) { formImage.ShowImageDialog(pictureboxTrainImage.Image); } } // train encoding private void comboboxTrainEncoding_SelectedIndexChanged(object sender, EventArgs e) { if (comboboxTrainEncoding.Tag == null) { int i = comboboxTrainEncoding.SelectedIndex; if (i >= 0 & i < EncodingCodepages.Length) { Result.TrainEncoding = System.Text.Encoding.GetEncoding(EncodingCodepages[i]); if (i == 0) { // remove from cache for (int j = 0; j < Interface.CurrentOptions.TrainEncodings.Length; j++) { if (Interface.CurrentOptions.TrainEncodings[j].Value == Result.TrainFolder) { Interface.CurrentOptions.TrainEncodings[j] = Interface.CurrentOptions.TrainEncodings[Interface.CurrentOptions.TrainEncodings.Length - 1]; Array.Resize(ref Interface.CurrentOptions.TrainEncodings, Interface.CurrentOptions.TrainEncodings.Length - 1); break; } } } else { // add to cache int j; for (j = 0; j < Interface.CurrentOptions.TrainEncodings.Length; j++) { if (Interface.CurrentOptions.TrainEncodings[j].Value == Result.TrainFolder) { Interface.CurrentOptions.TrainEncodings[j].Codepage = EncodingCodepages[i]; break; } } if (j == Interface.CurrentOptions.TrainEncodings.Length) { Array.Resize(ref Interface.CurrentOptions.TrainEncodings, j + 1); Interface.CurrentOptions.TrainEncodings[j].Codepage = EncodingCodepages[i]; Interface.CurrentOptions.TrainEncodings[j].Value = Result.TrainFolder; } } ShowTrain(true); } } } private void buttonTrainEncodingLatin1_Click(object sender, EventArgs e) { for (int i = 1; i < EncodingCodepages.Length; i++) { if (EncodingCodepages[i] == 1252) { comboboxTrainEncoding.SelectedIndex = i; return; } } System.Media.SystemSounds.Hand.Play(); } private void buttonTrainEncodingShiftJis_Click(object sender, EventArgs e) { for (int i = 1; i < EncodingCodepages.Length; i++) { if (EncodingCodepages[i] == 932) { comboboxTrainEncoding.SelectedIndex = i; return; } } System.Media.SystemSounds.Hand.Play(); } private void buttonTrainEncodingBig5_Click(object sender, EventArgs e) { for (int i = 1; i < EncodingCodepages.Length; i++) { if (EncodingCodepages[i] == 950) { comboboxTrainEncoding.SelectedIndex = i; return; } } System.Media.SystemSounds.Hand.Play(); } // ===== // start // ===== // start private void buttonStart_Click(object sender, EventArgs e) { if (Result.RouteFile != null & Result.TrainFolder != null & !IsBusy()) { if (System.IO.File.Exists(Result.RouteFile) & System.IO.Directory.Exists(Result.TrainFolder)) { Result.Start = true; this.Close(); } } else { System.Media.SystemSounds.Exclamation.Play(); } } // ========= // functions // ========= // show route private void ShowRoute(bool UserSelectedEncoding) { if (Result.RouteFile != null) { this.Cursor = Cursors.WaitCursor; Application.DoEvents(); // determine encoding if (!UserSelectedEncoding) { comboboxRouteEncoding.Tag = new object(); comboboxRouteEncoding.SelectedIndex = 0; comboboxRouteEncoding.Items[0] = "(UTF-8)"; comboboxRouteEncoding.Tag = null; Result.RouteEncoding = System.Text.Encoding.UTF8; switch (Interface.GetEncodingFromFile(Result.RouteFile)) { case Interface.Encoding.Utf8: panelRouteEncoding.Enabled = false; comboboxRouteEncoding.SelectedIndex = 0; comboboxRouteEncoding.Items[0] = "(UTF-8)"; Result.RouteEncoding = System.Text.Encoding.UTF8; break; case Interface.Encoding.Utf16Le: panelRouteEncoding.Enabled = false; comboboxRouteEncoding.SelectedIndex = 0; comboboxRouteEncoding.Items[0] = "(UTF-16 little endian)"; Result.RouteEncoding = System.Text.Encoding.Unicode; break; case Interface.Encoding.Utf16Be: panelRouteEncoding.Enabled = false; comboboxRouteEncoding.SelectedIndex = 0; comboboxRouteEncoding.Items[0] = "(UTF-16 big endian)"; Result.RouteEncoding = System.Text.Encoding.BigEndianUnicode; break; case Interface.Encoding.Utf32Le: panelRouteEncoding.Enabled = false; comboboxRouteEncoding.SelectedIndex = 0; comboboxRouteEncoding.Items[0] = "(UTF-32 little endian)"; Result.RouteEncoding = System.Text.Encoding.UTF32; break; case Interface.Encoding.Utf32Be: panelRouteEncoding.Enabled = false; comboboxRouteEncoding.SelectedIndex = 0; comboboxRouteEncoding.Items[0] = "(UTF-32 big endian)"; Result.RouteEncoding = System.Text.Encoding.GetEncoding(12001); break; } panelRouteEncoding.Enabled = true; comboboxRouteEncoding.Tag = new object(); int i; for (i = 0; i < Interface.CurrentOptions.RouteEncodings.Length; i++) { if (Interface.CurrentOptions.RouteEncodings[i].Value == Result.RouteFile) { int j; for (j = 1; j < EncodingCodepages.Length; j++) { if (EncodingCodepages[j] == Interface.CurrentOptions.RouteEncodings[i].Codepage) { comboboxRouteEncoding.SelectedIndex = j; Result.RouteEncoding = System.Text.Encoding.GetEncoding(EncodingCodepages[j]); break; } } if (j == EncodingCodepages.Length) { comboboxRouteEncoding.SelectedIndex = 0; Result.RouteEncoding = System.Text.Encoding.UTF8; } break; } } comboboxRouteEncoding.Tag = null; } // parse route try { Game.Reset(false); bool IsRW = string.Equals(System.IO.Path.GetExtension(Result.RouteFile), ".rw", StringComparison.OrdinalIgnoreCase); CsvRwRouteParser.ParseRoute(Result.RouteFile, IsRW, Result.RouteEncoding, null, null, null, true); pictureboxRouteMap.Image = Illustrations.CreateRouteMap(pictureboxRouteMap.Width, pictureboxRouteMap.Height); pictureboxRouteGradient.Image = Illustrations.CreateRouteGradientProfile(pictureboxRouteGradient.Width, pictureboxRouteGradient.Height); // image if (Game.RouteImage.Length != 0) { try { pictureboxRouteImage.Image = Image.FromFile(Game.RouteImage); } catch { TryLoadImage(pictureboxRouteImage, "route_error.png"); } } else { //string f = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Result.RouteFile), System.IO.Path.GetFileNameWithoutExtension(Result.RouteFile)); string[] e = new string[] { ".png", ".bmp", ".gif", ".tiff", ".tif", ".jpeg", ".jpg" }; int i; for (i = 0; i < e.Length; i++) { string g = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(Result.RouteFile), System.IO.Path.GetFileNameWithoutExtension(Result.RouteFile) + e[i]); if (System.IO.File.Exists(g)) { try { pictureboxRouteImage.Image = Image.FromFile(g); } catch { pictureboxRouteImage.Image = null; } break; } } if (i == e.Length) { TryLoadImage(pictureboxRouteImage, "route_unknown.png"); } } // description string Description = Interface.ConvertNewlinesToCrLf(Game.RouteComment); if (Description.Length != 0) { textboxRouteDescription.Text = Description; } else { textboxRouteDescription.Text = System.IO.Path.GetFileNameWithoutExtension(Result.RouteFile); } textboxRouteEncodingPreview.Text = Interface.ConvertNewlinesToCrLf(Description); if (Game.TrainName != null) { checkboxTrainDefault.Text = Interface.GetInterfaceString("start_train_usedefault") + " (" + Game.TrainName + ")"; } else { checkboxTrainDefault.Text = Interface.GetInterfaceString("start_train_usedefault"); } } catch (Exception ex) { // error TryLoadImage(pictureboxRouteImage, "route_error.png"); textboxRouteDescription.Text = ex.Message; textboxRouteEncodingPreview.Text = ""; pictureboxRouteMap.Image = null; pictureboxRouteGradient.Image = null; Result.RouteFile = null; checkboxTrainDefault.Text = Interface.GetInterfaceString("start_train_usedefault"); } groupboxRouteDetails.Visible = true; if (checkboxTrainDefault.Checked) { ShowDefaultTrain(); } this.Cursor = Cursors.Default; buttonStart.Enabled = Result.RouteFile != null & Result.TrainFolder != null; } } // show train private void ShowTrain(bool UserSelectedEncoding) { if (!UserSelectedEncoding) { comboboxTrainEncoding.Tag = new object(); comboboxTrainEncoding.SelectedIndex = 0; comboboxTrainEncoding.Items[0] = "(UTF-8)"; comboboxTrainEncoding.Tag = null; Result.TrainEncoding = System.Text.Encoding.UTF8; switch (Interface.GetEncodingFromFile(Result.TrainFolder, "train.txt")) { case Interface.Encoding.Utf8: comboboxTrainEncoding.SelectedIndex = 0; comboboxTrainEncoding.Items[0] = "(UTF-8)"; Result.TrainEncoding = System.Text.Encoding.UTF8; break; case Interface.Encoding.Utf16Le: comboboxTrainEncoding.SelectedIndex = 0; comboboxTrainEncoding.Items[0] = "(UTF-16 little endian)"; Result.TrainEncoding = System.Text.Encoding.Unicode; break; case Interface.Encoding.Utf16Be: comboboxTrainEncoding.SelectedIndex = 0; comboboxTrainEncoding.Items[0] = "(UTF-16 big endian)"; Result.TrainEncoding = System.Text.Encoding.BigEndianUnicode; break; case Interface.Encoding.Utf32Le: comboboxTrainEncoding.SelectedIndex = 0; comboboxTrainEncoding.Items[0] = "(UTF-32 little endian)"; Result.TrainEncoding = System.Text.Encoding.UTF32; break; case Interface.Encoding.Utf32Be: comboboxTrainEncoding.SelectedIndex = 0; comboboxTrainEncoding.Items[0] = "(UTF-32 big endian)"; Result.TrainEncoding = System.Text.Encoding.GetEncoding(12001); break; } int i; for (i = 0; i < Interface.CurrentOptions.TrainEncodings.Length; i++) { if (Interface.CurrentOptions.TrainEncodings[i].Value == Result.TrainFolder) { int j; for (j = 1; j < EncodingCodepages.Length; j++) { if (EncodingCodepages[j] == Interface.CurrentOptions.TrainEncodings[i].Codepage) { comboboxTrainEncoding.SelectedIndex = j; Result.TrainEncoding = System.Text.Encoding.GetEncoding(EncodingCodepages[j]); break; } } if (j == EncodingCodepages.Length) { comboboxTrainEncoding.SelectedIndex = 0; Result.TrainEncoding = System.Text.Encoding.UTF8; } break; } } panelTrainEncoding.Enabled = true; comboboxTrainEncoding.Tag = null; } { // train image string File = OpenBveApi.Path.CombineFile(Result.TrainFolder, "train.png"); if (!System.IO.File.Exists(File)) { File = OpenBveApi.Path.CombineFile(Result.TrainFolder, "train.bmp"); } if (System.IO.File.Exists(File)) { try { pictureboxTrainImage.Image = Image.FromFile(File); } catch { pictureboxTrainImage.Image = null; TryLoadImage(pictureboxTrainImage, "train_error.png"); } } else { TryLoadImage(pictureboxTrainImage, "train_unknown.png"); } } { // train description string File = OpenBveApi.Path.CombineFile(Result.TrainFolder, "train.txt"); if (System.IO.File.Exists(File)) { try { string Text = System.IO.File.ReadAllText(File, Result.TrainEncoding); Text = Interface.ConvertNewlinesToCrLf(Text); textboxTrainDescription.Text = Text; textboxTrainEncodingPreview.Text = Text; } catch { textboxTrainDescription.Text = System.IO.Path.GetFileName(Result.TrainFolder); textboxTrainEncodingPreview.Text = ""; } } else { textboxTrainDescription.Text = System.IO.Path.GetFileName(Result.TrainFolder); textboxTrainEncodingPreview.Text = ""; } } groupboxTrainDetails.Visible = true; labelTrainEncoding.Enabled = true; labelTrainEncodingPreview.Enabled = true; textboxTrainEncodingPreview.Enabled = true; buttonStart.Enabled = Result.RouteFile != null & Result.TrainFolder != null; } // show default train private void ShowDefaultTrain() { if (Result.RouteFile == null || Result.RouteFile.Length == 0) { return; } if (Game.TrainName == null || Game.TrainName.Length == 0) { return; } string Folder = null; try { Folder = System.IO.Path.GetDirectoryName(Result.RouteFile); if (Game.TrainName[0] == '$') { Folder = OpenBveApi.Path.CombineDirectory(Folder, Game.TrainName); if (System.IO.Directory.Exists(Folder)) { string File = OpenBveApi.Path.CombineFile(Folder, "train.dat"); if (System.IO.File.Exists(File)) { Result.TrainFolder = Folder; ShowTrain(false); return; } } } } catch { Folder = null; } try { while (true) { string TrainFolder = OpenBveApi.Path.CombineDirectory(Folder, "Train"); if (System.IO.Directory.Exists(TrainFolder)) { try { Folder = OpenBveApi.Path.CombineDirectory(TrainFolder, Game.TrainName); } catch { Folder = null; } if (Folder != null && System.IO.Directory.Exists(Folder)) { string File = OpenBveApi.Path.CombineFile(Folder, "train.dat"); if (System.IO.File.Exists(File)) { /// train found Result.TrainFolder = Folder; ShowTrain(false); return; } } break; } else { System.IO.DirectoryInfo Info = System.IO.Directory.GetParent(Folder); if (Info != null) { Folder = Info.FullName; } else { break; } } } } catch { } /// train not found Result.TrainFolder = null; TryLoadImage(pictureboxTrainImage, "train_error.png"); textboxTrainDescription.Text = Interface.ConvertNewlinesToCrLf(Interface.GetInterfaceString("start_train_notfound") + Game.TrainName); comboboxTrainEncoding.Tag = new object(); comboboxTrainEncoding.SelectedIndex = 0; comboboxTrainEncoding.Tag = null; labelTrainEncoding.Enabled = false; panelTrainEncoding.Enabled = false; labelTrainEncodingPreview.Enabled = false; textboxTrainEncodingPreview.Enabled = false; textboxTrainEncodingPreview.Text = ""; groupboxTrainDetails.Visible = true; } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formMain.cs000066400000000000000000001733431171674032100216520ustar00rootroot00000000000000using System; using System.Drawing; using System.Windows.Forms; using Tao.Sdl; using System.Text; namespace OpenBve { internal partial class formMain : Form { internal formMain() { InitializeComponent(); } // show main dialog internal struct MainDialogResult { internal bool Start; internal string RouteFile; internal System.Text.Encoding RouteEncoding; internal string TrainFolder; internal System.Text.Encoding TrainEncoding; } internal static MainDialogResult ShowMainDialog() { formMain Dialog = new formMain(); Dialog.ShowDialog(); MainDialogResult Result = Dialog.Result; Dialog.Dispose(); return Result; } // members private MainDialogResult Result = new MainDialogResult(); private int[] EncodingCodepages = new int[0]; private Image JoystickImage = null; private string[] LanguageFiles = new string[0]; private string CurrentLanguageCode = "en-US"; // ==== // form // ==== // load private void formMain_Load(object sender, EventArgs e) { this.MinimumSize = this.Size; if (Interface.CurrentOptions.MainMenuWidth == -1 & Interface.CurrentOptions.MainMenuHeight == -1) { this.WindowState = FormWindowState.Maximized; } else if (Interface.CurrentOptions.MainMenuWidth > 0 & Interface.CurrentOptions.MainMenuHeight > 0) { this.Size = new Size(Interface.CurrentOptions.MainMenuWidth, Interface.CurrentOptions.MainMenuHeight); this.CenterToScreen(); } #pragma warning disable 0162 // Unreachable code if (Program.IsDevelopmentVersion) { labelVersion.Text = " v" + Application.ProductVersion + " (development)"; labelVersion.Location = new Point(0, labelInfoTop.Bottom); labelVersion.Width = panelInfo.Width; labelVersion.BackColor = Color.Firebrick; labelInfoCenter.Location = new Point(0, labelVersion.Bottom); labelInfoCenter.Visible = true; } else { labelVersion.Text = "v" + Application.ProductVersion; } #pragma warning restore 0162 // Unreachable code System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; // form icon try { string File = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder(), "icon.ico"); this.Icon = new Icon(File); } catch { } // use button-style radio buttons on non-Mono if (!Program.CurrentlyRunningOnMono) { radiobuttonStart.Appearance = Appearance.Button; radiobuttonStart.AutoSize = false; radiobuttonStart.Size = new Size(buttonClose.Width, buttonClose.Height); radiobuttonStart.TextAlign = ContentAlignment.MiddleCenter; radiobuttonReview.Appearance = Appearance.Button; radiobuttonReview.AutoSize = false; radiobuttonReview.Size = new Size(buttonClose.Width, buttonClose.Height); radiobuttonReview.TextAlign = ContentAlignment.MiddleCenter; radiobuttonControls.Appearance = Appearance.Button; radiobuttonControls.AutoSize = false; radiobuttonControls.Size = new Size(buttonClose.Width, buttonClose.Height); radiobuttonControls.TextAlign = ContentAlignment.MiddleCenter; radiobuttonOptions.Appearance = Appearance.Button; radiobuttonOptions.AutoSize = false; radiobuttonOptions.Size = new Size(buttonClose.Width, buttonClose.Height); radiobuttonOptions.TextAlign = ContentAlignment.MiddleCenter; radiobuttonGetAddOns.Appearance = Appearance.Button; radiobuttonGetAddOns.AutoSize = false; radiobuttonGetAddOns.Size = new Size(buttonClose.Width, buttonClose.Height); radiobuttonGetAddOns.TextAlign = ContentAlignment.MiddleCenter; } // options Interface.LoadLogs(); ListLanguages(); { int Tab = 0; string[] Args = System.Environment.GetCommandLineArgs(); for (int i = 1; i < Args.Length; i++) { switch (Args[i].ToLowerInvariant()) { case "/newgame": Tab = 0; break; case "/review": Tab = 1; break; case "/controls": Tab = 2; break; case "/options": Tab = 3; break; } } switch (Tab) { case 1: radiobuttonReview.Checked = true; break; case 2: radiobuttonControls.Checked = true; break; case 3: radiobuttonOptions.Checked = true; break; default: radiobuttonStart.Checked = true; break; } } // icons and images string MenuFolder = Program.FileSystem.GetDataFolder("Menu"); Image ParentIcon = LoadImage(MenuFolder, "icon_parent.png"); Image FolderIcon = LoadImage(MenuFolder, "icon_folder.png"); Image RouteIcon = LoadImage(MenuFolder, "icon_route.png"); Image TrainIcon = LoadImage(MenuFolder, "icon_train.png"); Image LibraryIcon = LoadImage(MenuFolder, "icon_library.png"); Image KeyboardIcon = LoadImage(MenuFolder, "icon_keyboard.png"); Image MouseIcon = LoadImage(MenuFolder, "icon_mouse.png"); Image JoystickIcon = LoadImage(MenuFolder, "icon_joystick.png"); Image GamepadIcon = LoadImage(MenuFolder, "icon_gamepad.png"); JoystickImage = LoadImage(MenuFolder, "joystick.png"); Image Logo = LoadImage(MenuFolder, "logo.png"); if (Logo != null) pictureboxLogo.Image = Logo; string flagsFolder = Program.FileSystem.GetDataFolder("Flags"); string[] flags = System.IO.Directory.GetFiles(flagsFolder); // route selection listviewRouteFiles.SmallImageList = new ImageList(); listviewRouteFiles.SmallImageList.TransparentColor = Color.White; if (ParentIcon != null) listviewRouteFiles.SmallImageList.Images.Add("parent", ParentIcon); if (FolderIcon != null) listviewRouteFiles.SmallImageList.Images.Add("folder", FolderIcon); if (RouteIcon != null) listviewRouteFiles.SmallImageList.Images.Add("route", RouteIcon); treeviewRouteAddOns.ImageList = new ImageList(); if (FolderIcon != null) treeviewRouteAddOns.ImageList.Images.Add("folder", FolderIcon); if (RouteIcon != null) treeviewRouteAddOns.ImageList.Images.Add("route", RouteIcon); foreach (string flag in flags) { try { treeviewRouteAddOns.ImageList.Images.Add(System.IO.Path.GetFileNameWithoutExtension(flag), Image.FromFile(flag)); } catch { } } listviewRouteFiles.Columns.Clear(); listviewRouteFiles.Columns.Add(""); listviewRouteRecently.Items.Clear(); listviewRouteRecently.Columns.Add(""); listviewRouteRecently.SmallImageList = new ImageList(); listviewRouteRecently.SmallImageList.TransparentColor = Color.White; if (RouteIcon != null) listviewRouteRecently.SmallImageList.Images.Add("route", RouteIcon); for (int i = 0; i < Interface.CurrentOptions.RecentlyUsedRoutes.Length; i++) { ListViewItem Item = listviewRouteRecently.Items.Add(System.IO.Path.GetFileName(Interface.CurrentOptions.RecentlyUsedRoutes[i])); Item.ImageKey = "route"; Item.Tag = Interface.CurrentOptions.RecentlyUsedRoutes[i]; } listviewRouteRecently.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); // train selection listviewTrainFolders.SmallImageList = new ImageList(); listviewTrainFolders.SmallImageList.TransparentColor = Color.White; if (ParentIcon != null) listviewTrainFolders.SmallImageList.Images.Add("parent", ParentIcon); if (FolderIcon != null) listviewTrainFolders.SmallImageList.Images.Add("folder", FolderIcon); if (TrainIcon != null) listviewTrainFolders.SmallImageList.Images.Add("train", TrainIcon); treeviewTrainAddOns.ImageList = new ImageList(); if (FolderIcon != null) treeviewTrainAddOns.ImageList.Images.Add("folder", FolderIcon); if (RouteIcon != null) treeviewTrainAddOns.ImageList.Images.Add("train", TrainIcon); foreach (string flag in flags) { try { treeviewTrainAddOns.ImageList.Images.Add(System.IO.Path.GetFileNameWithoutExtension(flag), Image.FromFile(flag)); } catch { } } listviewTrainFolders.Columns.Clear(); listviewTrainFolders.Columns.Add(""); listviewTrainRecently.Columns.Clear(); listviewTrainRecently.Columns.Add(""); listviewTrainRecently.SmallImageList = new ImageList(); listviewTrainRecently.SmallImageList.TransparentColor = Color.White; if (TrainIcon != null) listviewTrainRecently.SmallImageList.Images.Add("train", TrainIcon); for (int i = 0; i < Interface.CurrentOptions.RecentlyUsedTrains.Length; i++) { ListViewItem Item = listviewTrainRecently.Items.Add(System.IO.Path.GetFileName(Interface.CurrentOptions.RecentlyUsedTrains[i])); Item.ImageKey = "train"; Item.Tag = Interface.CurrentOptions.RecentlyUsedTrains[i]; } listviewTrainRecently.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); // text boxes if (Interface.CurrentOptions.RouteFolder.Length != 0 && System.IO.Directory.Exists(Interface.CurrentOptions.RouteFolder)) { textboxRouteFolder.Text = Interface.CurrentOptions.RouteFolder; } else { textboxRouteFolder.Text = Program.FileSystem.InitialRouteFolder; } if (Interface.CurrentOptions.TrainFolder.Length != 0 && System.IO.Directory.Exists(Interface.CurrentOptions.TrainFolder)) { textboxTrainFolder.Text = Interface.CurrentOptions.TrainFolder; } else { textboxTrainFolder.Text = Program.FileSystem.InitialTrainFolder; } // encodings { System.Text.EncodingInfo[] Info = System.Text.Encoding.GetEncodings(); EncodingCodepages = new int[Info.Length + 1]; string[] EncodingDescriptions = new string[Info.Length + 1]; EncodingCodepages[0] = System.Text.Encoding.UTF8.CodePage; EncodingDescriptions[0] = "(UTF-8)"; for (int i = 0; i < Info.Length; i++) { EncodingCodepages[i + 1] = Info[i].CodePage; try { // MoMA says that DisplayName is flagged with [MonoTodo] EncodingDescriptions[i + 1] = Info[i].DisplayName + " - " + Info[i].CodePage.ToString(Culture); } catch { EncodingDescriptions[i + 1] = Info[i].Name; } } Array.Sort(EncodingDescriptions, EncodingCodepages, 1, Info.Length); comboboxRouteEncoding.Items.Clear(); comboboxTrainEncoding.Items.Clear(); for (int i = 0; i < Info.Length + 1; i++) { comboboxRouteEncoding.Items.Add(EncodingDescriptions[i]); comboboxTrainEncoding.Items.Add(EncodingDescriptions[i]); } } // modes comboboxMode.Items.Clear(); comboboxMode.Items.AddRange(new string[] { "", "", "" }); comboboxMode.SelectedIndex = Interface.CurrentOptions.GameMode == Interface.GameMode.Arcade ? 0 : Interface.CurrentOptions.GameMode == Interface.GameMode.Expert ? 2 : 1; // review last game { if (Game.LogRouteName.Length == 0 | Game.LogTrainName.Length == 0) { radiobuttonReview.Enabled = false; } else { double ratio = Game.CurrentScore.Maximum == 0 ? 0.0 : (double)Game.CurrentScore.Value / (double)Game.CurrentScore.Maximum; if (ratio < 0.0) ratio = 0.0; if (ratio > 1.0) ratio = 1.0; int index = (int)Math.Floor(ratio * (double)Interface.RatingsCount); if (index >= Interface.RatingsCount) index = Interface.RatingsCount - 1; labelReviewRouteValue.Text = Game.LogRouteName; labelReviewTrainValue.Text = Game.LogTrainName; labelReviewDateValue.Text = Game.LogDateTime.ToString("yyyy-MM-dd", Culture); labelReviewTimeValue.Text = Game.LogDateTime.ToString("HH:mm:ss", Culture); switch (Interface.CurrentOptions.GameMode) { case Interface.GameMode.Arcade: labelRatingModeValue.Text = Interface.GetInterfaceString("mode_arcade"); break; case Interface.GameMode.Normal: labelRatingModeValue.Text = Interface.GetInterfaceString("mode_normal"); break; case Interface.GameMode.Expert: labelRatingModeValue.Text = Interface.GetInterfaceString("mode_expert"); break; default: labelRatingModeValue.Text = Interface.GetInterfaceString("mode_unkown"); break; } if (Game.CurrentScore.Maximum == 0) { labelRatingColor.BackColor = Color.Gray; labelRatingDescription.Text = Interface.GetInterfaceString("rating_unkown"); } else { Color[] Colors = new Color[] { Color.PaleVioletRed, Color.IndianRed, Color.Peru, Color.Goldenrod, Color.DarkKhaki, Color.YellowGreen, Color.MediumSeaGreen, Color.MediumAquamarine, Color.SkyBlue, Color.CornflowerBlue }; if (index >= 0 & index < Colors.Length) { labelRatingColor.BackColor = Colors[index]; } else { labelRatingColor.BackColor = Color.Gray; } labelRatingDescription.Text = Interface.GetInterfaceString("rating_" + index.ToString(Culture)); } labelRatingAchievedValue.Text = Game.CurrentScore.Value.ToString(Culture); labelRatingMaximumValue.Text = Game.CurrentScore.Maximum.ToString(Culture); labelRatingRatioValue.Text = (100.0 * ratio).ToString("0.00", Culture) + "%"; } } comboboxBlackBoxFormat.Items.Clear(); comboboxBlackBoxFormat.Items.AddRange(new string[] { "", "" }); comboboxBlackBoxFormat.SelectedIndex = 1; if (Game.BlackBoxEntryCount == 0) { labelBlackBox.Enabled = false; labelBlackBoxFormat.Enabled = false; comboboxBlackBoxFormat.Enabled = false; buttonBlackBoxExport.Enabled = false; } // controls listviewControls.SmallImageList = new ImageList(); listviewControls.SmallImageList.TransparentColor = Color.White; if (KeyboardIcon != null) listviewControls.SmallImageList.Images.Add("keyboard", KeyboardIcon); if (MouseIcon != null) listviewControls.SmallImageList.Images.Add("mouse", MouseIcon); if (JoystickIcon != null) listviewControls.SmallImageList.Images.Add("joystick", JoystickIcon); if (GamepadIcon != null) listviewControls.SmallImageList.Images.Add("gamepad", GamepadIcon); // options if (Interface.CurrentOptions.FullscreenMode) { radiobuttonFullscreen.Checked = true; } else { radiobuttonWindow.Checked = true; } comboboxVSync.Items.Clear(); comboboxVSync.Items.Add(""); comboboxVSync.Items.Add(""); comboboxVSync.SelectedIndex = Interface.CurrentOptions.VerticalSynchronization ? 1 : 0; updownWindowWidth.Value = (decimal)Interface.CurrentOptions.WindowWidth; updownWindowHeight.Value = (decimal)Interface.CurrentOptions.WindowHeight; updownFullscreenWidth.Value = (decimal)Interface.CurrentOptions.FullscreenWidth; updownFullscreenHeight.Value = (decimal)Interface.CurrentOptions.FullscreenHeight; comboboxFullscreenBits.Items.Clear(); comboboxFullscreenBits.Items.Add("16"); comboboxFullscreenBits.Items.Add("32"); comboboxFullscreenBits.SelectedIndex = Interface.CurrentOptions.FullscreenBits == 16 ? 0 : 1; comboboxInterpolation.Items.Clear(); comboboxInterpolation.Items.AddRange(new string[] { "", "", "", "", "", "" }); if ((int)Interface.CurrentOptions.Interpolation >= 0 & (int)Interface.CurrentOptions.Interpolation < comboboxInterpolation.Items.Count) { comboboxInterpolation.SelectedIndex = (int)Interface.CurrentOptions.Interpolation; } else { comboboxInterpolation.SelectedIndex = 3; } if (Interface.CurrentOptions.AnisotropicFilteringMaximum <= 0) { labelAnisotropic.Enabled = false; updownAnisotropic.Enabled = false; updownAnisotropic.Minimum = (decimal)0; updownAnisotropic.Maximum = (decimal)0; } else { updownAnisotropic.Minimum = (decimal)1; updownAnisotropic.Maximum = (decimal)Interface.CurrentOptions.AnisotropicFilteringMaximum; if ((decimal)Interface.CurrentOptions.AnisotropicFilteringLevel >= updownAnisotropic.Minimum & (decimal)Interface.CurrentOptions.AnisotropicFilteringLevel <= updownAnisotropic.Maximum) { updownAnisotropic.Value = (decimal)Interface.CurrentOptions.AnisotropicFilteringLevel; } else { updownAnisotropic.Value = updownAnisotropic.Minimum; } } updownDistance.Value = (decimal)Interface.CurrentOptions.ViewingDistance; comboboxMotionBlur.Items.Clear(); comboboxMotionBlur.Items.AddRange(new string[] { "", "", "", "" }); comboboxMotionBlur.SelectedIndex = (int)Interface.CurrentOptions.MotionBlur; trackbarTransparency.Value = (int)Interface.CurrentOptions.TransparencyMode; checkboxToppling.Checked = Interface.CurrentOptions.Toppling; checkboxCollisions.Checked = Interface.CurrentOptions.Collisions; checkboxDerailments.Checked = Interface.CurrentOptions.Derailments; checkboxBlackBox.Checked = Interface.CurrentOptions.BlackBox; checkboxJoysticksUsed.Checked = Interface.CurrentOptions.UseJoysticks; { double a = (double)(trackbarJoystickAxisThreshold.Maximum - trackbarJoystickAxisThreshold.Minimum) * Interface.CurrentOptions.JoystickAxisThreshold + (double)trackbarJoystickAxisThreshold.Minimum; int b = (int)Math.Round(a); if (b < trackbarJoystickAxisThreshold.Minimum) b = trackbarJoystickAxisThreshold.Minimum; if (b > trackbarJoystickAxisThreshold.Maximum) b = trackbarJoystickAxisThreshold.Maximum; trackbarJoystickAxisThreshold.Value = b; } comboboxSoundRange.Items.Clear(); comboboxSoundRange.Items.AddRange(new string[] { "", "", "" }); if ((int)Interface.CurrentOptions.SoundRange >= 0 & (int)Interface.CurrentOptions.SoundRange < comboboxSoundRange.Items.Count) { comboboxSoundRange.SelectedIndex = (int)Interface.CurrentOptions.SoundRange; } else { comboboxSoundRange.SelectedIndex = 0; } updownSoundNumber.Value = (decimal)Interface.CurrentOptions.SoundNumber; checkboxWarningMessages.Checked = Interface.CurrentOptions.ShowWarningMessages; checkboxErrorMessages.Checked = Interface.CurrentOptions.ShowErrorMessages; // language { string Folder = Program.FileSystem.GetDataFolder("Languages"); int j; for (j = 0; j < LanguageFiles.Length; j++) { string File = OpenBveApi.Path.CombineFile(Folder, Interface.CurrentOptions.LanguageCode + ".cfg"); if (string.Compare(File, LanguageFiles[j], StringComparison.OrdinalIgnoreCase) == 0) { comboboxLanguages.SelectedIndex = j; break; } } if (j == LanguageFiles.Length) { #if !DEBUG try { #endif string File = OpenBveApi.Path.CombineFile(Folder, "en-US.cfg"); Interface.LoadLanguage(File); ApplyLanguage(); #if !DEBUG } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } #endif } } // lists ShowScoreLog(checkboxScorePenalties.Checked); // get add-ons checkboxFilterRoutes.Image = RouteIcon; checkboxFilterTrains.Image = TrainIcon; checkboxFilterLibraries.Image = LibraryIcon; checkboxFilterSharedLibraries.Image = LibraryIcon; treeviewPackages.ImageList = new ImageList(); treeviewPackages.ImageList.Images.Add("route_notinstalled", LoadImage(MenuFolder, "icon_route_notinstalled.png")); treeviewPackages.ImageList.Images.Add("route_outdatedversion", LoadImage(MenuFolder, "icon_route_outdatedversion.png")); treeviewPackages.ImageList.Images.Add("route_latestversion", LoadImage(MenuFolder, "icon_route_latestversion.png")); treeviewPackages.ImageList.Images.Add("route_protected", LoadImage(MenuFolder, "icon_route_protected.png")); treeviewPackages.ImageList.Images.Add("train_notinstalled", LoadImage(MenuFolder, "icon_train_notinstalled.png")); treeviewPackages.ImageList.Images.Add("train_outdatedversion", LoadImage(MenuFolder, "icon_train_outdatedversion.png")); treeviewPackages.ImageList.Images.Add("train_latestversion", LoadImage(MenuFolder, "icon_train_latestversion.png")); treeviewPackages.ImageList.Images.Add("train_protected", LoadImage(MenuFolder, "icon_train_protected.png")); treeviewPackages.ImageList.Images.Add("library_notinstalled", LoadImage(MenuFolder, "icon_library_notinstalled.png")); treeviewPackages.ImageList.Images.Add("library_outdatedversion", LoadImage(MenuFolder, "icon_library_outdatedversion.png")); treeviewPackages.ImageList.Images.Add("library_latestversion", LoadImage(MenuFolder, "icon_library_latestversion.png")); treeviewPackages.ImageList.Images.Add("library_protected", LoadImage(MenuFolder, "icon_library_protected.png")); treeviewPackages.ImageList.Images.Add("folder", LoadImage(MenuFolder, "icon_folder.png")); foreach (string flag in flags) { try { treeviewPackages.ImageList.Images.Add(System.IO.Path.GetFileNameWithoutExtension(flag), Image.FromFile(flag)); } catch { } } // result Result.Start = false; Result.RouteFile = null; Result.RouteEncoding = System.Text.Encoding.UTF8; Result.TrainFolder = null; Result.TrainEncoding = System.Text.Encoding.UTF8; } // apply language private string GetSomething(string text, string fallback) { text = text.Trim(); int count = 0; for (int i = 0; i < text.Length; i++) { if (char.IsLetterOrDigit(text, i)) { count++; } if (char.IsSurrogatePair(text, i)) { i++; } } if (count >= 5 & count >= text.Length * 3 / 4) { return text; } else { return fallback; } } private void ApplyLanguage() { // panel radiobuttonStart.Text = Interface.GetInterfaceString("panel_start"); radiobuttonReview.Text = Interface.GetInterfaceString("panel_review"); radiobuttonGetAddOns.Text = Interface.GetInterfaceString("panel_getaddons"); radiobuttonControls.Text = Interface.GetInterfaceString("panel_controls"); radiobuttonOptions.Text = Interface.GetInterfaceString("panel_options"); linkHomepage.Text = GetSomething(Interface.GetInterfaceString("panel_homepage"), "Visit official homepage"); linkUpdates.Text = Interface.GetInterfaceString("panel_updates"); buttonClose.Text = Interface.GetInterfaceString("panel_close"); // options labelOptionsTitle.Text = Interface.GetInterfaceString("options_title"); groupboxDisplayMode.Text = Interface.GetInterfaceString("options_display_mode"); radiobuttonWindow.Text = Interface.GetInterfaceString("options_display_mode_window"); radiobuttonFullscreen.Text = Interface.GetInterfaceString("options_display_mode_fullscreen"); labelVSync.Text = Interface.GetInterfaceString("options_display_vsync"); comboboxVSync.Items[0] = Interface.GetInterfaceString("options_display_vsync_off"); comboboxVSync.Items[1] = Interface.GetInterfaceString("options_display_vsync_on"); groupboxWindow.Text = Interface.GetInterfaceString("options_display_window"); labelWindowWidth.Text = Interface.GetInterfaceString("options_display_window_width"); labelWindowHeight.Text = Interface.GetInterfaceString("options_display_window_height"); groupboxFullscreen.Text = Interface.GetInterfaceString("options_display_fullscreen"); labelFullscreenWidth.Text = Interface.GetInterfaceString("options_display_fullscreen_width"); labelFullscreenHeight.Text = Interface.GetInterfaceString("options_display_fullscreen_height"); labelFullscreenBits.Text = Interface.GetInterfaceString("options_display_fullscreen_bits"); groupboxInterpolation.Text = Interface.GetInterfaceString("options_quality_interpolation"); labelInterpolation.Text = Interface.GetInterfaceString("options_quality_interpolation_mode"); comboboxInterpolation.Items[0] = Interface.GetInterfaceString("options_quality_interpolation_mode_nearest"); comboboxInterpolation.Items[1] = Interface.GetInterfaceString("options_quality_interpolation_mode_bilinear"); comboboxInterpolation.Items[2] = Interface.GetInterfaceString("options_quality_interpolation_mode_nearestmipmap"); comboboxInterpolation.Items[3] = Interface.GetInterfaceString("options_quality_interpolation_mode_bilinearmipmap"); comboboxInterpolation.Items[4] = Interface.GetInterfaceString("options_quality_interpolation_mode_trilinearmipmap"); comboboxInterpolation.Items[5] = Interface.GetInterfaceString("options_quality_interpolation_mode_anisotropic"); labelAnisotropic.Text = Interface.GetInterfaceString("options_quality_interpolation_anisotropic_level"); labelTransparency.Text = Interface.GetInterfaceString("options_quality_interpolation_transparency"); labelTransparencyPerformance.Text = Interface.GetInterfaceString("options_quality_interpolation_transparency_sharp"); labelTransparencyQuality.Text = Interface.GetInterfaceString("options_quality_interpolation_transparency_smooth"); groupboxDistance.Text = Interface.GetInterfaceString("options_quality_distance"); labelDistance.Text = Interface.GetInterfaceString("options_quality_distance_viewingdistance"); labelDistanceUnit.Text = Interface.GetInterfaceString("options_quality_distance_viewingdistance_meters"); labelMotionBlur.Text = "options_quality_distance_motionblur"; comboboxMotionBlur.Items[0] = Interface.GetInterfaceString("options_quality_distance_motionblur_none"); comboboxMotionBlur.Items[1] = Interface.GetInterfaceString("options_quality_distance_motionblur_low"); comboboxMotionBlur.Items[2] = Interface.GetInterfaceString("options_quality_distance_motionblur_medium"); comboboxMotionBlur.Items[3] = Interface.GetInterfaceString("options_quality_distance_motionblur_high"); labelMotionBlur.Text = Interface.GetInterfaceString("options_quality_distance_motionblur"); groupboxSimulation.Text = Interface.GetInterfaceString("options_misc_simulation"); checkboxToppling.Text = Interface.GetInterfaceString("options_misc_simulation_toppling"); checkboxCollisions.Text = Interface.GetInterfaceString("options_misc_simulation_collisions"); checkboxDerailments.Text = Interface.GetInterfaceString("options_misc_simulation_derailments"); checkboxBlackBox.Text = Interface.GetInterfaceString("options_misc_simulation_blackbox"); groupboxControls.Text = Interface.GetInterfaceString("options_misc_controls"); checkboxJoysticksUsed.Text = Interface.GetInterfaceString("options_misc_controls_joysticks"); labelJoystickAxisThreshold.Text = Interface.GetInterfaceString("options_misc_controls_threshold"); groupboxSound.Text = Interface.GetInterfaceString("options_misc_sound"); labelSoundRange.Text = Interface.GetInterfaceString("options_misc_sound_range"); comboboxSoundRange.Items[0] = Interface.GetInterfaceString("options_misc_sound_range_low"); comboboxSoundRange.Items[1] = Interface.GetInterfaceString("options_misc_sound_range_medium"); comboboxSoundRange.Items[2] = Interface.GetInterfaceString("options_misc_sound_range_high"); labelSoundNumber.Text = Interface.GetInterfaceString("options_misc_sound_number"); groupboxVerbosity.Text = Interface.GetInterfaceString("options_verbosity"); checkboxWarningMessages.Text = Interface.GetInterfaceString("options_verbosity_warningmessages"); checkboxErrorMessages.Text = Interface.GetInterfaceString("options_verbosity_errormessages"); // start labelStartTitle.Text = Interface.GetInterfaceString("start_title"); labelRoute.Text = Interface.GetInterfaceString("start_route"); groupboxRouteSelection.Text = Interface.GetInterfaceString("start_route_selection"); tabpageRouteManaged.Text = Interface.GetInterfaceString("start_route_addons"); tabpageRouteBrowse.Text = Interface.GetInterfaceString("start_route_browse"); tabpageRouteRecently.Text = Interface.GetInterfaceString("start_route_recently"); groupboxRouteDetails.Text = Interface.GetInterfaceString("start_route_details"); tabpageRouteDescription.Text = Interface.GetInterfaceString("start_route_description"); tabpageRouteMap.Text = Interface.GetInterfaceString("start_route_map"); tabpageRouteGradient.Text = Interface.GetInterfaceString("start_route_gradient"); tabpageRouteSettings.Text = Interface.GetInterfaceString("start_route_settings"); labelRouteEncoding.Text = Interface.GetInterfaceString("start_route_settings_encoding"); comboboxRouteEncoding.Items[0] = Interface.GetInterfaceString("(UTF-8)"); labelRouteEncodingPreview.Text = Interface.GetInterfaceString("start_route_settings_encoding_preview"); labelTrain.Text = Interface.GetInterfaceString("start_train"); groupboxTrainSelection.Text = Interface.GetInterfaceString("start_train_selection"); tabpageTrainManaged.Text = Interface.GetInterfaceString("start_train_addons"); tabpageTrainBrowse.Text = Interface.GetInterfaceString("start_train_browse"); tabpageTrainRecently.Text = Interface.GetInterfaceString("start_train_recently"); tabpageTrainDefault.Text = Interface.GetInterfaceString("start_train_default"); checkboxTrainDefault.Text = Interface.GetInterfaceString("start_train_usedefault"); groupboxTrainDetails.Text = Interface.GetInterfaceString("start_train_details"); tabpageTrainDescription.Text = Interface.GetInterfaceString("start_train_description"); tabpageTrainSettings.Text = Interface.GetInterfaceString("start_train_settings"); labelTrainEncoding.Text = Interface.GetInterfaceString("start_train_settings_encoding"); comboboxTrainEncoding.Items[0] = Interface.GetInterfaceString("(UTF-8)"); labelTrainEncodingPreview.Text = Interface.GetInterfaceString("start_train_settings_encoding_preview"); labelStart.Text = Interface.GetInterfaceString("start_start"); labelMode.Text = Interface.GetInterfaceString("start_start_mode"); buttonStart.Text = Interface.GetInterfaceString("start_start_start"); comboboxMode.Items[0] = Interface.GetInterfaceString("mode_arcade"); comboboxMode.Items[1] = Interface.GetInterfaceString("mode_normal"); comboboxMode.Items[2] = Interface.GetInterfaceString("mode_expert"); // getaddons labelGetAddOnsTitle.Text = Interface.GetInterfaceString("getaddons_title"); labelFilter.Text = Interface.GetInterfaceString("getaddons_filter"); checkboxFilterRoutes.Text = Interface.GetInterfaceString("getaddons_filter_routes"); checkboxFilterTrains.Text = Interface.GetInterfaceString("getaddons_filter_trains"); checkboxFilterLibraries.Text = Interface.GetInterfaceString("getaddons_filter_libraries"); checkboxFilterSharedLibraries.Text = Interface.GetInterfaceString("getaddons_filter_sharedlibraries"); checkboxFilterNoWIPs.Text = Interface.GetInterfaceString("getaddons_filter_nowips"); checkboxFilterUpdates.Text = Interface.GetInterfaceString("getaddons_filter_onlyupdates"); groupboxPackage.Text = Interface.GetInterfaceString("getaddons_package"); buttonPackageInstall.Text = Interface.GetInterfaceString("getaddons_package_install"); buttonPackageRemove.Text = Interface.GetInterfaceString("getaddons_package_remove"); buttonScreenshotPrevious.Text = Interface.GetInterfaceString("getaddons_screenshot_previous"); buttonScreenshotNext.Text = Interface.GetInterfaceString("getaddons_screenshot_next"); // review labelReviewTitle.Text = Interface.GetInterfaceString("review_title"); labelConditions.Text = Interface.GetInterfaceString("review_conditions"); groupboxReviewRoute.Text = Interface.GetInterfaceString("review_conditions_route"); labelReviewRouteCaption.Text = Interface.GetInterfaceString("review_conditions_route_file"); groupboxReviewTrain.Text = Interface.GetInterfaceString("review_conditions_train"); labelReviewTrainCaption.Text = Interface.GetInterfaceString("review_conditions_train_folder"); groupboxReviewDateTime.Text = Interface.GetInterfaceString("review_conditions_datetime"); labelReviewDateCaption.Text = Interface.GetInterfaceString("review_conditions_datetime_date"); labelReviewTimeCaption.Text = Interface.GetInterfaceString("review_conditions_datetime_time"); labelScore.Text = Interface.GetInterfaceString("review_score"); groupboxRating.Text = Interface.GetInterfaceString("review_score_rating"); labelRatingModeCaption.Text = Interface.GetInterfaceString("review_score_rating_mode"); switch (Interface.CurrentOptions.GameMode) { case Interface.GameMode.Arcade: labelRatingModeValue.Text = Interface.GetInterfaceString("mode_arcade"); break; case Interface.GameMode.Normal: labelRatingModeValue.Text = Interface.GetInterfaceString("mode_normal"); break; case Interface.GameMode.Expert: labelRatingModeValue.Text = Interface.GetInterfaceString("mode_expert"); break; default: labelRatingModeValue.Text = Interface.GetInterfaceString("mode_unkown"); break; } { double ratio = Game.CurrentScore.Maximum == 0 ? 0.0 : (double)Game.CurrentScore.Value / (double)Game.CurrentScore.Maximum; if (ratio < 0.0) ratio = 0.0; if (ratio > 1.0) ratio = 1.0; int index = (int)Math.Floor(ratio * (double)Interface.RatingsCount); if (index >= Interface.RatingsCount) index = Interface.RatingsCount - 1; if (Game.CurrentScore.Maximum == 0) { labelRatingDescription.Text = Interface.GetInterfaceString("rating_unkown"); } else { labelRatingDescription.Text = Interface.GetInterfaceString("rating_" + index.ToString(System.Globalization.CultureInfo.InvariantCulture)); } } labelRatingAchievedCaption.Text = Interface.GetInterfaceString("review_score_rating_achieved"); labelRatingMaximumCaption.Text = Interface.GetInterfaceString("review_score_rating_maximum"); labelRatingRatioCaption.Text = Interface.GetInterfaceString("review_score_rating_ratio"); groupboxScore.Text = Interface.GetInterfaceString("review_score_log"); listviewScore.Columns[0].Text = Interface.GetInterfaceString("review_score_log_list_time"); listviewScore.Columns[1].Text = Interface.GetInterfaceString("review_score_log_list_position"); listviewScore.Columns[2].Text = Interface.GetInterfaceString("review_score_log_list_value"); listviewScore.Columns[3].Text = Interface.GetInterfaceString("review_score_log_list_cumulative"); listviewScore.Columns[4].Text = Interface.GetInterfaceString("review_score_log_list_reason"); ShowScoreLog(checkboxScorePenalties.Checked); checkboxScorePenalties.Text = Interface.GetInterfaceString("review_score_log_penalties"); buttonScoreExport.Text = Interface.GetInterfaceString("review_score_log_export"); labelBlackBox.Text = Interface.GetInterfaceString("review_blackbox"); labelBlackBoxFormat.Text = Interface.GetInterfaceString("review_blackbox_format"); comboboxBlackBoxFormat.Items[0] = Interface.GetInterfaceString("review_blackbox_format_csv"); comboboxBlackBoxFormat.Items[1] = Interface.GetInterfaceString("review_blackbox_format_text"); buttonBlackBoxExport.Text = Interface.GetInterfaceString("review_blackbox_export"); // controls for (int i = 0; i < listviewControls.SelectedItems.Count; i++) { listviewControls.SelectedItems[i].Selected = false; } labelControlsTitle.Text = Interface.GetInterfaceString("controls_title"); listviewControls.Columns[0].Text = Interface.GetInterfaceString("controls_list_command"); listviewControls.Columns[1].Text = Interface.GetInterfaceString("controls_list_type"); listviewControls.Columns[2].Text = Interface.GetInterfaceString("controls_list_description"); listviewControls.Columns[3].Text = Interface.GetInterfaceString("controls_list_assignment"); buttonControlAdd.Text = Interface.GetInterfaceString("controls_add"); buttonControlRemove.Text = Interface.GetInterfaceString("controls_remove"); buttonControlsImport.Text = Interface.GetInterfaceString("controls_import"); buttonControlsExport.Text = Interface.GetInterfaceString("controls_export"); buttonControlUp.Text = Interface.GetInterfaceString("controls_up"); buttonControlDown.Text = Interface.GetInterfaceString("controls_down"); groupboxControl.Text = Interface.GetInterfaceString("controls_selection"); labelCommand.Text = Interface.GetInterfaceString("controls_selection_command"); radiobuttonKeyboard.Text = Interface.GetInterfaceString("controls_selection_keyboard"); labelKeyboardKey.Text = Interface.GetInterfaceString("controls_selection_keyboard_key"); labelKeyboardModifier.Text = Interface.GetInterfaceString("controls_selection_keyboard_modifiers"); checkboxKeyboardShift.Text = Interface.GetInterfaceString("controls_selection_keyboard_modifiers_shift"); checkboxKeyboardCtrl.Text = Interface.GetInterfaceString("controls_selection_keyboard_modifiers_ctrl"); checkboxKeyboardAlt.Text = Interface.GetInterfaceString("controls_selection_keyboard_modifiers_alt"); radiobuttonJoystick.Text = Interface.GetInterfaceString("controls_selection_joystick"); labelJoystickAssignmentCaption.Text = Interface.GetInterfaceString("controls_selection_joystick_assignment"); textboxJoystickGrab.Text = Interface.GetInterfaceString("controls_selection_joystick_assignment_grab"); groupboxJoysticks.Text = Interface.GetInterfaceString("controls_attached"); { listviewControls.Items.Clear(); comboboxCommand.Items.Clear(); for (int i = 0; i < Interface.CommandInfos.Length; i++) { comboboxCommand.Items.Add(Interface.CommandInfos[i].Name + " - " + Interface.CommandInfos[i].Description); } comboboxKeyboardKey.Items.Clear(); for (int i = 0; i < Interface.Keys.Length; i++) { comboboxKeyboardKey.Items.Add(Interface.Keys[i].Description); } ListViewItem[] Items = new ListViewItem[Interface.CurrentControls.Length]; for (int i = 0; i < Interface.CurrentControls.Length; i++) { Items[i] = new ListViewItem(new string[] { "", "", "", "" }); UpdateControlListElement(Items[i], i, false); } listviewControls.Items.AddRange(Items); listviewControls.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); } } // form closing private void formMain_FormClosing(object sender, FormClosingEventArgs e) { if (IsBusy()) { MessageBox.Show("The form cannot be closed because add-ons are currently being maintained.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); e.Cancel = true; return; } Interface.CurrentOptions.LanguageCode = CurrentLanguageCode; Interface.CurrentOptions.FullscreenMode = radiobuttonFullscreen.Checked; Interface.CurrentOptions.VerticalSynchronization = comboboxVSync.SelectedIndex == 1; Interface.CurrentOptions.WindowWidth = (int)Math.Round(updownWindowWidth.Value); Interface.CurrentOptions.WindowHeight = (int)Math.Round(updownWindowHeight.Value); Interface.CurrentOptions.FullscreenWidth = (int)Math.Round(updownFullscreenWidth.Value); Interface.CurrentOptions.FullscreenHeight = (int)Math.Round(updownFullscreenHeight.Value); Interface.CurrentOptions.FullscreenBits = comboboxFullscreenBits.SelectedIndex == 0 ? 16 : 32; Interface.CurrentOptions.Interpolation = (Interface.InterpolationMode)comboboxInterpolation.SelectedIndex; Interface.CurrentOptions.AnisotropicFilteringLevel = (int)Math.Round(updownAnisotropic.Value); Interface.CurrentOptions.TransparencyMode = (Renderer.TransparencyMode)trackbarTransparency.Value; Interface.CurrentOptions.ViewingDistance = (int)Math.Round(updownDistance.Value); Interface.CurrentOptions.MotionBlur = (Interface.MotionBlurMode)comboboxMotionBlur.SelectedIndex; Interface.CurrentOptions.Toppling = checkboxToppling.Checked; Interface.CurrentOptions.Collisions = checkboxCollisions.Checked; Interface.CurrentOptions.Derailments = checkboxDerailments.Checked; Interface.CurrentOptions.GameMode = (Interface.GameMode)comboboxMode.SelectedIndex; Interface.CurrentOptions.BlackBox = checkboxBlackBox.Checked; Interface.CurrentOptions.UseJoysticks = checkboxJoysticksUsed.Checked; Interface.CurrentOptions.JoystickAxisThreshold = ((double)trackbarJoystickAxisThreshold.Value - (double)trackbarJoystickAxisThreshold.Minimum) / (double)(trackbarJoystickAxisThreshold.Maximum - trackbarJoystickAxisThreshold.Minimum); Interface.CurrentOptions.SoundRange = (Interface.SoundRange)comboboxSoundRange.SelectedIndex; Interface.CurrentOptions.SoundNumber = (int)Math.Round(updownSoundNumber.Value); Interface.CurrentOptions.ShowWarningMessages = checkboxWarningMessages.Checked; Interface.CurrentOptions.ShowErrorMessages = checkboxErrorMessages.Checked; Interface.CurrentOptions.RouteFolder = textboxRouteFolder.Text; Interface.CurrentOptions.TrainFolder = textboxTrainFolder.Text; Interface.CurrentOptions.MainMenuWidth = this.WindowState == FormWindowState.Maximized ? -1 : this.Size.Width; Interface.CurrentOptions.MainMenuHeight = this.WindowState == FormWindowState.Maximized ? -1 : this.Size.Height; if (Result.Start) { // recently used routes if (Interface.CurrentOptions.RecentlyUsedLimit > 0) { int i; for (i = 0; i < Interface.CurrentOptions.RecentlyUsedRoutes.Length; i++) { if (string.Compare(Result.RouteFile, Interface.CurrentOptions.RecentlyUsedRoutes[i], StringComparison.OrdinalIgnoreCase) == 0) { break; } } if (i == Interface.CurrentOptions.RecentlyUsedRoutes.Length) { if (Interface.CurrentOptions.RecentlyUsedRoutes.Length < Interface.CurrentOptions.RecentlyUsedLimit) { Array.Resize(ref Interface.CurrentOptions.RecentlyUsedRoutes, i + 1); } else { i--; } } for (int j = i; j > 0; j--) { Interface.CurrentOptions.RecentlyUsedRoutes[j] = Interface.CurrentOptions.RecentlyUsedRoutes[j - 1]; } Interface.CurrentOptions.RecentlyUsedRoutes[0] = Result.RouteFile; } // recently used trains if (Interface.CurrentOptions.RecentlyUsedLimit > 0) { int i; for (i = 0; i < Interface.CurrentOptions.RecentlyUsedTrains.Length; i++) { if (string.Compare(Result.TrainFolder, Interface.CurrentOptions.RecentlyUsedTrains[i], StringComparison.OrdinalIgnoreCase) == 0) { break; } } if (i == Interface.CurrentOptions.RecentlyUsedTrains.Length) { if (Interface.CurrentOptions.RecentlyUsedTrains.Length < Interface.CurrentOptions.RecentlyUsedLimit) { Array.Resize(ref Interface.CurrentOptions.RecentlyUsedTrains, i + 1); } else { i--; } } for (int j = i; j > 0; j--) { Interface.CurrentOptions.RecentlyUsedTrains[j] = Interface.CurrentOptions.RecentlyUsedTrains[j - 1]; } Interface.CurrentOptions.RecentlyUsedTrains[0] = Result.TrainFolder; } } // remove non-existing recently used routes { int n = 0; string[] a = new string[Interface.CurrentOptions.RecentlyUsedRoutes.Length]; for (int i = 0; i < Interface.CurrentOptions.RecentlyUsedRoutes.Length; i++) { if (System.IO.File.Exists(Interface.CurrentOptions.RecentlyUsedRoutes[i])) { a[n] = Interface.CurrentOptions.RecentlyUsedRoutes[i]; n++; } } Array.Resize(ref a, n); Interface.CurrentOptions.RecentlyUsedRoutes = a; } // remove non-existing recently used trains { int n = 0; string[] a = new string[Interface.CurrentOptions.RecentlyUsedTrains.Length]; for (int i = 0; i < Interface.CurrentOptions.RecentlyUsedTrains.Length; i++) { if (System.IO.Directory.Exists(Interface.CurrentOptions.RecentlyUsedTrains[i])) { a[n] = Interface.CurrentOptions.RecentlyUsedTrains[i]; n++; } } Array.Resize(ref a, n); Interface.CurrentOptions.RecentlyUsedTrains = a; } // remove non-existing route encoding mappings { int n = 0; Interface.EncodingValue[] a = new Interface.EncodingValue[Interface.CurrentOptions.RouteEncodings.Length]; for (int i = 0; i < Interface.CurrentOptions.RouteEncodings.Length; i++) { if (System.IO.File.Exists(Interface.CurrentOptions.RouteEncodings[i].Value)) { a[n] = Interface.CurrentOptions.RouteEncodings[i]; n++; } } Array.Resize(ref a, n); Interface.CurrentOptions.RouteEncodings = a; } // remove non-existing train encoding mappings { int n = 0; Interface.EncodingValue[] a = new Interface.EncodingValue[Interface.CurrentOptions.TrainEncodings.Length]; for (int i = 0; i < Interface.CurrentOptions.TrainEncodings.Length; i++) { if (System.IO.Directory.Exists(Interface.CurrentOptions.TrainEncodings[i].Value)) { a[n] = Interface.CurrentOptions.TrainEncodings[i]; n++; } } Array.Resize(ref a, n); Interface.CurrentOptions.TrainEncodings = a; } // clear cache string directory = System.IO.Path.Combine(Program.FileSystem.SettingsFolder, "Cache"); ClearCache(directory, NumberOfDaysScreenshotsAreCached); // finish #if !DEBUG try { #endif Interface.SaveOptions(); #if !DEBUG } catch (Exception ex) { MessageBox.Show(ex.Message, "Save options", MessageBoxButtons.OK, MessageBoxIcon.Hand); } #endif #if !DEBUG try { #endif Interface.SaveControls(null); #if !DEBUG } catch (Exception ex) { MessageBox.Show(ex.Message, "Save controls", MessageBoxButtons.OK, MessageBoxIcon.Hand); } #endif } // resize private void formMain_Resize(object sender, EventArgs e) { try { int wt = panelStart.Width; int ox = labelStart.Left; int wa = (wt - 3 * ox) / 2; int wb = (wt - 3 * ox) / 2; groupboxRouteSelection.Width = wa; groupboxRouteDetails.Left = 2 * ox + wa; groupboxRouteDetails.Width = wb; groupboxTrainSelection.Width = wa; groupboxTrainDetails.Left = 2 * ox + wa; groupboxTrainDetails.Width = wb; int oy = (labelRoute.Top - labelStartTitleBackground.Height) / 2; int ht = (labelStart.Top - labelRoute.Top - 4 * oy) / 2 - labelRoute.Height - oy; groupboxRouteSelection.Height = ht; groupboxRouteDetails.Height = ht; labelTrain.Top = groupboxRouteSelection.Top + groupboxRouteSelection.Height + 2 * oy; groupboxTrainSelection.Top = labelTrain.Top + labelTrain.Height + oy; groupboxTrainDetails.Top = labelTrain.Top + labelTrain.Height + oy; groupboxTrainSelection.Height = ht; groupboxTrainDetails.Height = ht; tabcontrolRouteSelection.Width = groupboxRouteSelection.Width - 2 * tabcontrolRouteSelection.Left; tabcontrolRouteSelection.Height = groupboxRouteSelection.Height - 3 * tabcontrolRouteSelection.Top / 2; tabcontrolRouteDetails.Width = groupboxRouteDetails.Width - 2 * tabcontrolRouteDetails.Left; tabcontrolRouteDetails.Height = groupboxRouteDetails.Height - 3 * tabcontrolRouteDetails.Top / 2; tabcontrolTrainSelection.Width = groupboxTrainSelection.Width - 2 * tabcontrolTrainSelection.Left; tabcontrolTrainSelection.Height = groupboxTrainSelection.Height - 3 * tabcontrolTrainSelection.Top / 2; tabcontrolTrainDetails.Width = groupboxTrainDetails.Width - 2 * tabcontrolTrainDetails.Left; tabcontrolTrainDetails.Height = groupboxTrainDetails.Height - 3 * tabcontrolTrainDetails.Top / 2; } catch { } try { int width = Math.Min((panelOptions.Width - 24) / 2, 420); panelOptionsLeft.Width = width; panelOptionsRight.Left = panelOptionsLeft.Left + width + 8; panelOptionsRight.Width = width; } catch { } try { int width = Math.Min((panelReview.Width - 32) / 3, 360); groupboxReviewRoute.Width = width; groupboxReviewTrain.Left = groupboxReviewRoute.Left + width + 8; groupboxReviewTrain.Width = width; groupboxReviewDateTime.Left = groupboxReviewTrain.Left + width + 8; groupboxReviewDateTime.Width = width; } catch { } } // shown private void formMain_Shown(object sender, EventArgs e) { if (radiobuttonStart.Checked) { listviewRouteFiles.Focus(); } else if (radiobuttonReview.Checked) { listviewScore.Focus(); } else if (radiobuttonControls.Checked) { listviewControls.Focus(); } else if (radiobuttonOptions.Checked) { comboboxLanguages.Focus(); } formMain_Resize(null, null); if (this.WindowState != FormWindowState.Maximized) { Size sss = this.ClientRectangle.Size; System.Windows.Forms.Screen s = System.Windows.Forms.Screen.FromControl(this); if ((double)this.Width >= 0.95 * (double)s.WorkingArea.Width | (double)this.Height >= 0.95 * (double)s.WorkingArea.Height) { this.WindowState = FormWindowState.Maximized; } } // add-ons TextboxRouteFilterTextChanged(null, null); TextboxTrainFilterTextChanged(null, null); // command line arguments if (Result.TrainFolder != null) { if (checkboxTrainDefault.Checked) checkboxTrainDefault.Checked = false; ShowTrain(false); } if (Result.RouteFile != null) { ShowRoute(false); } } // list languages private void ListLanguages() { string Folder = Program.FileSystem.GetDataFolder("Languages"); if (System.IO.Directory.Exists(Folder)) { string[] Files = System.IO.Directory.GetFiles(Folder); string[] LanguageNames = new string[Files.Length]; LanguageFiles = new string[Files.Length]; int n = 0; for (int i = 0; i < Files.Length; i++) { string Title = System.IO.Path.GetFileName(Files[i]); if (Title.EndsWith(".cfg", StringComparison.OrdinalIgnoreCase)) { string Code = Title.Substring(0, Title.Length - 4); string[] Lines = System.IO.File.ReadAllLines(Files[i], System.Text.Encoding.UTF8); string Section = ""; string Name = Code; for (int j = 0; j < Lines.Length; j++) { Lines[j] = Lines[j].Trim(); if (Lines[j].StartsWith("[", StringComparison.Ordinal) & Lines[j].EndsWith("]", StringComparison.Ordinal)) { Section = Lines[j].Substring(1, Lines[j].Length - 2).Trim().ToLowerInvariant(); } else if (!Lines[j].StartsWith(";", StringComparison.OrdinalIgnoreCase)) { int k = Lines[j].IndexOf('='); if (k >= 0) { string Key = Lines[j].Substring(0, k).TrimEnd().ToLowerInvariant(); string Value = Lines[j].Substring(k + 1).TrimStart(); if (Section == "language" & Key == "name") { Name = Value; break; } } } } LanguageFiles[n] = Files[i]; LanguageNames[n] = Name; n++; } } Array.Resize(ref LanguageFiles, n); Array.Resize(ref LanguageNames, n); Array.Sort(LanguageNames, LanguageFiles); comboboxLanguages.Items.Clear(); for (int i = 0; i < n; i++) { comboboxLanguages.Items.Add(LanguageNames[i]); } } else { LanguageFiles = new string[] { }; comboboxLanguages.Items.Clear(); } } // ======== // top page // ======== // page selection private void radiobuttonStart_CheckedChanged(object sender, EventArgs e) { panelStart.Visible = true; panelReview.Visible = false; panelControls.Visible = false; panelOptions.Visible = false; panelGetAddOns.Visible = false; panelPanels.BackColor = labelStartTitle.BackColor; pictureboxJoysticks.Visible = false; radiobuttonStart.BackColor = SystemColors.ButtonHighlight; radiobuttonReview.BackColor = SystemColors.ButtonFace; radiobuttonControls.BackColor = SystemColors.ButtonFace; radiobuttonOptions.BackColor = SystemColors.ButtonFace; radiobuttonGetAddOns.BackColor = SystemColors.ButtonFace; UpdateRadioButtonBackColor(); } private void radiobuttonReview_CheckedChanged(object sender, EventArgs e) { panelReview.Visible = true; panelStart.Visible = false; panelControls.Visible = false; panelOptions.Visible = false; panelGetAddOns.Visible = false; panelPanels.BackColor = labelReviewTitle.BackColor; pictureboxJoysticks.Visible = false; radiobuttonStart.BackColor = SystemColors.ButtonFace; radiobuttonReview.BackColor = SystemColors.ButtonHighlight; radiobuttonControls.BackColor = SystemColors.ButtonFace; radiobuttonOptions.BackColor = SystemColors.ButtonFace; radiobuttonGetAddOns.BackColor = SystemColors.ButtonFace; UpdateRadioButtonBackColor(); } private void radiobuttonControls_CheckedChanged(object sender, EventArgs e) { panelControls.Visible = true; panelStart.Visible = false; panelReview.Visible = false; panelOptions.Visible = false; panelGetAddOns.Visible = false; panelPanels.BackColor = labelControlsTitle.BackColor; pictureboxJoysticks.Visible = true; radiobuttonStart.BackColor = SystemColors.ButtonFace; radiobuttonReview.BackColor = SystemColors.ButtonFace; radiobuttonControls.BackColor = SystemColors.ButtonHighlight; radiobuttonOptions.BackColor = SystemColors.ButtonFace; radiobuttonGetAddOns.BackColor = SystemColors.ButtonFace; UpdateRadioButtonBackColor(); } private void radiobuttonOptions_CheckedChanged(object sender, EventArgs e) { panelOptions.Visible = true; panelStart.Visible = false; panelReview.Visible = false; panelControls.Visible = false; panelGetAddOns.Visible = false; panelPanels.BackColor = labelOptionsTitle.BackColor; pictureboxJoysticks.Visible = false; radiobuttonStart.BackColor = SystemColors.ButtonFace; radiobuttonReview.BackColor = SystemColors.ButtonFace; radiobuttonControls.BackColor = SystemColors.ButtonFace; radiobuttonOptions.BackColor = SystemColors.ButtonHighlight; radiobuttonGetAddOns.BackColor = SystemColors.ButtonFace; UpdateRadioButtonBackColor(); } private void RadiobuttonGetAddOnsCheckedChanged(object sender, EventArgs e) { panelGetAddOns.Visible = true; panelStart.Visible = false; panelReview.Visible = false; panelControls.Visible = false; panelOptions.Visible = false; panelPanels.BackColor = labelGetAddOnsTitle.BackColor; pictureboxJoysticks.Visible = false; radiobuttonStart.BackColor = SystemColors.ButtonFace; radiobuttonReview.BackColor = SystemColors.ButtonFace; radiobuttonControls.BackColor = SystemColors.ButtonFace; radiobuttonOptions.BackColor = SystemColors.ButtonFace; radiobuttonGetAddOns.BackColor = SystemColors.ButtonHighlight; UpdateRadioButtonBackColor(); if (radiobuttonGetAddOns.Checked) { EnterGetAddOns(); } } private void UpdateRadioButtonBackColor() { // work-around for button-style radio buttons on Mono if (Program.CurrentlyRunningOnMono) { radiobuttonStart.BackColor = panelPanels.BackColor; radiobuttonReview.BackColor = panelPanels.BackColor; radiobuttonControls.BackColor = panelPanels.BackColor; radiobuttonOptions.BackColor = panelPanels.BackColor; radiobuttonGetAddOns.BackColor = panelPanels.BackColor; } } // homepage private void linkHomepage_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { const string Url = "http://trainsimframework.org"; try { System.Diagnostics.Process.Start(Url); } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } } // updates private static bool CurrentlyCheckingForUpdates = false; private void linkUpdates_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { if (CurrentlyCheckingForUpdates) { return; } const string url = "http://trainsimframework.org/common/version.txt"; CurrentlyCheckingForUpdates = true; this.Cursor = Cursors.WaitCursor; Application.DoEvents(); try { byte[] bytes = Internet.DownloadBytesFromUrl(url); System.Text.Encoding Encoding = new System.Text.UTF8Encoding(); string Text = new string(Encoding.GetChars(bytes)); if (Text.Length != 0 && Text[0] == '\uFEFF') Text = Text.Substring(1); string[] Lines = Text.Split(new char[] { '\r', '\n' }); if (Lines.Length == 0 || !Lines[0].Equals("$OpenBveVersionInformation", StringComparison.OrdinalIgnoreCase)) { MessageBox.Show(Interface.GetInterfaceString("panel_updates_invalid"), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } else { string StableVersion = "0.0.0.0"; string StableDate = "0000-00-00"; string DevelopmentVersion = "0.0.0.0"; string DevelopmentDate = "0000-00-00"; int i; for (i = 1; i < Lines.Length; i++) { if (Lines[i].Equals("----")) break; int h = Lines[i].IndexOf('='); if (h >= 0) { string a = Lines[i].Substring(0, h).TrimEnd(); string b = Lines[i].Substring(h + 1).TrimStart(); if (a.Equals("version", StringComparison.OrdinalIgnoreCase)) { StableVersion = b; } else if (a.Equals("date", StringComparison.OrdinalIgnoreCase)) { StableDate = b; } else if (a.Equals("developmentversion", StringComparison.OrdinalIgnoreCase)) { DevelopmentVersion = b; } else if (a.Equals("developmentdate", StringComparison.OrdinalIgnoreCase)) { DevelopmentDate = b; } } } StringBuilder StableText = new StringBuilder(); StringBuilder DevelopmentText = new StringBuilder(); int j; for (j = i + 1; j < Lines.Length; j++) { if (Lines[j].Equals("----")) break; StableText.AppendLine(Lines[j]); } for (int k = j + 1; k < Lines.Length; k++) { if (Lines[k].Equals("----")) break; DevelopmentText.AppendLine(Lines[k]); } bool Found = false; if (IsNewVersionHigher(Application.ProductVersion, StableVersion)) { string Message = Interface.GetInterfaceString("panel_updates_new") + StableText.ToString().Trim(); Message = Message.Replace("[version]", StableVersion); Message = Message.Replace("[date]", StableDate); MessageBox.Show(Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); Found = true; } #pragma warning disable 0162 // Unreachable code if (Program.IsDevelopmentVersion) { if (IsNewVersionHigher(Application.ProductVersion, DevelopmentVersion)) { string Message = Interface.GetInterfaceString("panel_updates_new") + DevelopmentText.ToString().Trim(); Message = Message.Replace("[version]", DevelopmentVersion); Message = Message.Replace("[date]", DevelopmentDate); MessageBox.Show(Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); Found = true; } } #pragma warning restore 0162 // Unreachable code if (!Found) { string Message = Interface.GetInterfaceString("panel_updates_old"); MessageBox.Show(Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); } } } catch (Exception ex) { MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } this.Cursor = Cursors.Default; CurrentlyCheckingForUpdates = false; } private bool IsNewVersionHigher(string Current, string New) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string[] a = Current.Split('.'); string[] b = New.Split('.'); if (a.Length < b.Length) { Array.Resize(ref a, b.Length); } else if (a.Length > b.Length) { Array.Resize(ref b, a.Length); } for (int i = 0; i < a.Length; i++) { int x = a[i] != null ? int.Parse(a[i], Culture) : 0; int y = b[i] != null ? int.Parse(b[i], Culture) : 0; if (x < y) return true; if (x > y) return false; } return false; } // close private void buttonClose_Click(object sender, EventArgs e) { this.Close(); } // ====== // events // ====== // tick private void timerEvents_Tick(object sender, EventArgs e) { if (textboxJoystickGrab.Focused & this.Tag == null & listviewControls.SelectedIndices.Count == 1) { int j = listviewControls.SelectedIndices[0]; Sdl.SDL_JoystickUpdate(); for (int k = 0; k < Joysticks.AttachedJoysticks.Length; k++) { IntPtr handle = Joysticks.AttachedJoysticks[k].SdlHandle; int axes = Sdl.SDL_JoystickNumAxes(handle); for (int i = 0; i < axes; i++) { double a = (double)Sdl.SDL_JoystickGetAxis(handle, i) / 32768.0; if (a < -0.75) { Interface.CurrentControls[j].Device = k; Interface.CurrentControls[j].Component = Interface.JoystickComponent.Axis; Interface.CurrentControls[j].Element = i; Interface.CurrentControls[j].Direction = -1; radiobuttonJoystick.Focus(); UpdateJoystickDetails(); UpdateControlListElement(listviewControls.Items[j], j, true); return; } else if (a > 0.75) { Interface.CurrentControls[j].Device = k; Interface.CurrentControls[j].Component = Interface.JoystickComponent.Axis; Interface.CurrentControls[j].Element = i; Interface.CurrentControls[j].Direction = 1; radiobuttonJoystick.Focus(); UpdateJoystickDetails(); UpdateControlListElement(listviewControls.Items[j], j, true); return; } } int buttons = Sdl.SDL_JoystickNumButtons(handle); for (int i = 0; i < buttons; i++) { if (Sdl.SDL_JoystickGetButton(handle, i) == 1) { Interface.CurrentControls[j].Device = k; Interface.CurrentControls[j].Component = Interface.JoystickComponent.Button; Interface.CurrentControls[j].Element = i; Interface.CurrentControls[j].Direction = 1; radiobuttonJoystick.Focus(); UpdateJoystickDetails(); UpdateControlListElement(listviewControls.Items[j], j, true); return; } } int hats = Sdl.SDL_JoystickNumHats(handle); for (int i = 0; i < hats; i++) { int hat = Sdl.SDL_JoystickGetHat(handle, i); if (hat != 0) { Interface.CurrentControls[j].Device = k; Interface.CurrentControls[j].Component = Interface.JoystickComponent.Hat; Interface.CurrentControls[j].Element = i; Interface.CurrentControls[j].Direction = hat; radiobuttonJoystick.Focus(); UpdateJoystickDetails(); UpdateControlListElement(listviewControls.Items[j], j, true); return; } } } } Sdl.SDL_Event Event; while (Sdl.SDL_PollEvent(out Event) != 0) { } pictureboxJoysticks.Invalidate(); } // ========= // functions // ========= // load image private Image LoadImage(string Folder, string Title) { string File = OpenBveApi.Path.CombineFile(Folder, Title); if (System.IO.File.Exists(File)) { try { return Image.FromFile(File); } catch { } } return null; } // try load image private bool TryLoadImage(PictureBox Box, string Title) { string Folder = Program.FileSystem.GetDataFolder("Menu"); string File = OpenBveApi.Path.CombineFile(Folder, Title); if (System.IO.File.Exists(File)) { try { Box.Image = Image.FromFile(File); return true; } catch { Box.Image = Box.ErrorImage; return false; } } else { Box.Image = Box.ErrorImage; return false; } } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formMain.resources000066400000000000000000000016511171674032100232470ustar00rootroot00000000000000lSystem.Resources.ResourceReader, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceSetgSystem.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3aPADPADPP}n 3F [j50timerEvents.TrayLocation0timerFilter.TrayLocation2timerInstall.TrayLocation8@ QSystem.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3aSystem.Drawing.Pointxy @ QSystem.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3aSystem.Drawing.Pointxy @ QSystem.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3aSystem.Drawing.Pointxyw openbve-1.4.0.10/openBVE/OpenBve/OldCode/formMain.resx000066400000000000000000000144211171674032100222150ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 128, 17 234, 17 openbve-1.4.0.10/openBVE/OpenBve/OldCode/formPackage.Designer.cs000066400000000000000000000023731171674032100240520ustar00rootroot00000000000000 namespace OpenBve.OldCode { partial class formPackage { /// /// Designer variable used to keep track of non-visual components. /// private System.ComponentModel.IContainer components = null; /// /// Disposes resources used by the form. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } /// /// This method is required for Windows Forms designer support. /// Do not change the method contents inside the source code editor. The Forms designer might /// not be able to load this method if it was changed manually. /// private void InitializeComponent() { this.SuspendLayout(); // // formPackage // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(292, 273); this.Name = "formPackage"; this.Text = "Package details"; this.ResumeLayout(false); } } } openbve-1.4.0.10/openBVE/OpenBve/OldCode/formPackage.cs000066400000000000000000000003341171674032100223060ustar00rootroot00000000000000using System; using System.Drawing; using System.Windows.Forms; namespace OpenBve.OldCode { public partial class formPackage : Form { public formPackage() { InitializeComponent(); } } }openbve-1.4.0.10/openBVE/OpenBve/OldCode/formPackage.resx000066400000000000000000000132711171674032100226660ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 openbve-1.4.0.10/openBVE/OpenBve/OldParsers/000077500000000000000000000000001171674032100203055ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBve/OldParsers/AnimatedObjectParser.cs000066400000000000000000000746121171674032100246740ustar00rootroot00000000000000using System; using OpenBveApi.Math; namespace OpenBve { internal static class AnimatedObjectParser { // parse animated object config /// Loads a collection of animated objects from a file. /// The text file to load the animated object from. Must be an absolute file name. /// 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. /// The texture load mode. /// The collection of animated objects. 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++; Vector3 position = new Vector3(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 Vector3(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(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(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(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(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 Vector3(1.0, 0.0, 0.0); Result.Objects[ObjectCount].TranslateYDirection = new Vector3(0.0, 1.0, 0.0); Result.Objects[ObjectCount].TranslateZDirection = new Vector3(0.0, 0.0, 1.0); Result.Objects[ObjectCount].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Result.Objects[ObjectCount].RotateYDirection = new Vector3(0.0, 1.0, 0.0); Result.Objects[ObjectCount].RotateZDirection = new Vector3(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; Vector3 Position = new Vector3(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 Vector3(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 Vector3(x, y, z); break; case "translateydirection": Result.Objects[ObjectCount].TranslateYDirection = new Vector3(x, y, z); break; case "translatezdirection": Result.Objects[ObjectCount].TranslateZDirection = new Vector3(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 Vector3(x, y, z); break; case "rotateydirection": Result.Objects[ObjectCount].RotateYDirection = new Vector3(x, y, z); break; case "rotatezdirection": Result.Objects[ObjectCount].RotateZDirection = new Vector3(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 Vector3(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(ref Result.Objects, ObjectCount); return Result; } } }openbve-1.4.0.10/openBVE/OpenBve/OldParsers/CsvB3dObjectParser.cs000066400000000000000000001646501171674032100242400ustar00rootroot00000000000000using System; using OpenBveApi.Colors; using OpenBveApi.Math; namespace OpenBve { internal static class CsvB3dObjectParser { // structures private class Material { internal Color32 Color; internal Color24 EmissiveColor; internal bool EmissiveColorUsed; internal Color24 TransparentColor; internal bool TransparentColorUsed; internal string DaytimeTexture; internal string NighttimeTexture; internal World.MeshMaterialBlendMode BlendMode; internal ushort GlowAttenuationData; internal Material() { this.Color = new Color32(255, 255, 255, 255); this.EmissiveColor = new Color24(0, 0, 0); this.EmissiveColorUsed = false; this.TransparentColor = new Color24(0, 0, 0); this.TransparentColorUsed = false; this.DaytimeTexture = null; this.NighttimeTexture = null; this.BlendMode = World.MeshMaterialBlendMode.Normal; this.GlowAttenuationData = 0; } internal Material(Material Prototype) { this.Color = Prototype.Color; this.EmissiveColor = Prototype.EmissiveColor; this.EmissiveColorUsed = Prototype.EmissiveColorUsed; this.TransparentColor = Prototype.TransparentColor; this.TransparentColorUsed = Prototype.TransparentColorUsed; this.DaytimeTexture = Prototype.DaytimeTexture; this.NighttimeTexture = Prototype.NighttimeTexture; this.BlendMode = Prototype.BlendMode; this.GlowAttenuationData = Prototype.GlowAttenuationData; } } private class MeshBuilder { internal World.Vertex[] Vertices; internal World.MeshFace[] Faces; internal Material[] Materials; internal MeshBuilder() { this.Vertices = new World.Vertex[] { }; this.Faces = new World.MeshFace[] { }; this.Materials = new Material[] { new Material() }; } } // read object /// Loads a CSV or B3D object from a file. /// The text file to load the animated object from. Must be an absolute file name. /// 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. /// The texture load mode. /// Whether to force TextureWrapMode.Repeat for referenced textures. /// The object loaded. internal static ObjectManager.StaticObject ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; bool IsB3D = string.Equals(System.IO.Path.GetExtension(FileName), ".b3d", StringComparison.OrdinalIgnoreCase); // initialize object ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); Object.Mesh.Faces = new World.MeshFace[] { }; Object.Mesh.Materials = new World.MeshMaterial[] { }; Object.Mesh.Vertices = new World.Vertex[] { }; // read lines string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); // parse lines MeshBuilder Builder = new MeshBuilder(); World.Vector3Df[] Normals = new World.Vector3Df[4]; for (int i = 0; i < Lines.Length; i++) { { // strip away comments int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j); } } // collect arguments string[] Arguments = Lines[i].Split(new char[] { ',' }, StringSplitOptions.None); for (int j = 0; j < Arguments.Length; j++) { Arguments[j] = Arguments[j].Trim(); } { // remove unused arguments at the end of the chain int j; for (j = Arguments.Length - 1; j >= 0; j--) { if (Arguments[j].Length != 0) break; } Array.Resize(ref Arguments, j + 1); } // style string Command; if (IsB3D & Arguments.Length != 0) { // b3d int j = Arguments[0].IndexOf(' '); if (j >= 0) { Command = Arguments[0].Substring(0, j).TrimEnd(); Arguments[0] = Arguments[0].Substring(j + 1).TrimStart(); } else { Command = Arguments[0]; if (Arguments.Length != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid syntax at line " + (i + 1).ToString(Culture) + " in file " + FileName); } Arguments = new string[] { }; } } else if (Arguments.Length != 0) { // csv Command = Arguments[0]; for (int j = 0; j < Arguments.Length - 1; j++) { Arguments[j] = Arguments[j + 1]; } Array.Resize(ref Arguments, Arguments.Length - 1); } else { // empty Command = null; } // parse terms if (Command != null) { string cmd = Command.ToLowerInvariant(); switch(cmd) { case "createmeshbuilder": case "[meshbuilder]": { if (cmd == "createmeshbuilder" & IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "CreateMeshBuilder is not a supported command - did you mean [MeshBuilder]? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "[meshbuilder]" & !IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "[MeshBuilder] is not a supported command - did you mean CreateMeshBuilder? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 0) { Interface.AddMessage(Interface.MessageType.Warning, false, "0 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } ApplyMeshBuilder(ref Object, Builder, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); Builder = new MeshBuilder(); Normals = new World.Vector3Df[4]; } break; case "addvertex": case "vertex": { if (cmd == "addvertex" & IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "AddVertex is not a supported command - did you mean Vertex? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "vertex" & !IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "Vertex is not a supported command - did you mean AddVertex? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 6) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 6 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double vx = 0.0, vy = 0.0, vz = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out vx)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument vX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); vx = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out vy)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument vY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); vy = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], out vz)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument vZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); vz = 0.0; } double nx = 0.0, ny = 0.0, nz = 0.0; if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[3], out nx)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument nX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); nx = 0.0; } if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[4], out ny)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument nY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); ny = 0.0; } if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[5], out nz)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument nZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); nz = 0.0; } World.Normalize(ref nx, ref ny, ref nz); Array.Resize(ref Builder.Vertices, Builder.Vertices.Length + 1); while (Builder.Vertices.Length >= Normals.Length) { Array.Resize(ref Normals, Normals.Length << 1); } Builder.Vertices[Builder.Vertices.Length - 1].Coordinates = new Vector3(vx, vy, vz); Normals[Builder.Vertices.Length - 1] = new World.Vector3Df((float)nx, (float)ny, (float)nz); } break; case "addface": case "addface2": case "face": case "face2": { if (IsB3D) { if (cmd == "addface") { Interface.AddMessage(Interface.MessageType.Warning, false, "AddFace is not a supported command - did you mean Face? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "addface2") { Interface.AddMessage(Interface.MessageType.Warning, false, "AddFace2 is not a supported command - did you mean Face2? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { if (cmd == "face") { Interface.AddMessage(Interface.MessageType.Warning, false, "Face is not a supported command - did you mean AddFace? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "face2") { Interface.AddMessage(Interface.MessageType.Warning, false, "Face2 is not a supported command - did you mean AddFace2? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } if (Arguments.Length < 3) { Interface.AddMessage(Interface.MessageType.Error, false, "At least 3 arguments are required in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { bool q = true; int[] a = new int[Arguments.Length]; for (int j = 0; j < Arguments.Length; j++) { if (!Interface.TryParseIntVb6(Arguments[j], out a[j])) { Interface.AddMessage(Interface.MessageType.Error, false, "v" + j.ToString(Culture) + " is invalid in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); q = false; break; } else if (a[j] < 0 | a[j] >= Builder.Vertices.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "v" + j.ToString(Culture) + " references a non-existing vertex in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); q = false; break; } else if (a[j] > 65535) { Interface.AddMessage(Interface.MessageType.Error, false, "v" + j.ToString(Culture) + " indexes a vertex above 65535 which is not currently supported in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); q = false; break; } } if (q) { int f = Builder.Faces.Length; Array.Resize(ref Builder.Faces, f + 1); Builder.Faces[f] = new World.MeshFace(); Builder.Faces[f].Vertices = new World.MeshFaceVertex[Arguments.Length]; while (Builder.Vertices.Length > Normals.Length) { Array.Resize(ref Normals, Normals.Length << 1); } for (int j = 0; j < Arguments.Length; j++) { Builder.Faces[f].Vertices[j].Index = (ushort)a[j]; Builder.Faces[f].Vertices[j].Normal = Normals[a[j]]; } if (cmd == "addface2" | cmd == "face2") { Builder.Faces[f].Flags = (byte)World.MeshFace.Face2Mask; } } } } break; case "cube": { if (Arguments.Length > 3) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double x = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument HalfWidth in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 1.0; } double y = x, z = x; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument HalfHeight in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 1.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument HalfDepth in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); z = 1.0; } CreateCube(ref Builder, x, y, z); } break; case "cylinder": { if (Arguments.Length > 4) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 4 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } int n = 8; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out n)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument n in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); n = 8; } if (n < 2) { Interface.AddMessage(Interface.MessageType.Error, false, "n is expected to be at least 2 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); n = 8; } double r1 = 0.0, r2 = 0.0, h = 1.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out r1)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument UpperRadius in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r1 = 1.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], out r2)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument LowerRadius in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r2 = 1.0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[3], out h)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Height in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); h = 1.0; } CreateCylinder(ref Builder, n, r1, r2, h); } break; case "translate": case "translateall": { if (Arguments.Length > 3) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double x = 0.0, y = 0.0, z = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Z in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); z = 0.0; } ApplyTranslation(Builder, x, y, z); if (cmd == "translateall") { ApplyTranslation(Object, x, y, z); } } break; case "scale": case "scaleall": { if (Arguments.Length > 3) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double x = 1.0, y = 1.0, z = 1.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 1.0; } else if (x == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "X is required to be different from zero in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 1.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 1.0; } else if (y == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is required to be different from zero in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 1.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Z in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); z = 1.0; } else if (z == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is required to be different from zero in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); z = 1.0; } ApplyScale(Builder, x, y, z); if (cmd == "scaleall") { ApplyScale(Object, x, y, z); } } break; case "rotate": case "rotateall": { if (Arguments.Length > 4) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 4 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double x = 0.0, y = 0.0, z = 0.0, a = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Z in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); z = 0.0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[3], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Angle in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); a = 0.0; } double t = x * x + y * y + z * z; if (t == 0.0) { x = 1.0; y = 0.0; z = 0.0; t = 1.0; } if (a != 0.0) { t = 1.0 / Math.Sqrt(t); x *= t; y *= t; z *= t; a *= 0.0174532925199433; ApplyRotation(Builder, x, y, z, a); if (cmd == "rotateall") { ApplyRotation(Object, x, y, z, a); } } } break; case "shear": case "shearall": { if (Arguments.Length > 7) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 7 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } double dx = 0.0, dy = 0.0, dz = 0.0; double sx = 0.0, sy = 0.0, sz = 0.0; double r = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out dx)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument dX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); dx = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out dy)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument dY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); dy = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], out dz)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument dZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); dz = 0.0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[3], out sx)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument sX in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); sx = 0.0; } if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[4], out sy)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument sY in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); sy = 0.0; } if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[5], out sz)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument sZ in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); sz = 0.0; } if (Arguments.Length >= 7 && Arguments[6].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[6], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Ratio in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = 0.0; } World.Normalize(ref dx, ref dy, ref dz); World.Normalize(ref sx, ref sy, ref sz); ApplyShear(Builder, dx, dy, dz, sx, sy, sz, r); if (cmd == "shearall") { ApplyShear(Object, dx, dy, dz, sx, sy, sz, r); } } break; case "generatenormals": case "[texture]": if (cmd == "generatenormals" & IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "GenerateNormals is not a supported command - did you mean [Texture]? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "[texture]" & !IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "[Texture] is not a supported command - did you mean GenerateNormals? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "setcolor": case "color": { if (cmd == "setcolor" & IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "SetColor is not a supported command - did you mean Color? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "color" & !IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "Color is not a supported command - did you mean SetColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 4) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 4 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } int r = 0, g = 0, b = 0, a = 255; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Red in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = 0; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "Red is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Green in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = 0; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "Green is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Blue in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = 0; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "Blue is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = b < 0 ? 0 : 255; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseIntVb6(Arguments[3], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Alpha in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); a = 255; } else if (a < 0 | a > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "Alpha is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); a = a < 0 ? 0 : 255; } int m = Builder.Materials.Length; Array.Resize(ref Builder.Materials, m << 1); for (int j = m; j < Builder.Materials.Length; j++) { Builder.Materials[j] = new Material(Builder.Materials[j - m]); Builder.Materials[j].Color = new Color32((byte)r, (byte)g, (byte)b, (byte)a); Builder.Materials[j].BlendMode = Builder.Materials[0].BlendMode; Builder.Materials[j].GlowAttenuationData = Builder.Materials[0].GlowAttenuationData; Builder.Materials[j].DaytimeTexture = Builder.Materials[0].DaytimeTexture; Builder.Materials[j].NighttimeTexture = Builder.Materials[0].NighttimeTexture; Builder.Materials[j].TransparentColor = Builder.Materials[0].TransparentColor; Builder.Materials[j].TransparentColorUsed = Builder.Materials[0].TransparentColorUsed; } for (int j = 0; j < Builder.Faces.Length; j++) { Builder.Faces[j].Material += (ushort)m; } } break; case "setemissivecolor": case "emissivecolor": { if (cmd == "setemissivecolor" & IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "SetEmissiveColor is not a supported command - did you mean EmissiveColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "emissivecolor" & !IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "EmissiveColor is not a supported command - did you mean SetEmissiveColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 3) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } int r = 0, g = 0, b = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Red in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = 0; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "Red is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Green in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = 0; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "Green is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Blue in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = 0; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "Blue is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = b < 0 ? 0 : 255; } int m = Builder.Materials.Length; Array.Resize(ref Builder.Materials, m << 1); for (int j = m; j < Builder.Materials.Length; j++) { Builder.Materials[j] = new Material(Builder.Materials[j - m]); Builder.Materials[j].EmissiveColor = new Color24((byte)r, (byte)g, (byte)b); Builder.Materials[j].EmissiveColorUsed = true; Builder.Materials[j].BlendMode = Builder.Materials[0].BlendMode; Builder.Materials[j].GlowAttenuationData = Builder.Materials[0].GlowAttenuationData; Builder.Materials[j].DaytimeTexture = Builder.Materials[0].DaytimeTexture; Builder.Materials[j].NighttimeTexture = Builder.Materials[0].NighttimeTexture; Builder.Materials[j].TransparentColor = Builder.Materials[0].TransparentColor; Builder.Materials[j].TransparentColorUsed = Builder.Materials[0].TransparentColorUsed; } for (int j = 0; j < Builder.Faces.Length; j++) { Builder.Faces[j].Material += (ushort)m; } } break; case "setdecaltransparentcolor": case "transparent": { if (cmd == "setdecaltransparentcolor" & IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "SetDecalTransparentColor is not a supported command - did you mean Transparent? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "transparent" & !IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "Transparent is not a supported command - did you mean SetDecalTransparentColor? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 3) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } int r = 0, g = 0, b = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Red in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = 0; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "Red is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Green in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = 0; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "Green is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Blue in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = 0; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "Blue is required to be within the range from 0 to 255 in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); b = b < 0 ? 0 : 255; } for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].TransparentColor = new Color24((byte)r, (byte)g, (byte)b); Builder.Materials[j].TransparentColorUsed = true; } } break; case "setblendmode": case "blendmode": { if (cmd == "setblendmode" & IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "SetBlendMode is not a supported command - did you mean BlendMode? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "blendmode" & !IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "BlendMode is not a supported command - did you mean SetBlendMode? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 3) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } World.MeshMaterialBlendMode blendmode = World.MeshMaterialBlendMode.Normal; if (Arguments.Length >= 1 && Arguments[0].Length > 0) { switch (Arguments[0].ToLowerInvariant()) { case "normal": blendmode = World.MeshMaterialBlendMode.Normal; break; case "additive": blendmode = World.MeshMaterialBlendMode.Additive; break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The given BlendMode is not supported in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); blendmode = World.MeshMaterialBlendMode.Normal; break; } } double glowhalfdistance = 0.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out glowhalfdistance)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument GlowHalfDistance in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); glowhalfdistance = 0; } World.GlowAttenuationMode glowmode = World.GlowAttenuationMode.DivisionExponent4; if (Arguments.Length >= 3 && Arguments[2].Length > 0) { switch (Arguments[2].ToLowerInvariant()) { case "divideexponent2": glowmode = World.GlowAttenuationMode.DivisionExponent2; break; case "divideexponent4": glowmode = World.GlowAttenuationMode.DivisionExponent4; break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The given GlowAttenuationMode is not supported in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].BlendMode = blendmode; Builder.Materials[j].GlowAttenuationData = World.GetGlowAttenuationData(glowhalfdistance, glowmode); } } break; case "loadtexture": case "load": { if (cmd == "loadtexture" & IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "LoadTexture is not a supported command - did you mean Load? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "load" & !IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "Load is not a supported command - did you mean LoadTexture? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 2) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 2 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } string tday = null, tnight = null; if (Arguments.Length >= 1 && Arguments[0].Length != 0) { if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeTexture contains illegal characters in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { tday = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[0]); if (!System.IO.File.Exists(tday)) { Interface.AddMessage(Interface.MessageType.Error, true, "DaytimeTexture " + tday + " could not be found in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); tday = null; } } } if (Arguments.Length >= 2 && Arguments[1].Length != 0) { if (Arguments[0].Length == 0) { Interface.AddMessage(Interface.MessageType.Error, true, "DaytimeTexture is required to be specified in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (Interface.ContainsInvalidPathChars(Arguments[1])) { Interface.AddMessage(Interface.MessageType.Error, false, "NighttimeTexture contains illegal characters in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { tnight = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[1]); if (!System.IO.File.Exists(tnight)) { Interface.AddMessage(Interface.MessageType.Error, true, "The NighttimeTexture " + tnight + " could not be found in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); tnight = null; } } } } for (int j = 0; j < Builder.Materials.Length; j++) { Builder.Materials[j].DaytimeTexture = tday; Builder.Materials[j].NighttimeTexture = tnight; } } break; case "settexturecoordinates": case "coordinates": { if (cmd == "settexturecoordinates" & IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "SetTextureCoordinates is not a supported command - did you mean Coordinates? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (cmd == "coordinates" & !IsB3D) { Interface.AddMessage(Interface.MessageType.Warning, false, "Coordinates is not a supported command - did you mean SetTextureCoordinates? - at line " + (i + 1).ToString(Culture) + " in file " + FileName); } if (Arguments.Length > 3) { Interface.AddMessage(Interface.MessageType.Warning, false, "At most 3 arguments are expected in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } int j = 0; float x = 0.0f, y = 0.0f; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out j)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument VertexIndex in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); j = 0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseFloatVb6(Arguments[1], out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument X in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); x = 0.0f; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseFloatVb6(Arguments[2], out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid argument Y in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); y = 0.0f; } if (j >= 0 & j < Builder.Vertices.Length) { Builder.Vertices[j].TextureCoordinates = new World.Vector2Df(x, y); } else { Interface.AddMessage(Interface.MessageType.Error, false, "VertexIndex references a non-existing vertex in " + Command + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: if (Command.Length != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "The command " + Command + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; } } } // finalize object ApplyMeshBuilder(ref Object, Builder, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); World.CreateNormals(ref Object.Mesh); return Object; } // create cube private static void CreateCube(ref MeshBuilder Builder, double sx, double sy, double sz) { int v = Builder.Vertices.Length; Array.Resize(ref Builder.Vertices, v + 8); Builder.Vertices[v + 0].Coordinates = new Vector3(sx, sy, -sz); Builder.Vertices[v + 1].Coordinates = new Vector3(sx, -sy, -sz); Builder.Vertices[v + 2].Coordinates = new Vector3(-sx, -sy, -sz); Builder.Vertices[v + 3].Coordinates = new Vector3(-sx, sy, -sz); Builder.Vertices[v + 4].Coordinates = new Vector3(sx, sy, sz); Builder.Vertices[v + 5].Coordinates = new Vector3(sx, -sy, sz); Builder.Vertices[v + 6].Coordinates = new Vector3(-sx, -sy, sz); Builder.Vertices[v + 7].Coordinates = new Vector3(-sx, sy, sz); int f = Builder.Faces.Length; Array.Resize(ref Builder.Faces, f + 6); Builder.Faces[f + 0].Vertices = new World.MeshFaceVertex[] { new World.MeshFaceVertex(v + 0), new World.MeshFaceVertex(v + 1), new World.MeshFaceVertex(v + 2), new World.MeshFaceVertex(v + 3) }; Builder.Faces[f + 1].Vertices = new World.MeshFaceVertex[] { new World.MeshFaceVertex(v + 0), new World.MeshFaceVertex(v + 4), new World.MeshFaceVertex(v + 5), new World.MeshFaceVertex(v + 1) }; Builder.Faces[f + 2].Vertices = new World.MeshFaceVertex[] { new World.MeshFaceVertex(v + 0), new World.MeshFaceVertex(v + 3), new World.MeshFaceVertex(v + 7), new World.MeshFaceVertex(v + 4) }; Builder.Faces[f + 3].Vertices = new World.MeshFaceVertex[] { new World.MeshFaceVertex(v + 6), new World.MeshFaceVertex(v + 5), new World.MeshFaceVertex(v + 4), new World.MeshFaceVertex(v + 7) }; Builder.Faces[f + 4].Vertices = new World.MeshFaceVertex[] { new World.MeshFaceVertex(v + 6), new World.MeshFaceVertex(v + 7), new World.MeshFaceVertex(v + 3), new World.MeshFaceVertex(v + 2) }; Builder.Faces[f + 5].Vertices = new World.MeshFaceVertex[] { new World.MeshFaceVertex(v + 6), new World.MeshFaceVertex(v + 2), new World.MeshFaceVertex(v + 1), new World.MeshFaceVertex(v + 5) }; } // create cylinder private static void CreateCylinder(ref MeshBuilder Builder, int n, double r1, double r2, double h) { // parameters bool uppercap = r1 > 0.0; bool lowercap = r2 > 0.0; int m = (uppercap ? 1 : 0) + (lowercap ? 1 : 0); r1 = Math.Abs(r1); r2 = Math.Abs(r2); double ns = h >= 0.0 ? 1.0 : -1.0; // initialization int v = Builder.Vertices.Length; Array.Resize(ref Builder.Vertices, v + 2 * n); World.Vector3Df[] Normals = new World.Vector3Df[2 * n]; double d = 2.0 * Math.PI / (double)n; double g = 0.5 * h; double t = 0.0; double a = h != 0.0 ? Math.Atan((r2 - r1) / h) : 0.0; double cosa = Math.Cos(a); double sina = Math.Sin(a); // vertices and normals for (int i = 0; i < n; i++) { double dx = Math.Cos(t); double dz = Math.Sin(t); double lx = dx * r2; double lz = dz * r2; double ux = dx * r1; double uz = dz * r1; Builder.Vertices[v + 2 * i + 0].Coordinates = new Vector3(ux, g, uz); Builder.Vertices[v + 2 * i + 1].Coordinates = new Vector3(lx, -g, lz); double nx = dx * ns, ny = 0.0, nz = dz * ns; double sx, sy, sz; World.Cross(nx, ny, nz, 0.0, 1.0, 0.0, out sx, out sy, out sz); World.Rotate(ref nx, ref ny, ref nz, sx, sy, sz, cosa, sina); Normals[2 * i + 0] = new World.Vector3Df((float)nx, (float)ny, (float)nz); Normals[2 * i + 1] = new World.Vector3Df((float)nx, (float)ny, (float)nz); t += d; } // faces int f = Builder.Faces.Length; Array.Resize(ref Builder.Faces, f + n + m); for (int i = 0; i < n; i++) { Builder.Faces[f + i].Flags = 0; int i0 = (2 * i + 2) % (2 * n); int i1 = (2 * i + 3) % (2 * n); int i2 = 2 * i + 1; int i3 = 2 * i; Builder.Faces[f + i].Vertices = new World.MeshFaceVertex[] { new World.MeshFaceVertex(v + i0, Normals[i0]), new World.MeshFaceVertex(v + i1, Normals[i1]), new World.MeshFaceVertex(v + i2, Normals[i2]), new World.MeshFaceVertex(v + i3, Normals[i3]) }; } for (int i = 0; i < m; i++) { Builder.Faces[f + n + i].Vertices = new World.MeshFaceVertex[n]; for (int j = 0; j < n; j++) { if (i == 0 & lowercap) { // lower cap Builder.Faces[f + n + i].Vertices[j] = new World.MeshFaceVertex(v + 2 * (n - j - 1)); } else { // upper cap Builder.Faces[f + n + i].Vertices[j] = new World.MeshFaceVertex(v + 2 * j + 1); } } } } // apply translation private static void ApplyTranslation(MeshBuilder Builder, double x, double y, double z) { for (int i = 0; i < Builder.Vertices.Length; i++) { Builder.Vertices[i].Coordinates.X += x; Builder.Vertices[i].Coordinates.Y += y; Builder.Vertices[i].Coordinates.Z += z; } } private static void ApplyTranslation(ObjectManager.StaticObject Object, double x, double y, double z) { for (int i = 0; i < Object.Mesh.Vertices.Length; i++) { Object.Mesh.Vertices[i].Coordinates.X += x; Object.Mesh.Vertices[i].Coordinates.Y += y; Object.Mesh.Vertices[i].Coordinates.Z += z; } } // apply scale private static void ApplyScale(MeshBuilder Builder, double x, double y, double z) { float rx = (float)(1.0 / x); float ry = (float)(1.0 / y); float rz = (float)(1.0 / z); float rx2 = rx * rx; float ry2 = ry * ry; float rz2 = rz * rz; for (int i = 0; i < Builder.Vertices.Length; i++) { Builder.Vertices[i].Coordinates.X *= x; Builder.Vertices[i].Coordinates.Y *= y; Builder.Vertices[i].Coordinates.Z *= z; } for (int i = 0; i < Builder.Faces.Length; i++) { for (int j = 0; j < Builder.Faces[i].Vertices.Length; j++) { float nx2 = Builder.Faces[i].Vertices[j].Normal.X * Builder.Faces[i].Vertices[j].Normal.X; float ny2 = Builder.Faces[i].Vertices[j].Normal.Y * Builder.Faces[i].Vertices[j].Normal.Y; float nz2 = Builder.Faces[i].Vertices[j].Normal.Z * Builder.Faces[i].Vertices[j].Normal.Z; float u = nx2 * rx2 + ny2 * ry2 + nz2 * rz2; if (u != 0.0) { u = (float)Math.Sqrt((double)((nx2 + ny2 + nz2) / u)); Builder.Faces[i].Vertices[j].Normal.X *= rx * u; Builder.Faces[i].Vertices[j].Normal.Y *= ry * u; Builder.Faces[i].Vertices[j].Normal.Z *= rz * u; } } } if (x * y * z < 0.0) { for (int i = 0; i < Builder.Faces.Length; i++) { Builder.Faces[i].Flip(); } } } internal static void ApplyScale(ObjectManager.StaticObject Object, double x, double y, double z) { float rx = (float)(1.0 / x); float ry = (float)(1.0 / y); float rz = (float)(1.0 / z); float rx2 = rx * rx; float ry2 = ry * ry; float rz2 = rz * rz; bool reverse = x * y * z < 0.0; for (int j = 0; j < Object.Mesh.Vertices.Length; j++) { Object.Mesh.Vertices[j].Coordinates.X *= x; Object.Mesh.Vertices[j].Coordinates.Y *= y; Object.Mesh.Vertices[j].Coordinates.Z *= z; } for (int j = 0; j < Object.Mesh.Faces.Length; j++) { for (int k = 0; k < Object.Mesh.Faces[j].Vertices.Length; k++) { float nx2 = Object.Mesh.Faces[j].Vertices[k].Normal.X * Object.Mesh.Faces[j].Vertices[k].Normal.X; float ny2 = Object.Mesh.Faces[j].Vertices[k].Normal.Y * Object.Mesh.Faces[j].Vertices[k].Normal.Y; float nz2 = Object.Mesh.Faces[j].Vertices[k].Normal.Z * Object.Mesh.Faces[j].Vertices[k].Normal.Z; float u = nx2 * rx2 + ny2 * ry2 + nz2 * rz2; if (u != 0.0) { u = (float)Math.Sqrt((double)((nx2 + ny2 + nz2) / u)); Object.Mesh.Faces[j].Vertices[k].Normal.X *= rx * u; Object.Mesh.Faces[j].Vertices[k].Normal.Y *= ry * u; Object.Mesh.Faces[j].Vertices[k].Normal.Z *= rz * u; } } } if (reverse) { for (int j = 0; j < Object.Mesh.Faces.Length; j++) { Object.Mesh.Faces[j].Flip(); } } } // apply rotation private static void ApplyRotation(MeshBuilder Builder, double x, double y, double z, double a) { double cosa = Math.Cos(a); double sina = Math.Sin(a); for (int i = 0; i < Builder.Vertices.Length; i++) { World.Rotate(ref Builder.Vertices[i].Coordinates.X, ref Builder.Vertices[i].Coordinates.Y, ref Builder.Vertices[i].Coordinates.Z, x, y, z, cosa, sina); } for (int i = 0; i < Builder.Faces.Length; i++) { for (int j = 0; j < Builder.Faces[i].Vertices.Length; j++) { World.Rotate(ref Builder.Faces[i].Vertices[j].Normal.X, ref Builder.Faces[i].Vertices[j].Normal.Y, ref Builder.Faces[i].Vertices[j].Normal.Z, x, y, z, cosa, sina); } } } private static void ApplyRotation(ObjectManager.StaticObject Object, double x, double y, double z, double a) { double cosa = Math.Cos(a); double sina = Math.Sin(a); for (int j = 0; j < Object.Mesh.Vertices.Length; j++) { World.Rotate(ref Object.Mesh.Vertices[j].Coordinates.X, ref Object.Mesh.Vertices[j].Coordinates.Y, ref Object.Mesh.Vertices[j].Coordinates.Z, x, y, z, cosa, sina); } for (int j = 0; j < Object.Mesh.Faces.Length; j++) { for (int k = 0; k < Object.Mesh.Faces[j].Vertices.Length; k++) { World.Rotate(ref Object.Mesh.Faces[j].Vertices[k].Normal.X, ref Object.Mesh.Faces[j].Vertices[k].Normal.Y, ref Object.Mesh.Faces[j].Vertices[k].Normal.Z, x, y, z, cosa, sina); } } } // apply shear private static void ApplyShear(MeshBuilder Builder, double dx, double dy, double dz, double sx, double sy, double sz, double r) { for (int j = 0; j < Builder.Vertices.Length; j++) { double n = r * (dx * Builder.Vertices[j].Coordinates.X + dy * Builder.Vertices[j].Coordinates.Y + dz * Builder.Vertices[j].Coordinates.Z); Builder.Vertices[j].Coordinates.X += sx * n; Builder.Vertices[j].Coordinates.Y += sy * n; Builder.Vertices[j].Coordinates.Z += sz * n; } for (int j = 0; j < Builder.Faces.Length; j++) { for (int k = 0; k < Builder.Faces[j].Vertices.Length; k++) { if (Builder.Faces[j].Vertices[k].Normal.X != 0.0f | Builder.Faces[j].Vertices[k].Normal.Y != 0.0f | Builder.Faces[j].Vertices[k].Normal.Z != 0.0f) { double nx = (double)Builder.Faces[j].Vertices[k].Normal.X; double ny = (double)Builder.Faces[j].Vertices[k].Normal.Y; double nz = (double)Builder.Faces[j].Vertices[k].Normal.Z; double n = r * (sx * nx + sy * ny + sz * nz); nx -= dx * n; ny -= dy * n; nz -= dz * n; World.Normalize(ref nx, ref ny, ref nz); Builder.Faces[j].Vertices[k].Normal.X = (float)nx; Builder.Faces[j].Vertices[k].Normal.Y = (float)ny; Builder.Faces[j].Vertices[k].Normal.Z = (float)nz; } } } } private static void ApplyShear(ObjectManager.StaticObject Object, double dx, double dy, double dz, double sx, double sy, double sz, double r) { for (int j = 0; j < Object.Mesh.Vertices.Length; j++) { double n = r * (dx * Object.Mesh.Vertices[j].Coordinates.X + dy * Object.Mesh.Vertices[j].Coordinates.Y + dz * Object.Mesh.Vertices[j].Coordinates.Z); Object.Mesh.Vertices[j].Coordinates.X += sx * n; Object.Mesh.Vertices[j].Coordinates.Y += sy * n; Object.Mesh.Vertices[j].Coordinates.Z += sz * n; } double ux, uy, uz; World.Cross(sx, sy, sz, dx, dy, dz, out ux, out uy, out uz); for (int j = 0; j < Object.Mesh.Faces.Length; j++) { for (int k = 0; k < Object.Mesh.Faces[j].Vertices.Length; k++) { if (Object.Mesh.Faces[j].Vertices[k].Normal.X != 0.0f | Object.Mesh.Faces[j].Vertices[k].Normal.Y != 0.0f | Object.Mesh.Faces[j].Vertices[k].Normal.Z != 0.0f) { double nx = (double)Object.Mesh.Faces[j].Vertices[k].Normal.X; double ny = (double)Object.Mesh.Faces[j].Vertices[k].Normal.Y; double nz = (double)Object.Mesh.Faces[j].Vertices[k].Normal.Z; double n = r * (sx * nx + sy * ny + sz * nz); nx -= dx * n; ny -= dy * n; nz -= dz * n; World.Normalize(ref nx, ref ny, ref nz); Object.Mesh.Faces[j].Vertices[k].Normal.X = (float)nx; Object.Mesh.Faces[j].Vertices[k].Normal.Y = (float)ny; Object.Mesh.Faces[j].Vertices[k].Normal.Z = (float)nz; } } } } // apply mesh builder private static void ApplyMeshBuilder(ref ObjectManager.StaticObject Object, MeshBuilder Builder, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { if (Builder.Faces.Length != 0) { int mf = Object.Mesh.Faces.Length; int mm = Object.Mesh.Materials.Length; int mv = Object.Mesh.Vertices.Length; Array.Resize(ref Object.Mesh.Faces, mf + Builder.Faces.Length); Array.Resize(ref Object.Mesh.Materials, mm + Builder.Materials.Length); Array.Resize(ref Object.Mesh.Vertices, mv + Builder.Vertices.Length); for (int i = 0; i < Builder.Vertices.Length; i++) { Object.Mesh.Vertices[mv + i] = Builder.Vertices[i]; } for (int i = 0; i < Builder.Faces.Length; i++) { Object.Mesh.Faces[mf + i] = Builder.Faces[i]; for (int j = 0; j < Object.Mesh.Faces[mf + i].Vertices.Length; j++) { Object.Mesh.Faces[mf + i].Vertices[j].Index += (ushort)mv; } Object.Mesh.Faces[mf + i].Material += (ushort)mm; } for (int i = 0; i < Builder.Materials.Length; i++) { Object.Mesh.Materials[mm + i].Flags = (byte)((Builder.Materials[i].EmissiveColorUsed ? World.MeshMaterial.EmissiveColorMask : 0) | (Builder.Materials[i].TransparentColorUsed ? World.MeshMaterial.TransparentColorMask : 0)); Object.Mesh.Materials[mm + i].Color = Builder.Materials[i].Color; Object.Mesh.Materials[mm + i].TransparentColor = Builder.Materials[i].TransparentColor; if (Builder.Materials[i].DaytimeTexture != null) { Textures.Texture tday; if (Builder.Materials[i].TransparentColorUsed) { Textures.RegisterTexture(Builder.Materials[i].DaytimeTexture, new OpenBveApi.Textures.TextureParameters(null, new Color24(Builder.Materials[i].TransparentColor.R, Builder.Materials[i].TransparentColor.G, Builder.Materials[i].TransparentColor.B)), out tday); } else { Textures.RegisterTexture(Builder.Materials[i].DaytimeTexture, out tday); } Object.Mesh.Materials[mm + i].DaytimeTexture = tday; } else { Object.Mesh.Materials[mm + i].DaytimeTexture = null; } Object.Mesh.Materials[mm + i].EmissiveColor = Builder.Materials[i].EmissiveColor; if (Builder.Materials[i].NighttimeTexture != null) { Textures.Texture tnight; if (Builder.Materials[i].TransparentColorUsed) { Textures.RegisterTexture(Builder.Materials[i].NighttimeTexture, new OpenBveApi.Textures.TextureParameters(null, new Color24(Builder.Materials[i].TransparentColor.R, Builder.Materials[i].TransparentColor.G, Builder.Materials[i].TransparentColor.B)), out tnight); } else { Textures.RegisterTexture(Builder.Materials[i].NighttimeTexture, out tnight); } Object.Mesh.Materials[mm + i].NighttimeTexture = tnight; } else { Object.Mesh.Materials[mm + i].NighttimeTexture = null; } Object.Mesh.Materials[mm + i].DaytimeNighttimeBlend = 0; Object.Mesh.Materials[mm + i].BlendMode = Builder.Materials[i].BlendMode; Object.Mesh.Materials[mm + i].GlowAttenuationData = Builder.Materials[i].GlowAttenuationData; } } } } }openbve-1.4.0.10/openBVE/OpenBve/OldParsers/CsvRwRouteParser.cs000066400000000000000000014535011171674032100241050ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Globalization; using OpenBveApi.Colors; using OpenBveApi.Math; namespace OpenBve { internal class CsvRwRouteParser { // structures private struct Rail { internal bool RailStart; internal bool RailStartRefreshed; internal double RailStartX; internal double RailStartY; internal bool RailEnd; internal double RailEndX; internal double RailEndY; } private struct WallDike { internal bool Exists; internal int Type; internal int Direction; } private struct FreeObj { internal double TrackPosition; internal int Type; internal double X; internal double Y; internal double Yaw; internal double Pitch; internal double Roll; } private struct Pole { internal bool Exists; internal int Mode; internal double Location; internal double Interval; internal int Type; } private struct Form { internal int PrimaryRail; internal int SecondaryRail; internal int FormType; internal int RoofType; internal const int SecondaryRailStub = 0; internal const int SecondaryRailL = -1; internal const int SecondaryRailR = -2; } private struct Crack { internal int PrimaryRail; internal int SecondaryRail; internal int Type; } private struct Signal { internal double TrackPosition; internal int Section; internal int SignalCompatibilityObjectIndex; internal int SignalObjectIndex; internal double X; internal double Y; internal double Yaw; internal double Pitch; internal double Roll; internal bool ShowObject; internal bool ShowPost; internal int GameSignalIndex; } private struct Section { internal double TrackPosition; internal int[] Aspects; internal int DepartureStationIndex; internal bool Invisible; internal Game.SectionType Type; } private struct Limit { internal double TrackPosition; internal double Speed; internal int Direction; internal int Cource; } private struct Stop { internal double TrackPosition; internal int Station; internal int Direction; internal double ForwardTolerance; internal double BackwardTolerance; internal int Cars; } private struct Brightness { internal double TrackPosition; internal float Value; } private struct Marker { internal double StartingPosition; internal double EndingPosition; internal Textures.Texture Texture; } private enum SoundType { World, TrainStatic, TrainDynamic } private struct Sound { internal double TrackPosition; internal Sounds.SoundBuffer SoundBuffer; internal SoundType Type; internal double X; internal double Y; internal double Radius; internal double Speed; } private struct Transponder { internal double TrackPosition; internal int Type; internal bool ShowDefaultObject; internal int BeaconStructureIndex; internal int Data; internal int Section; internal double X; internal double Y; internal double Yaw; internal double Pitch; internal double Roll; } private struct PointOfInterest { internal double TrackPosition; internal int RailIndex; internal double X; internal double Y; internal double Yaw; internal double Pitch; internal double Roll; internal string Text; } private class Block { internal int Background; internal Brightness[] Brightness; internal Game.Fog Fog; internal bool FogDefined; internal int[] Cycle; internal double Height; internal Rail[] Rail; internal int[] RailType; internal WallDike[] RailWall; internal WallDike[] RailDike; internal Pole[] RailPole; internal FreeObj[][] RailFreeObj; internal FreeObj[] GroundFreeObj; internal Form[] Form; internal Crack[] Crack; internal Signal[] Signal; internal Section[] Section; internal Limit[] Limit; internal Stop[] Stop; internal Sound[] Sound; internal Transponder[] Transponder; internal PointOfInterest[] PointsOfInterest; internal TrackManager.TrackElement CurrentTrackState; internal double Pitch; internal double Turn; internal int Station; internal bool StationPassAlarm; internal double Accuracy; internal double AdhesionMultiplier; } private struct StructureData { internal ObjectManager.UnifiedObject[] Rail; internal ObjectManager.UnifiedObject[][] Poles; internal ObjectManager.UnifiedObject[] Ground; internal ObjectManager.UnifiedObject[] WallL; internal ObjectManager.UnifiedObject[] WallR; internal ObjectManager.UnifiedObject[] DikeL; internal ObjectManager.UnifiedObject[] DikeR; internal ObjectManager.UnifiedObject[] FormL; internal ObjectManager.UnifiedObject[] FormR; internal ObjectManager.StaticObject[] FormCL; internal ObjectManager.StaticObject[] FormCR; internal ObjectManager.UnifiedObject[] RoofL; internal ObjectManager.UnifiedObject[] RoofR; internal ObjectManager.StaticObject[] RoofCL; internal ObjectManager.StaticObject[] RoofCR; internal ObjectManager.StaticObject[] CrackL; internal ObjectManager.StaticObject[] CrackR; internal ObjectManager.UnifiedObject[] FreeObj; internal ObjectManager.UnifiedObject[] Beacon; internal int[][] Cycle; internal int[] Run; internal int[] Flange; } private abstract class SignalData { } private class Bve4SignalData : SignalData { internal ObjectManager.StaticObject BaseObject; internal ObjectManager.StaticObject GlowObject; internal Textures.Texture[] SignalTextures; internal Textures.Texture[] GlowTextures; } private class CompatibilitySignalData : SignalData { internal int[] Numbers; internal ObjectManager.StaticObject[] Objects; internal CompatibilitySignalData(int[] Numbers, ObjectManager.StaticObject[] Objects) { this.Numbers = Numbers; this.Objects = Objects; } } private class AnimatedObjectSignalData : SignalData { internal ObjectManager.AnimatedObjectCollection Objects; } private struct RouteData { internal double TrackPosition; internal double BlockInterval; internal double UnitOfSpeed; internal bool AccurateObjectDisposal; internal bool SignedCant; internal bool FogTransitionMode; internal StructureData Structure; internal SignalData[] SignalData; internal CompatibilitySignalData[] CompatibilitySignalData; internal Textures.Texture[] TimetableDaytime; internal Textures.Texture[] TimetableNighttime; internal World.Background[] Backgrounds; internal double[] SignalSpeeds; internal Block[] Blocks; internal Marker[] Markers; internal int FirstUsedBlock; } // parse route internal static void ParseRoute(string FileName, bool IsRW, System.Text.Encoding Encoding, string TrainPath, string ObjectPath, string SoundPath, bool PreviewOnly) { // initialize data string CompatibilityFolder = Program.FileSystem.GetDataFolder("Compatibility"); RouteData Data = new RouteData(); Data.BlockInterval = 25.0; Data.AccurateObjectDisposal = false; Data.FirstUsedBlock = -1; Data.Blocks = new Block[1]; Data.Blocks[0] = new Block(); Data.Blocks[0].Rail = new Rail[1]; Data.Blocks[0].Rail[0].RailStart = true; Data.Blocks[0].RailType = new int[] { 0 }; Data.Blocks[0].Limit = new Limit[] { }; Data.Blocks[0].Stop = new Stop[] { }; Data.Blocks[0].Station = -1; Data.Blocks[0].StationPassAlarm = false; Data.Blocks[0].Accuracy = 2.0; Data.Blocks[0].AdhesionMultiplier = 1.0; Data.Blocks[0].CurrentTrackState = new TrackManager.TrackElement(0.0); if (!PreviewOnly) { Data.Blocks[0].Background = 0; Data.Blocks[0].Brightness = new Brightness[] { }; Data.Blocks[0].Fog.Start = Game.NoFogStart; Data.Blocks[0].Fog.End = Game.NoFogEnd; Data.Blocks[0].Fog.Color = new Color24(128, 128, 128); Data.Blocks[0].Cycle = new int[] { -1 }; Data.Blocks[0].Height = IsRW ? 0.3 : 0.0; Data.Blocks[0].RailFreeObj = new FreeObj[][] { }; Data.Blocks[0].GroundFreeObj = new FreeObj[] { }; Data.Blocks[0].RailWall = new WallDike[] { }; Data.Blocks[0].RailDike = new WallDike[] { }; Data.Blocks[0].RailPole = new Pole[] { }; Data.Blocks[0].Form = new Form[] { }; Data.Blocks[0].Crack = new Crack[] { }; Data.Blocks[0].Signal = new Signal[] { }; Data.Blocks[0].Section = new Section[] { }; Data.Blocks[0].Sound = new Sound[] { }; Data.Blocks[0].Transponder = new Transponder[] { }; Data.Blocks[0].PointsOfInterest = new PointOfInterest[] { }; Data.Markers = new Marker[] { }; string PoleFolder = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Poles"); Data.Structure.Poles = new ObjectManager.UnifiedObject[][] { new ObjectManager.UnifiedObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (PoleFolder, "pole_1.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }, new ObjectManager.UnifiedObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (PoleFolder, "pole_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }, new ObjectManager.UnifiedObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (PoleFolder, "pole_3.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }, new ObjectManager.UnifiedObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (PoleFolder, "pole_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) } }; Data.Structure.Rail = new ObjectManager.UnifiedObject[] { }; Data.Structure.Ground = new ObjectManager.UnifiedObject[] { }; Data.Structure.WallL = new ObjectManager.UnifiedObject[] { }; Data.Structure.WallR = new ObjectManager.UnifiedObject[] { }; Data.Structure.DikeL = new ObjectManager.UnifiedObject[] { }; Data.Structure.DikeR = new ObjectManager.UnifiedObject[] { }; Data.Structure.FormL = new ObjectManager.UnifiedObject[] { }; Data.Structure.FormR = new ObjectManager.UnifiedObject[] { }; Data.Structure.FormCL = new ObjectManager.StaticObject[] { }; Data.Structure.FormCR = new ObjectManager.StaticObject[] { }; Data.Structure.RoofL = new ObjectManager.UnifiedObject[] { }; Data.Structure.RoofR = new ObjectManager.UnifiedObject[] { }; Data.Structure.RoofCL = new ObjectManager.StaticObject[] { }; Data.Structure.RoofCR = new ObjectManager.StaticObject[] { }; Data.Structure.CrackL = new ObjectManager.StaticObject[] { }; Data.Structure.CrackR = new ObjectManager.StaticObject[] { }; Data.Structure.FreeObj = new ObjectManager.UnifiedObject[] { }; Data.Structure.Beacon = new ObjectManager.UnifiedObject[] { }; Data.Structure.Cycle = new int[][] { }; Data.Structure.Run = new int[] { }; Data.Structure.Flange = new int[] { }; Data.Backgrounds = new World.Background[] { }; Data.TimetableDaytime = new Textures.Texture[] { null, null, null, null }; Data.TimetableNighttime = new Textures.Texture[] { null, null, null, null }; // signals string SignalFolder = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Signals"); Data.SignalData = new SignalData[7]; Data.SignalData[3] = new CompatibilitySignalData(new int[] { 0, 2, 4 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (SignalFolder, "signal_3_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (SignalFolder, "signal_3_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (SignalFolder, "signal_3_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.SignalData[4] = new CompatibilitySignalData(new int[] { 0, 1, 2, 4 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (SignalFolder, "signal_4_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (SignalFolder, "signal_4a_1.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (SignalFolder, "signal_4a_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile (SignalFolder, "signal_4a_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.SignalData[5] = new CompatibilitySignalData(new int[] { 0, 1, 2, 3, 4 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5a_1.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_3.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.SignalData[6] = new CompatibilitySignalData(new int[] { 0, 3, 4 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "repeatingsignal_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "repeatingsignal_3.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "repeatingsignal_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); // compatibility signals Data.CompatibilitySignalData = new CompatibilitySignalData[9]; Data.CompatibilitySignalData[0] = new CompatibilitySignalData(new int[] { 0, 2 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_2_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_2a_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.CompatibilitySignalData[1] = new CompatibilitySignalData(new int[] { 0, 4 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_2_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_2b_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.CompatibilitySignalData[2] = new CompatibilitySignalData(new int[] { 0, 2, 4 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_3_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_3_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_3_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.CompatibilitySignalData[3] = new CompatibilitySignalData(new int[] { 0, 1, 2, 4 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_4_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_4a_1.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_4a_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_4a_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.CompatibilitySignalData[4] = new CompatibilitySignalData(new int[] { 0, 2, 3, 4 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_4_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_4b_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_4b_3.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_4b_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.CompatibilitySignalData[5] = new CompatibilitySignalData(new int[] { 0, 1, 2, 3, 4 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5a_1.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_3.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.CompatibilitySignalData[6] = new CompatibilitySignalData(new int[] { 0, 2, 3, 4, 5 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_3.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_5b_5.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.CompatibilitySignalData[7] = new CompatibilitySignalData(new int[] { 0, 1, 2, 3, 4, 5 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_6_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_6_1.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_6_2.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_6_3.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_6_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "signal_6_5.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); Data.CompatibilitySignalData[8] = new CompatibilitySignalData(new int[] { 0, 3, 4 }, new ObjectManager.StaticObject[] { ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "repeatingsignal_0.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "repeatingsignal_3.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false), ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalFolder, "repeatingsignal_4.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false) }); // game data Game.Sections = new Game.Section[1]; Game.Sections[0].Aspects = new Game.SectionAspect[] { new Game.SectionAspect(0, 0.0), new Game.SectionAspect(4, double.PositiveInfinity) }; Game.Sections[0].CurrentAspect = 0; Game.Sections[0].NextSection = -1; Game.Sections[0].PreviousSection = -1; Game.Sections[0].SignalIndices = new int[] { }; Game.Sections[0].StationIndex = -1; Game.Sections[0].TrackPosition = 0; Game.Sections[0].Trains = new TrainManager.Train[] { }; // continue Data.SignalSpeeds = new double[] { 0.0, 6.94444444444444, 15.2777777777778, 20.8333333333333, double.PositiveInfinity, double.PositiveInfinity }; } ParseRouteForData(FileName, IsRW, Encoding, TrainPath, ObjectPath, SoundPath, ref Data, PreviewOnly); if (Loading.Cancel) return; ApplyRouteData(FileName, Encoding, ref Data, PreviewOnly); } // ================================ // parse route for data private class Expression { internal string File; internal string Text; internal int Line; internal int Column; internal double TrackPositionOffset; } private static void ParseRouteForData(string FileName, bool IsRW, System.Text.Encoding Encoding, string TrainPath, string ObjectPath, string SoundPath, ref RouteData Data, bool PreviewOnly) { // parse string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); Expression[] Expressions; PreprocessSplitIntoExpressions(FileName, IsRW, Lines, Encoding, out Expressions, true, 0.0); PreprocessChrRndSub(FileName, IsRW, ref Expressions); double[] UnitOfLength = new double[] { 1.0 }; Data.UnitOfSpeed = 0.277777777777778; PreprocessOptions(FileName, IsRW, Encoding, Expressions, ref Data, ref UnitOfLength); PreprocessSortByTrackPosition(FileName, IsRW, UnitOfLength, ref Expressions); ParseRouteForData(FileName, IsRW, Encoding, Expressions, TrainPath, ObjectPath, SoundPath, UnitOfLength, ref Data, PreviewOnly); Game.RouteUnitOfLength = UnitOfLength; } // preprocess split into expressions private static void PreprocessSplitIntoExpressions(string FileName, bool IsRW, string[] Lines, System.Text.Encoding Encoding, out Expression[] Expressions, bool AllowRwRouteDescription, double trackPositionOffset) { Expressions = new Expression[4096]; int e = 0; // full-line rw comments if (IsRW) { for (int i = 0; i < Lines.Length; i++) { int Level = 0; for (int j = 0; j < Lines[i].Length; j++) { switch (Lines[i][j]) { case '(': Level++; break; case ')': Level--; break; case ';': if (Level == 0) { Lines[i] = Lines[i].Substring(0, j).TrimEnd(); j = Lines[i].Length; } break; case '=': if (Level == 0) { j = Lines[i].Length; } break; } } } } // parse for (int i = 0; i < Lines.Length; i++) { if (IsRW & AllowRwRouteDescription) { // ignore rw route description if ( Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].IndexOf("]", StringComparison.Ordinal) > 0 | Lines[i].StartsWith("$") ) { AllowRwRouteDescription = false; Game.RouteComment = Game.RouteComment.Trim(); } else { if (Game.RouteComment.Length != 0) { Game.RouteComment += "\n"; } Game.RouteComment += Lines[i]; continue; } } { // count expressions int n = 0; int Level = 0; for (int j = 0; j < Lines[i].Length; j++) { switch (Lines[i][j]) { case '(': Level++; break; case ')': Level--; break; case ',': if (!IsRW & Level == 0) n++; break; case '@': if (IsRW & Level == 0) n++; break; } } // create expressions int m = e + n + 1; while (m >= Expressions.Length) { Array.Resize(ref Expressions, Expressions.Length << 1); } Level = 0; int a = 0, c = 0; for (int j = 0; j < Lines[i].Length; j++) { switch (Lines[i][j]) { case '(': Level++; break; case ')': Level--; break; case ',': if (Level == 0 & !IsRW) { string t = Lines[i].Substring(a, j - a).Trim(); if (t.Length > 0 && !t.StartsWith(";")) { Expressions[e] = new Expression(); Expressions[e].File = FileName; Expressions[e].Text = t; Expressions[e].Line = i + 1; Expressions[e].Column = c + 1; Expressions[e].TrackPositionOffset = trackPositionOffset; e++; } a = j + 1; c++; } break; case '@': if (Level == 0 & IsRW) { string t = Lines[i].Substring(a, j - a).Trim(); if (t.Length > 0 && !t.StartsWith(";")) { Expressions[e] = new Expression(); Expressions[e].File = FileName; Expressions[e].Text = t; Expressions[e].Line = i + 1; Expressions[e].Column = c + 1; Expressions[e].TrackPositionOffset = trackPositionOffset; e++; } a = j + 1; c++; } break; } } if (Lines[i].Length - a > 0) { string t = Lines[i].Substring(a).Trim(); if (t.Length > 0 && !t.StartsWith(";")) { Expressions[e] = new Expression(); Expressions[e].File = FileName; Expressions[e].Text = t; Expressions[e].Line = i + 1; Expressions[e].Column = c + 1; Expressions[e].TrackPositionOffset = trackPositionOffset; e++; } } } } Array.Resize(ref Expressions, e); } // preprocess chrrndsub private static void PreprocessChrRndSub(string FileName, bool IsRW, ref Expression[] Expressions) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; System.Text.Encoding Encoding = new System.Text.ASCIIEncoding(); string[] Subs = new string[16]; int openIfs = 0; for (int i = 0; i < Expressions.Length; i++) { string Epilog = " at line " + Expressions[i].Line.ToString(Culture) + ", column " + Expressions[i].Column.ToString(Culture) + " in file " + Expressions[i].File; bool continueWithNextExpression = false; for (int j = Expressions[i].Text.Length - 1; j >= 0; j--) { if (Expressions[i].Text[j] == '$') { int k; for (k = j + 1; k < Expressions[i].Text.Length; k++) { if (Expressions[i].Text[k] == '(') { break; } else if (Expressions[i].Text[k] == '/' | Expressions[i].Text[k] == '\\') { k = Expressions[i].Text.Length + 1; break; } } if (k <= Expressions[i].Text.Length) { string t = Expressions[i].Text.Substring(j, k - j).TrimEnd(); int l = 1, h; for (h = k + 1; h < Expressions[i].Text.Length; h++) { switch (Expressions[i].Text[h]) { case '(': l++; break; case ')': l--; if (l < 0) { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "Invalid parenthesis structure in " + t + Epilog); } break; } if (l <= 0) { break; } } if (continueWithNextExpression) { break; } if (l != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid parenthesis structure in " + t + Epilog); continueWithNextExpression = true; break; } string s = Expressions[i].Text.Substring(k + 1, h - k - 1).Trim(); switch (t.ToLowerInvariant()) { case "$if": if (j != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "The $If directive must not appear within another statement" + Epilog); } else { double num; if (double.TryParse(s, System.Globalization.NumberStyles.Float, Culture, out num)) { openIfs++; Expressions[i].Text = string.Empty; if (num == 0.0) { /* * Blank every expression until the matching $Else or $EndIf * */ i++; int level = 1; while (i < Expressions.Length) { if (Expressions[i].Text.StartsWith("$if", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level++; } else if (Expressions[i].Text.StartsWith("$else", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; if (level == 1) { level--; break; } } else if (Expressions[i].Text.StartsWith("$endif", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level--; if (level == 0) { openIfs--; break; } } else { Expressions[i].Text = string.Empty; } i++; } if (level != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "$EndIf missing at the end of the file" + Epilog); } } continueWithNextExpression = true; break; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The $If condition does not evaluate to a number" + Epilog); } } continueWithNextExpression = true; break; case "$else": /* * Blank every expression until the matching $EndIf * */ Expressions[i].Text = string.Empty; if (openIfs != 0) { i++; int level = 1; while (i < Expressions.Length) { if (Expressions[i].Text.StartsWith("$if", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level++; } else if (Expressions[i].Text.StartsWith("$else", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; if (level == 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Duplicate $Else encountered" + Epilog); } } else if (Expressions[i].Text.StartsWith("$endif", StringComparison.OrdinalIgnoreCase)) { Expressions[i].Text = string.Empty; level--; if (level == 0) { openIfs--; break; } } else { Expressions[i].Text = string.Empty; } i++; } if (level != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "$EndIf missing at the end of the file" + Epilog); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "$Else without matching $If encountered" + Epilog); } continueWithNextExpression = true; break; case "$endif": Expressions[i].Text = string.Empty; if (openIfs != 0) { openIfs--; } else { Interface.AddMessage(Interface.MessageType.Error, false, "$EndIf without matching $If encountered" + Epilog); } continueWithNextExpression = true; break; case "$include": if (j != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "The $Include directive must not appear within another statement" + Epilog); continueWithNextExpression = true; break; } else { string[] args = s.Split(';'); for (int ia = 0; ia < args.Length; ia++) { args[ia] = args[ia].Trim(); } int count = (args.Length + 1) / 2; string[] files = new string[count]; double[] weights = new double[count]; double[] offsets = new double[count]; double weightsTotal = 0.0; for (int ia = 0; ia < count; ia++) { string file; double offset; int colon = args[2 * ia].IndexOf(':'); if (colon >= 0) { file = args[2 * ia].Substring(0, colon).TrimEnd(); string value = args[2 * ia].Substring(colon + 1).TrimStart(); if (!double.TryParse(value, NumberStyles.Float, Culture, out offset)) { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "The track position offset " + value + " is invalid in " + t + Epilog); break; } } else { file = args[2 * ia]; offset = 0.0; } files[ia] = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), file); offsets[ia] = offset; if (!System.IO.File.Exists(files[ia])) { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "The file " + file + " could not be found in " + t + Epilog); break; } else if (2 * ia + 1 < args.Length) { if (!Interface.TryParseDoubleVb6(args[2 * ia + 1], out weights[ia])) { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "A weight is invalid in " + t + Epilog); break; } else if (weights[ia] <= 0.0) { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "A weight is not positive in " + t + Epilog); break; } else { weightsTotal += weights[ia]; } } else { weights[ia] = 1.0; weightsTotal += 1.0; } } if (count == 0) { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "No file was specified in " + t + Epilog); break; } else if (!continueWithNextExpression) { double number = Program.RandomNumberGenerator.NextDouble() * weightsTotal; double value = 0.0; int chosenIndex = 0; for (int ia = 0; ia < count; ia++) { value += weights[ia]; if (value > number) { chosenIndex = ia; break; } } Expression[] expr; string[] lines = System.IO.File.ReadAllLines(files[chosenIndex], Encoding); PreprocessSplitIntoExpressions(files[chosenIndex], IsRW, lines, Encoding, out expr, false, offsets[chosenIndex] + Expressions[i].TrackPositionOffset); int length = Expressions.Length; if (expr.Length == 0) { for (int ia = i; ia < Expressions.Length - 1; ia++) { Expressions[ia] = Expressions[ia + 1]; } Array.Resize(ref Expressions, length - 1); } else { Array.Resize(ref Expressions, length + expr.Length - 1); for (int ia = Expressions.Length - 1; ia >= i + expr.Length; ia--) { Expressions[ia] = Expressions[ia - expr.Length + 1]; } for (int ia = 0; ia < expr.Length; ia++) { Expressions[i + ia] = expr[ia]; } } i--; continueWithNextExpression = true; } } break; case "$chr": { int x; if (Interface.TryParseIntVb6(s, out x)) { if (x > 0 & x < 128) { Expressions[i].Text = Expressions[i].Text.Substring(0, j) + new string(Encoding.GetChars(new byte[] { (byte)x })) + Expressions[i].Text.Substring(h + 1); } else { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "Index does not correspond to a valid ASCII character in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "Index is invalid in " + t + Epilog); } } break; case "$rnd": { int m = s.IndexOf(";", StringComparison.Ordinal); if (m >= 0) { string s1 = s.Substring(0, m).TrimEnd(); string s2 = s.Substring(m + 1).TrimStart(); int x; if (Interface.TryParseIntVb6(s1, out x)) { int y; if (Interface.TryParseIntVb6(s2, out y)) { int z = x + (int)Math.Floor(Program.RandomNumberGenerator.NextDouble() * (double)(y - x + 1)); Expressions[i].Text = Expressions[i].Text.Substring(0, j) + z.ToString(Culture) + Expressions[i].Text.Substring(h + 1); } else { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "Index2 is invalid in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "Index1 is invalid in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + t + Epilog); } } break; case "$sub": { l = 0; bool f = false; int m; for (m = h + 1; m < Expressions[i].Text.Length; m++) { switch (Expressions[i].Text[m]) { case '(': l++; break; case ')': l--; break; case '=': if (l == 0) { f = true; } break; default: if (!char.IsWhiteSpace(Expressions[i].Text[m])) l = -1; break; } if (f | l < 0) break; } if (f) { l = 0; int n; for (n = m + 1; n < Expressions[i].Text.Length; n++) { switch (Expressions[i].Text[n]) { case '(': l++; break; case ')': l--; break; } if (l < 0) break; } int x; if (Interface.TryParseIntVb6(s, out x)) { if (x >= 0) { while (x >= Subs.Length) { Array.Resize(ref Subs, Subs.Length << 1); } Subs[x] = Expressions[i].Text.Substring(m + 1, n - m - 1).Trim(); Expressions[i].Text = Expressions[i].Text.Substring(0, j) + Expressions[i].Text.Substring(n); } else { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "Index is expected to be non-negative in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "Index is invalid in " + t + Epilog); } } else { int x; if (Interface.TryParseIntVb6(s, out x)) { if (x >= 0 & x < Subs.Length && Subs[x] != null) { Expressions[i].Text = Expressions[i].Text.Substring(0, j) + Subs[x] + Expressions[i].Text.Substring(h + 1); } else { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "Index is out of range in " + t + Epilog); } } else { continueWithNextExpression = true; Interface.AddMessage(Interface.MessageType.Error, false, "Index is invalid in " + t + Epilog); } } } break; } } } if (continueWithNextExpression) { break; } } } // handle comments introduced via chr, rnd, sub { int length = Expressions.Length; for (int i = 0; i < length; i++) { Expressions[i].Text = Expressions[i].Text.Trim(); if (Expressions[i].Text.Length != 0) { if (Expressions[i].Text[0] == ';') { for (int j = i; j < length - 1; j++) { Expressions[j] = Expressions[j + 1]; } length--; i--; } } else { for (int j = i; j < length - 1; j++) { Expressions[j] = Expressions[j + 1]; } length--; i--; } } if (length != Expressions.Length) { Array.Resize(ref Expressions, length); } } } // preprocess options private static void PreprocessOptions(string FileName, bool IsRW, System.Text.Encoding Encoding, Expression[] Expressions, ref RouteData Data, ref double[] UnitOfLength) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string Section = ""; bool SectionAlwaysPrefix = false; // process expressions for (int j = 0; j < Expressions.Length; j++) { if (IsRW && Expressions[j].Text.StartsWith("[") && Expressions[j].Text.EndsWith("]")) { Section = Expressions[j].Text.Substring(1, Expressions[j].Text.Length - 2).Trim(); if (string.Compare(Section, "object", StringComparison.OrdinalIgnoreCase) == 0) { Section = "Structure"; } else if (string.Compare(Section, "railway", StringComparison.OrdinalIgnoreCase) == 0) { Section = "Track"; } SectionAlwaysPrefix = true; } else { // find equals int Equals = Expressions[j].Text.IndexOf('='); if (Equals >= 0) { // handle RW cycle syntax string t = Expressions[j].Text.Substring(0, Equals); if (Section.ToLowerInvariant() == "cycle" & SectionAlwaysPrefix) { double b; if (Interface.TryParseDoubleVb6(t, out b)) { t = ".Ground(" + t + ")"; } } else if (Section.ToLowerInvariant() == "signal" & SectionAlwaysPrefix) { double b; if (Interface.TryParseDoubleVb6(t, out b)) { t = ".Void(" + t + ")"; } } // convert RW style into CSV style Expressions[j].Text = t + " " + Expressions[j].Text.Substring(Equals + 1); } // separate command and arguments string Command, ArgumentSequence; SeparateCommandsAndArguments(Expressions[j], out Command, out ArgumentSequence, Culture, Expressions[j].File, j, true); // process command double Number; bool NumberCheck = !IsRW || string.Compare(Section, "track", StringComparison.OrdinalIgnoreCase) == 0; if (!NumberCheck || !Interface.TryParseDoubleVb6(Command, UnitOfLength, out Number)) { // split arguments string[] Arguments; { int n = 0; for (int k = 0; k < ArgumentSequence.Length; k++) { if (IsRW & ArgumentSequence[k] == ',') { n++; } else if (ArgumentSequence[k] == ';') { n++; } } Arguments = new string[n + 1]; int a = 0, h = 0; for (int k = 0; k < ArgumentSequence.Length; k++) { if (IsRW & ArgumentSequence[k] == ',') { Arguments[h] = ArgumentSequence.Substring(a, k - a).Trim(); a = k + 1; h++; } else if (ArgumentSequence[k] == ';') { Arguments[h] = ArgumentSequence.Substring(a, k - a).Trim(); a = k + 1; h++; } } if (ArgumentSequence.Length - a > 0) { Arguments[h] = ArgumentSequence.Substring(a).Trim(); h++; } Array.Resize(ref Arguments, h); } // preprocess command if (Command.ToLowerInvariant() == "with") { if (Arguments.Length >= 1) { Section = Arguments[0]; SectionAlwaysPrefix = false; } else { Section = ""; SectionAlwaysPrefix = false; } Command = null; } else { if (Command.StartsWith(".")) { Command = Section + Command; } else if (SectionAlwaysPrefix) { Command = Section + "." + Command; } Command = Command.Replace(".Void", ""); } // handle indices int CommandIndex1 = 0, CommandIndex2 = 0; if (Command != null && Command.EndsWith(")")) { for (int k = Command.Length - 2; k >= 0; k--) { if (Command[k] == '(') { string Indices = Command.Substring(k + 1, Command.Length - k - 2).TrimStart(); Command = Command.Substring(0, k).TrimEnd(); int h = Indices.IndexOf(";"); if (h >= 0) { string a = Indices.Substring(0, h).TrimEnd(); string b = Indices.Substring(h + 1).TrimStart(); if (a.Length > 0 && !Interface.TryParseIntVb6(a, out CommandIndex1)) { Command = null; break; } else if (b.Length > 0 && !Interface.TryParseIntVb6(b, out CommandIndex2)) { Command = null; break; } } else { if (Indices.Length > 0 && !Interface.TryParseIntVb6(Indices, out CommandIndex1)) { Command = null; break; } } break; } } } // process command if (Command != null) { switch (Command.ToLowerInvariant()) { // options case "options.unitoflength": { if (Arguments.Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "At least 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { UnitOfLength = new double[Arguments.Length]; for (int i = 0; i < Arguments.Length; i++) { UnitOfLength[i] = i == Arguments.Length - 1 ? 1.0 : 0.0; if (Arguments[i].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[i], out UnitOfLength[i])) { Interface.AddMessage(Interface.MessageType.Error, false, "FactorInMeters" + i.ToString(Culture) + " is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); UnitOfLength[i] = i == 0 ? 1.0 : 0.0; } else if (UnitOfLength[i] <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "FactorInMeters" + i.ToString(Culture) + " is expected to be positive in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); UnitOfLength[i] = i == Arguments.Length - 1 ? 1.0 : 0.0; } } } } break; case "options.unitofspeed": { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length > 1) { Interface.AddMessage(Interface.MessageType.Warning, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } if (Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Data.UnitOfSpeed)) { Interface.AddMessage(Interface.MessageType.Error, false, "FactorInKmph is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); Data.UnitOfSpeed = 0.277777777777778; } else if (Data.UnitOfSpeed <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "FactorInKmph is expected to be positive in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); Data.UnitOfSpeed = 0.277777777777778; } else { Data.UnitOfSpeed *= 0.277777777777778; } } } break; case "options.objectvisibility": { if (Arguments.Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length > 1) { Interface.AddMessage(Interface.MessageType.Warning, false, "Exactly 1 argument is expected in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } int mode = 0; if (Arguments.Length >= 1 && Arguments[0].Length != 0 && !Interface.TryParseIntVb6(Arguments[0], out mode)) { Interface.AddMessage(Interface.MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); mode = 0; } else if (mode != 0 & mode != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "The specified Mode is not supported in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); mode = 0; } Data.AccurateObjectDisposal = mode == 1; } } break; } } } } } } // preprocess sort by track position private struct PositionedExpression { internal double TrackPosition; internal Expression Expression; } private static void PreprocessSortByTrackPosition(string FileName, bool IsRW, double[] UnitFactors, ref Expression[] Expressions) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; PositionedExpression[] p = new PositionedExpression[Expressions.Length]; int n = 0; double a = -1.0; bool NumberCheck = !IsRW; for (int i = 0; i < Expressions.Length; i++) { if (IsRW) { // only check for track positions in the railway section for RW routes if (Expressions[i].Text.StartsWith("[", StringComparison.Ordinal) & Expressions[i].Text.EndsWith("]", StringComparison.Ordinal)) { string s = Expressions[i].Text.Substring(1, Expressions[i].Text.Length - 2).Trim(); if (string.Compare(s, "Railway", StringComparison.OrdinalIgnoreCase) == 0) { NumberCheck = true; } else { NumberCheck = false; } } } double x; if (NumberCheck && Interface.TryParseDouble(Expressions[i].Text, UnitFactors, out x)) { x += Expressions[i].TrackPositionOffset; if (x >= 0.0) { a = x; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Negative track position encountered at line " + Expressions[i].Line.ToString(Culture) + ", column " + Expressions[i].Column.ToString(Culture) + " in file " + Expressions[i].File); } } else { p[n].TrackPosition = a; p[n].Expression = Expressions[i]; int j = n; n++; while (j > 0) { if (p[j].TrackPosition < p[j - 1].TrackPosition) { PositionedExpression t = p[j]; p[j] = p[j - 1]; p[j - 1] = t; j--; } else { break; } } } } a = -1.0; Expression[] e = new Expression[Expressions.Length]; int m = 0; for (int i = 0; i < n; i++) { if (p[i].TrackPosition != a) { a = p[i].TrackPosition; e[m] = new Expression(); e[m].Text = (a / UnitFactors[UnitFactors.Length - 1]).ToString(Culture); e[m].Line = -1; e[m].Column = -1; m++; } e[m] = p[i].Expression; m++; } Array.Resize(ref e, m); Expressions = e; } // separate commands and arguments private static void SeparateCommandsAndArguments(Expression Expression, out string Command, out string ArgumentSequence, System.Globalization.CultureInfo Culture, string FileName, int LineNumber, bool RaiseErrors) { bool openingerror = false, closingerror = false; int i; for (i = 0; i < Expression.Text.Length; i++) { if (Expression.Text[i] == '(') { bool found = false; i++; while (i < Expression.Text.Length) { if (Expression.Text[i] == '(') { if (RaiseErrors & !openingerror) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid opening parenthesis encountered at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); openingerror = true; } } else if (Expression.Text[i] == ')') { found = true; break; } i++; } if (!found) { if (RaiseErrors & !closingerror) { Interface.AddMessage(Interface.MessageType.Error, false, "Missing closing parenthesis encountered at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); closingerror = true; } Expression.Text += ")"; } } else if (Expression.Text[i] == ')') { if (RaiseErrors & !closingerror) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid closing parenthesis encountered at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); closingerror = true; } } else if (char.IsWhiteSpace(Expression.Text[i])) { if (i >= Expression.Text.Length - 1 || !char.IsWhiteSpace(Expression.Text[i + 1])) { break; } } } if (i < Expression.Text.Length) { // white space was found outside of parentheses string a = Expression.Text.Substring(0, i); if (a.IndexOf('(') >= 0 & a.IndexOf(')') >= 0) { // indices found not separated from the command by spaces Command = Expression.Text.Substring(0, i).TrimEnd(); ArgumentSequence = Expression.Text.Substring(i + 1).TrimStart(); if (ArgumentSequence.StartsWith("(") & ArgumentSequence.EndsWith(")")) { // arguments are enclosed by parentheses ArgumentSequence = ArgumentSequence.Substring(1, ArgumentSequence.Length - 2).Trim(); } else if (ArgumentSequence.StartsWith("(")) { // only opening parenthesis found if (RaiseErrors & !closingerror) { Interface.AddMessage(Interface.MessageType.Error, false, "Missing closing parenthesis encountered at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } ArgumentSequence = ArgumentSequence.Substring(1).TrimStart(); } } else { // no indices found before the space if (i < Expression.Text.Length - 1 && Expression.Text[i + 1] == '(') { // opening parenthesis follows the space int j = Expression.Text.IndexOf(')', i + 1); if (j > i + 1) { // closing parenthesis found if (j == Expression.Text.Length - 1) { // only closing parenthesis found at the end of the expression Command = Expression.Text.Substring(0, i).TrimEnd(); ArgumentSequence = Expression.Text.Substring(i + 2, j - i - 2).Trim(); } else { // detect border between indices and arguments bool found = false; Command = null; ArgumentSequence = null; for (int k = j + 1; k < Expression.Text.Length; k++) { if (char.IsWhiteSpace(Expression.Text[k])) { Command = Expression.Text.Substring(0, k).TrimEnd(); ArgumentSequence = Expression.Text.Substring(k + 1).TrimStart(); found = true; break; } else if (Expression.Text[k] == '(') { Command = Expression.Text.Substring(0, k).TrimEnd(); ArgumentSequence = Expression.Text.Substring(k).TrimStart(); found = true; break; } } if (!found) { if (RaiseErrors & !openingerror & !closingerror) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid syntax encountered at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); openingerror = true; closingerror = true; } Command = Expression.Text; ArgumentSequence = ""; } if (ArgumentSequence.StartsWith("(") & ArgumentSequence.EndsWith(")")) { // arguments are enclosed by parentheses ArgumentSequence = ArgumentSequence.Substring(1, ArgumentSequence.Length - 2).Trim(); } else if (ArgumentSequence.StartsWith("(")) { // only opening parenthesis found if (RaiseErrors & !closingerror) { Interface.AddMessage(Interface.MessageType.Error, false, "Missing closing parenthesis encountered at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } ArgumentSequence = ArgumentSequence.Substring(1).TrimStart(); } } } else { // no closing parenthesis found if (RaiseErrors & !closingerror) { Interface.AddMessage(Interface.MessageType.Error, false, "Missing closing parenthesis encountered at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } Command = Expression.Text.Substring(0, i).TrimEnd(); ArgumentSequence = Expression.Text.Substring(i + 2).TrimStart(); } } else { // no index possible Command = Expression.Text.Substring(0, i).TrimEnd(); ArgumentSequence = Expression.Text.Substring(i + 1).TrimStart(); if (ArgumentSequence.StartsWith("(") & ArgumentSequence.EndsWith(")")) { // arguments are enclosed by parentheses ArgumentSequence = ArgumentSequence.Substring(1, ArgumentSequence.Length - 2).Trim(); } else if (ArgumentSequence.StartsWith("(")) { // only opening parenthesis found if (RaiseErrors & !closingerror) { Interface.AddMessage(Interface.MessageType.Error, false, "Missing closing parenthesis encountered at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } ArgumentSequence = ArgumentSequence.Substring(1).TrimStart(); } } } } else { // no single space found if (Expression.Text.EndsWith(")")) { i = Expression.Text.LastIndexOf('('); if (i >= 0) { Command = Expression.Text.Substring(0, i).TrimEnd(); ArgumentSequence = Expression.Text.Substring(i + 1, Expression.Text.Length - i - 2).Trim(); } else { Command = Expression.Text; ArgumentSequence = ""; } } else { i = Expression.Text.IndexOf('('); if (i >= 0) { if (RaiseErrors & !closingerror) { Interface.AddMessage(Interface.MessageType.Error, false, "Missing closing parenthesis encountered at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } Command = Expression.Text.Substring(0, i).TrimEnd(); ArgumentSequence = Expression.Text.Substring(i + 1).TrimStart(); } else { if (RaiseErrors) { i = Expression.Text.IndexOf(')'); if (i >= 0 & !closingerror) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid closing parenthesis encountered at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } } Command = Expression.Text; ArgumentSequence = ""; } } } // invalid trailing characters if (Command.EndsWith(";")) { if (RaiseErrors) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid trailing semicolon encountered in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } while (Command.EndsWith(";")) { Command = Command.Substring(0, Command.Length - 1); } } } // parse route for data private static void ParseRouteForData(string FileName, bool IsRW, System.Text.Encoding Encoding, Expression[] Expressions, string TrainPath, string ObjectPath, string SoundPath, double[] UnitOfLength, ref RouteData Data, bool PreviewOnly) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string Section = ""; bool SectionAlwaysPrefix = false; int BlockIndex = 0; int BlocksUsed = Data.Blocks.Length; Game.Stations = new Game.Station[] { }; int CurrentStation = -1; int CurrentStop = -1; bool DepartureSignalUsed = false; int CurrentSection = 0; bool ValueBasedSections = false; double progressFactor = Expressions.Length == 0 ? 0.3333 : 0.3333 / (double)Expressions.Length; // process non-track namespaces for (int j = 0; j < Expressions.Length; j++) { Loading.RouteProgress = (double)j * progressFactor; if ((j & 255) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } if (Expressions[j].Text.StartsWith("[") & Expressions[j].Text.EndsWith("]")) { Section = Expressions[j].Text.Substring(1, Expressions[j].Text.Length - 2).Trim(); if (string.Compare(Section, "object", StringComparison.OrdinalIgnoreCase) == 0) { Section = "Structure"; } else if (string.Compare(Section, "railway", StringComparison.OrdinalIgnoreCase) == 0) { Section = "Track"; } SectionAlwaysPrefix = true; } else { // find equals int Equals = Expressions[j].Text.IndexOf('='); if (Equals >= 0) { // handle RW cycle syntax string t = Expressions[j].Text.Substring(0, Equals); if (Section.ToLowerInvariant() == "cycle" & SectionAlwaysPrefix) { double b; if (Interface.TryParseDoubleVb6(t, out b)) { t = ".Ground(" + t + ")"; } } else if (Section.ToLowerInvariant() == "signal" & SectionAlwaysPrefix) { double b; if (Interface.TryParseDoubleVb6(t, out b)) { t = ".Void(" + t + ")"; } } // convert RW style into CSV style Expressions[j].Text = t + " " + Expressions[j].Text.Substring(Equals + 1); } // separate command and arguments string Command, ArgumentSequence; SeparateCommandsAndArguments(Expressions[j], out Command, out ArgumentSequence, Culture, Expressions[j].File, j, false); // process command double Number; bool NumberCheck = !IsRW || string.Compare(Section, "track", StringComparison.OrdinalIgnoreCase) == 0; if (NumberCheck && Interface.TryParseDouble(Command, UnitOfLength, out Number)) { // track position (ignored) } else { // split arguments string[] Arguments; { int n = 0; for (int k = 0; k < ArgumentSequence.Length; k++) { if (IsRW & ArgumentSequence[k] == ',') { n++; } else if (ArgumentSequence[k] == ';') { n++; } } Arguments = new string[n + 1]; int a = 0, h = 0; for (int k = 0; k < ArgumentSequence.Length; k++) { if (IsRW & ArgumentSequence[k] == ',') { Arguments[h] = ArgumentSequence.Substring(a, k - a).Trim(); a = k + 1; h++; } else if (ArgumentSequence[k] == ';') { Arguments[h] = ArgumentSequence.Substring(a, k - a).Trim(); a = k + 1; h++; } } if (ArgumentSequence.Length - a > 0) { Arguments[h] = ArgumentSequence.Substring(a).Trim(); h++; } Array.Resize(ref Arguments, h); } // preprocess command if (Command.ToLowerInvariant() == "with") { if (Arguments.Length >= 1) { Section = Arguments[0]; SectionAlwaysPrefix = false; } else { Section = ""; SectionAlwaysPrefix = false; } Command = null; } else { if (Command.StartsWith(".")) { Command = Section + Command; } else if (SectionAlwaysPrefix) { Command = Section + "." + Command; } Command = Command.Replace(".Void", ""); if (Command.StartsWith("structure", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".load", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 5).TrimEnd(); } else if (Command.StartsWith("texture.background", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".load", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 5).TrimEnd(); } else if (Command.StartsWith("texture.background", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".x", StringComparison.OrdinalIgnoreCase)) { Command = "texture.background.x" + Command.Substring(18, Command.Length - 20).TrimEnd(); } else if (Command.StartsWith("texture.background", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".aspect", StringComparison.OrdinalIgnoreCase)) { Command = "texture.background.aspect" + Command.Substring(18, Command.Length - 25).TrimEnd(); } else if (Command.StartsWith("structure.back", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".x", StringComparison.OrdinalIgnoreCase)) { Command = "texture.background.x" + Command.Substring(14, Command.Length - 16).TrimEnd(); } else if (Command.StartsWith("structure.back", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".aspect", StringComparison.OrdinalIgnoreCase)) { Command = "texture.background.aspect" + Command.Substring(14, Command.Length - 21).TrimEnd(); } else if (Command.StartsWith("cycle", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".params", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 7).TrimEnd(); } else if (Command.StartsWith("signal", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".load", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 5).TrimEnd(); } else if (Command.StartsWith("train.run", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".set", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 4).TrimEnd(); } else if (Command.StartsWith("train.flange", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".set", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 4).TrimEnd(); } else if (Command.StartsWith("train.timetable", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".day.load", StringComparison.OrdinalIgnoreCase)) { Command = "train.timetable.day" + Command.Substring(15, Command.Length - 24).Trim(); } else if (Command.StartsWith("train.timetable", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".night.load", StringComparison.OrdinalIgnoreCase)) { Command = "train.timetable.night" + Command.Substring(15, Command.Length - 26).Trim(); } else if (Command.StartsWith("train.timetable", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".day", StringComparison.OrdinalIgnoreCase)) { Command = "train.timetable.day" + Command.Substring(15, Command.Length - 19).Trim(); } else if (Command.StartsWith("train.timetable", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".night", StringComparison.OrdinalIgnoreCase)) { Command = "train.timetable.night" + Command.Substring(15, Command.Length - 21).Trim(); } else if (Command.StartsWith("route.signal", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".set", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 4).TrimEnd(); } } // handle indices int CommandIndex1 = 0, CommandIndex2 = 0; if (Command != null && Command.EndsWith(")")) { for (int k = Command.Length - 2; k >= 0; k--) { if (Command[k] == '(') { string Indices = Command.Substring(k + 1, Command.Length - k - 2).TrimStart(); Command = Command.Substring(0, k).TrimEnd(); int h = Indices.IndexOf(";"); if (h >= 0) { string a = Indices.Substring(0, h).TrimEnd(); string b = Indices.Substring(h + 1).TrimStart(); if (a.Length > 0 && !Interface.TryParseIntVb6(a, out CommandIndex1)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid first index appeared at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File + "."); Command = null; break; } else if (b.Length > 0 && !Interface.TryParseIntVb6(b, out CommandIndex2)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid second index appeared at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File + "."); Command = null; break; } } else { if (Indices.Length > 0 && !Interface.TryParseIntVb6(Indices, out CommandIndex1)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid index appeared at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File + "."); Command = null; break; } } break; } } } // process command if (Command != null && Command.Length != 0) { switch (Command.ToLowerInvariant()) { // options case "options.blocklength": { double length = 25.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], UnitOfLength, out length)) { Interface.AddMessage(Interface.MessageType.Error, false, "Length is invalid in Options.BlockLength at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); length = 25.0; } Data.BlockInterval = length; } break; case "options.unitoflength": case "options.unitofspeed": case "options.objectvisibility": break; case "options.sectionbehavior": if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { int a; if (!Interface.TryParseIntVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (a != 0 & a != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Mode is expected to be either 0 or 1 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { ValueBasedSections = a == 1; } } break; case "options.cantbehavior": if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { int a; if (!Interface.TryParseIntVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (a != 0 & a != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Mode is expected to be either 0 or 1 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.SignedCant = a == 1; } } break; case "options.fogbehavior": if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { int a; if (!Interface.TryParseIntVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (a != 0 & a != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Mode is expected to be either 0 or 1 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.FogTransitionMode = a == 1; } } break; // route case "route.comment": if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Game.RouteComment = Arguments[0]; } break; case "route.image": if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { string f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Game.RouteImage = f; } } break; case "route.timetable": if (!PreviewOnly) { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, "" + Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Timetable.DefaultTimetableDescription = Arguments[0]; } } break; case "route.change": if (!PreviewOnly) { int change = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out change)) { Interface.AddMessage(Interface.MessageType.Error, false, "Mode is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); change = 0; } else if (change < -1 | change > 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Mode is expected to be -1, 0 or 1 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); change = 0; } Game.TrainStart = (Game.TrainStartMode)change; } break; case "route.gauge": case "train.gauge": if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double a; if (!Interface.TryParseDoubleVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInMillimeters is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInMillimeters is expected to be positive in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Game.RouteRailGauge = 0.001 * a; } } break; case "route.signal": if (!PreviewOnly) { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double a; if (!Interface.TryParseDoubleVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Speed is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "AspectIndex is expected to be non-negative in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Speed is expected to be non-negative in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.SignalSpeeds.Length) { int n = Data.SignalSpeeds.Length; Array.Resize(ref Data.SignalSpeeds, CommandIndex1 + 1); for (int i = n; i < CommandIndex1; i++) { Data.SignalSpeeds[i] = double.PositiveInfinity; } } Data.SignalSpeeds[CommandIndex1] = a * Data.UnitOfSpeed; } } } } break; case "route.runinterval": case "train.interval": { if (!PreviewOnly) { double[] intervals = new double[Arguments.Length]; for (int k = 0; k < Arguments.Length; k++) { if (!Interface.TryParseDoubleVb6(Arguments[k], out intervals[k])) { Interface.AddMessage(Interface.MessageType.Error, false, "Interval" + k.ToString(Culture) + " is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } } Array.Sort(intervals); Game.PrecedingTrainTimeDeltas = intervals; } } break; case "train.velocity": { if (!PreviewOnly) { double limit = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out limit)) { Interface.AddMessage(Interface.MessageType.Error, false, "Speed is invalid in Train.Velocity at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); limit = 0.0; } Game.PrecedingTrainSpeedLimit = limit <= 0.0 ? double.PositiveInfinity : Data.UnitOfSpeed * limit; } } break; case "route.accelerationduetogravity": if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double a; if (!Interface.TryParseDoubleVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be positive in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Game.RouteAccelerationDueToGravity = a; } } break; case "route.elevation": if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double a; if (!Interface.TryParseDoubleVb6(Arguments[0], UnitOfLength, out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Game.RouteInitialElevation = a; } } break; case "route.temperature": if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double a; if (!Interface.TryParseDoubleVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInCelsius is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (a <= -273.15) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInCelsius is expected to be greater than to -273.15 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Game.RouteInitialAirTemperature = a + 273.15; } } break; case "route.pressure": if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double a; if (!Interface.TryParseDoubleVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInKPa is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInKPa is expected to be positive in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Game.RouteInitialAirPressure = 1000.0 * a; } } break; case "route.ambientlight": { int r = 255, g = 255, b = 255; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); b = b < 0 ? 0 : 255; } Renderer.OptionAmbientColor = new Color24((byte)r, (byte)g, (byte)b); } break; case "route.directionallight": { int r = 255, g = 255, b = 255; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); b = b < 0 ? 0 : 255; } Renderer.OptionDiffuseColor = new Color24((byte)r, (byte)g, (byte)b); } break; case "route.lightdirection": { double theta = 60.0, phi = -26.565051177078; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out theta)) { Interface.AddMessage(Interface.MessageType.Error, false, "Theta is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out phi)) { Interface.AddMessage(Interface.MessageType.Error, false, "Phi is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } theta *= 0.0174532925199433; phi *= 0.0174532925199433; double dx = Math.Cos(theta) * Math.Sin(phi); double dy = -Math.Sin(theta); double dz = Math.Cos(theta) * Math.Cos(phi); Renderer.OptionLightPosition = new World.Vector3Df((float)-dx, (float)-dy, (float)-dz); } break; // train case "train.folder": case "train.file": { if (PreviewOnly) { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FolderName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Game.TrainName = Arguments[0]; } } } } break; case "train.run": case "train.rail": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailTypeIndex is out of range in "+Command+" at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { int val = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out val)) { Interface.AddMessage(Interface.MessageType.Error, false, "RunSoundIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); val = 0; } if (val < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RunSoundIndex is expected to be non-negative in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); val = 0; } if (CommandIndex1 >= Data.Structure.Run.Length) { Array.Resize(ref Data.Structure.Run, CommandIndex1 + 1); } Data.Structure.Run[CommandIndex1] = val; } } } break; case "train.flange": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailTypeIndex is out of range in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { int val = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out val)) { Interface.AddMessage(Interface.MessageType.Error, false, "FlangeSoundIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); val = 0; } if (val < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "FlangeSoundIndex expected to be non-negative in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); val = 0; } if (CommandIndex1 >= Data.Structure.Flange.Length) { Array.Resize(ref Data.Structure.Flange, CommandIndex1 + 1); } Data.Structure.Flange[CommandIndex1] = val; } } } break; case "train.timetable.day": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "TimetableIndex is expected to be non-negative in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { while (CommandIndex1 >= Data.TimetableDaytime.Length) { int n = Data.TimetableDaytime.Length; Array.Resize(ref Data.TimetableDaytime, n << 1); for (int i = n; i < Data.TimetableDaytime.Length; i++) { Data.TimetableDaytime[i] = null; } } string f = OpenBveApi.Path.CombineFile(TrainPath, Arguments[0]); if (!System.IO.File.Exists(f)) { f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); } if (System.IO.File.Exists(f)) { Textures.RegisterTexture(f, out Data.TimetableDaytime[CommandIndex1]); } } } } } break; case "train.timetable.night": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "TimetableIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { while (CommandIndex1 >= Data.TimetableNighttime.Length) { int n = Data.TimetableNighttime.Length; Array.Resize(ref Data.TimetableNighttime, n << 1); for (int i = n; i < Data.TimetableNighttime.Length; i++) { Data.TimetableNighttime[i] = null; } } string f = OpenBveApi.Path.CombineFile(TrainPath, Arguments[0]); if (!System.IO.File.Exists(f)) { f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); } if (System.IO.File.Exists(f)) { Textures.RegisterTexture(f, out Data.TimetableNighttime[CommandIndex1]); } } } } } break; // structure case "structure.rail": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.Rail.Length) { Array.Resize(ref Data.Structure.Rail, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.Rail[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; case "structure.beacon": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "BeaconStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.Beacon.Length) { Array.Resize(ref Data.Structure.Beacon, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.Beacon[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; case "structure.pole": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "AdditionalRailsCovered is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (CommandIndex2 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "PoleStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.Poles.Length) { Array.Resize(ref Data.Structure.Poles, CommandIndex1 + 1); } if (Data.Structure.Poles[CommandIndex1] == null) { Data.Structure.Poles[CommandIndex1] = new ObjectManager.UnifiedObject[CommandIndex2 + 1]; } else if (CommandIndex2 >= Data.Structure.Poles[CommandIndex1].Length) { Array.Resize(ref Data.Structure.Poles[CommandIndex1], CommandIndex2 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.Poles[CommandIndex1][CommandIndex2] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; case "structure.ground": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "GroundStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.Ground.Length) { Array.Resize(ref Data.Structure.Ground, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.Ground[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; case "structure.walll": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "WallStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.WallL.Length) { Array.Resize(ref Data.Structure.WallL, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.WallL[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; case "structure.wallr": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "WallStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.WallR.Length) { Array.Resize(ref Data.Structure.WallR, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.WallR[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; case "structure.dikel": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "DikeStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.DikeL.Length) { Array.Resize(ref Data.Structure.DikeL, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.DikeL[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; case "structure.diker": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "DikeStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.DikeR.Length) { Array.Resize(ref Data.Structure.DikeR, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.DikeR[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; case "structure.forml": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.FormL.Length) { Array.Resize(ref Data.Structure.FormL, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.FormL[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; case "structure.formr": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.FormR.Length) { Array.Resize(ref Data.Structure.FormR, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.FormR[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; case "structure.formcl": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.FormCL.Length) { Array.Resize(ref Data.Structure.FormCL, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.FormCL[CommandIndex1] = ObjectManager.LoadStaticObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, true, false, false); } } } } } break; case "structure.formcr": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.FormCR.Length) { Array.Resize(ref Data.Structure.FormCR, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.FormCR[CommandIndex1] = ObjectManager.LoadStaticObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, true, false, false); } } } } } break; case "structure.roofl": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex was omitted or is 0 in " + Command + " argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); CommandIndex1 = 1; } if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex is expected to be non-negativ in " + Command + " argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.RoofL.Length) { Array.Resize(ref Data.Structure.RoofL, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.RoofL[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } } break; case "structure.roofr": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex was omitted or is 0 in " + Command + " argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); CommandIndex1 = 1; } if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex is expected to be non-negativ in " + Command + " argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.RoofR.Length) { Array.Resize(ref Data.Structure.RoofR, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.RoofR[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } } break; case "structure.roofcl": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex was omitted or is 0 in " + Command + " argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); CommandIndex1 = 1; } if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex is expected to be non-negativ in " + Command + " argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.RoofCL.Length) { Array.Resize(ref Data.Structure.RoofCL, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.RoofCL[CommandIndex1] = ObjectManager.LoadStaticObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, true, false, false); } } } } } } break; case "structure.roofcr": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex was omitted or is 0 in " + Command + " argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); CommandIndex1 = 1; } if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex is expected to be non-negativ in " + Command + " argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.RoofCR.Length) { Array.Resize(ref Data.Structure.RoofCR, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.RoofCR[CommandIndex1] = ObjectManager.LoadStaticObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, true, false, false); } } } } } } break; case "structure.crackl": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "CrackStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.CrackL.Length) { Array.Resize(ref Data.Structure.CrackL, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.CrackL[CommandIndex1] = ObjectManager.LoadStaticObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, true, false, false); } } } } } break; case "structure.crackr": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "CrackStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.CrackR.Length) { Array.Resize(ref Data.Structure.CrackR, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.CrackR[CommandIndex1] = ObjectManager.LoadStaticObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, true, false, false); } } } } } break; case "structure.freeobj": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "FreeObjStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Structure.FreeObj.Length) { Array.Resize(ref Data.Structure.FreeObj, CommandIndex1 + 1); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " could not be found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Structure.FreeObj[CommandIndex1] = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } } } } break; // signal case "signal": { if (!PreviewOnly) { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have between 1 and 2 arguments at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.SignalData.Length) { Array.Resize(ref Data.SignalData, CommandIndex1 + 1); } if (Arguments[0].EndsWith(".animated", StringComparison.OrdinalIgnoreCase)) { if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "AnimatedObjectFile contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length > 1) { Interface.AddMessage(Interface.MessageType.Warning, false, Command + " is expected to have exactly 1 argument when using animated objects at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "SignalFileWithoutExtension " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { ObjectManager.UnifiedObject Object = ObjectManager.LoadObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); if (Object is ObjectManager.AnimatedObjectCollection) { AnimatedObjectSignalData Signal = new AnimatedObjectSignalData(); Signal.Objects = (ObjectManager.AnimatedObjectCollection)Object; Data.SignalData[CommandIndex1] = Signal; } else { Interface.AddMessage(Interface.MessageType.Error, true, "GlowFileWithoutExtension " + f + " is not a valid animated object in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } } } } else { if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "SignalFileWithoutExtension contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Arguments.Length > 2) { Interface.AddMessage(Interface.MessageType.Warning, false, Command + " is expected to have between 1 and 2 arguments at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } string f = System.IO.Path.Combine(ObjectPath, Arguments[0]); Bve4SignalData Signal = new Bve4SignalData(); Signal.BaseObject = ObjectManager.LoadStaticObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); Signal.GlowObject = null; string Folder = System.IO.Path.GetDirectoryName(f); if (!System.IO.Directory.Exists(Folder)) { Interface.AddMessage(Interface.MessageType.Error, true, "The folder " + Folder + " could not be found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Signal.SignalTextures = LoadAllTextures(f, false); Signal.GlowTextures = new Textures.Texture[] { }; if (Arguments.Length >= 2 && Arguments[1].Length != 0) { if (Interface.ContainsInvalidPathChars(Arguments[1])) { Interface.AddMessage(Interface.MessageType.Error, false, "GlowFileWithoutExtension contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { f = System.IO.Path.Combine(ObjectPath, Arguments[1]); Signal.GlowObject = ObjectManager.LoadStaticObject(f, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); if (Signal.GlowObject != null) { Signal.GlowTextures = LoadAllTextures(f, true); for (int p = 0; p < Signal.GlowObject.Mesh.Materials.Length; p++) { Signal.GlowObject.Mesh.Materials[p].BlendMode = World.MeshMaterialBlendMode.Additive; Signal.GlowObject.Mesh.Materials[p].GlowAttenuationData = World.GetGlowAttenuationData(200.0, World.GlowAttenuationMode.DivisionExponent4); } } } } Data.SignalData[CommandIndex1] = Signal; } } } } } } break; // texture case "texture.background": case "structure.back": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "BackgroundTextureIndex is expected to be non-negative at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Backgrounds.Length) { int a = Data.Backgrounds.Length; Array.Resize(ref Data.Backgrounds, CommandIndex1 + 1); for (int k = a; k <= CommandIndex1; k++) { Data.Backgrounds[k] = new World.Background(null, 6, false); } } string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Textures.RegisterTexture(f, out Data.Backgrounds[CommandIndex1].Texture); } } } } } break; case "texture.background.x": case "structure.back.x": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "BackgroundTextureIndex is expected to be non-negative at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Backgrounds.Length) { int a = Data.Backgrounds.Length; Array.Resize(ref Data.Backgrounds, CommandIndex1 + 1); for (int k = a; k <= CommandIndex1; k++) { Data.Backgrounds[k] = new World.Background(null, 6, false); } } int x; if (!Interface.TryParseIntVb6(Arguments[0], out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "BackgroundTextureIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (x == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RepetitionCount is expected to be non-zero in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Backgrounds[CommandIndex1].Repetition = x; } } } } break; case "texture.background.aspect": case "structure.back.aspect": { if (!PreviewOnly) { if (CommandIndex1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "BackgroundTextureIndex is expected to be non-negative at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (CommandIndex1 >= Data.Backgrounds.Length) { int a = Data.Backgrounds.Length; Array.Resize(ref Data.Backgrounds, CommandIndex1 + 1); for (int k = a; k <= CommandIndex1; k++) { Data.Backgrounds[k] = new World.Background(null, 6, false); } } int aspect; if (!Interface.TryParseIntVb6(Arguments[0], out aspect)) { Interface.AddMessage(Interface.MessageType.Error, false, "BackgroundTextureIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (aspect != 0 & aspect != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be either 0 or 1 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Backgrounds[CommandIndex1].KeepAspectRatio = aspect == 1; } } } } break; // cycle case "cycle.ground": if (!PreviewOnly) { if (CommandIndex1 >= Data.Structure.Cycle.Length) { Array.Resize(ref Data.Structure.Cycle, CommandIndex1 + 1); } Data.Structure.Cycle[CommandIndex1] = new int[Arguments.Length]; for (int k = 0; k < Arguments.Length; k++) { int ix = 0; if (Arguments[k].Length > 0 && !Interface.TryParseIntVb6(Arguments[k], out ix)) { Interface.AddMessage(Interface.MessageType.Error, false, "GroundStructureIndex" + (k + 1).ToString(Culture) + " is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); ix = 0; } if (ix < 0 | ix >= Data.Structure.Ground.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "GroundStructureIndex" + (k + 1).ToString(Culture) + " is out of range in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); ix = 0; } Data.Structure.Cycle[CommandIndex1][k] = ix; } } break; } } } } } // process track namespace for (int j = 0; j < Expressions.Length; j++) { Loading.RouteProgress = 0.3333 + (double)j * progressFactor; if ((j & 255) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } if (Expressions[j].Text.StartsWith("[") & Expressions[j].Text.EndsWith("]")) { Section = Expressions[j].Text.Substring(1, Expressions[j].Text.Length - 2).Trim(); if (string.Compare(Section, "object", StringComparison.OrdinalIgnoreCase) == 0) { Section = "Structure"; } else if (string.Compare(Section, "railway", StringComparison.OrdinalIgnoreCase) == 0) { Section = "Track"; } SectionAlwaysPrefix = true; } else { // find equals int Equals = Expressions[j].Text.IndexOf('='); if (Equals >= 0) { // handle RW cycle syntax string t = Expressions[j].Text.Substring(0, Equals); if (Section.ToLowerInvariant() == "cycle" & SectionAlwaysPrefix) { double b; if (Interface.TryParseDoubleVb6(t, out b)) { t = ".Ground(" + t + ")"; } } else if (Section.ToLowerInvariant() == "signal" & SectionAlwaysPrefix) { double b; if (Interface.TryParseDoubleVb6(t, out b)) { t = ".Void(" + t + ")"; } } // convert RW style into CSV style Expressions[j].Text = t + " " + Expressions[j].Text.Substring(Equals + 1); } // separate command and arguments string Command, ArgumentSequence; SeparateCommandsAndArguments(Expressions[j], out Command, out ArgumentSequence, Culture, Expressions[j].File, j, false); // process command double Number; bool NumberCheck = !IsRW || string.Compare(Section, "track", StringComparison.OrdinalIgnoreCase) == 0; if (NumberCheck && Interface.TryParseDouble(Command, UnitOfLength, out Number)) { // track position if (ArgumentSequence.Length != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "A track position must not contain any arguments at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Number < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Negative track position encountered at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.TrackPosition = Number; BlockIndex = (int)Math.Floor(Number / Data.BlockInterval + 0.001); if (Data.FirstUsedBlock == -1) Data.FirstUsedBlock = BlockIndex; CreateMissingBlocks(ref Data, ref BlocksUsed, BlockIndex, PreviewOnly); } } else { // split arguments string[] Arguments; { int n = 0; for (int k = 0; k < ArgumentSequence.Length; k++) { if (IsRW & ArgumentSequence[k] == ',') { n++; } else if (ArgumentSequence[k] == ';') { n++; } } Arguments = new string[n + 1]; int a = 0, h = 0; for (int k = 0; k < ArgumentSequence.Length; k++) { if (IsRW & ArgumentSequence[k] == ',') { Arguments[h] = ArgumentSequence.Substring(a, k - a).Trim(); a = k + 1; h++; } else if (ArgumentSequence[k] == ';') { Arguments[h] = ArgumentSequence.Substring(a, k - a).Trim(); a = k + 1; h++; } } if (ArgumentSequence.Length - a > 0) { Arguments[h] = ArgumentSequence.Substring(a).Trim(); h++; } Array.Resize(ref Arguments, h); } // preprocess command if (Command.ToLowerInvariant() == "with") { if (Arguments.Length >= 1) { Section = Arguments[0]; SectionAlwaysPrefix = false; } else { Section = ""; SectionAlwaysPrefix = false; } Command = null; } else { if (Command.StartsWith(".")) { Command = Section + Command; } else if (SectionAlwaysPrefix) { Command = Section + "." + Command; } Command = Command.Replace(".Void", ""); if (Command.StartsWith("structure", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".load", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 5).TrimEnd(); } else if (Command.StartsWith("texture.background", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".load", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 5).TrimEnd(); } else if (Command.StartsWith("texture.background", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".x", StringComparison.OrdinalIgnoreCase)) { Command = "texture.background.x" + Command.Substring(18, Command.Length - 20).TrimEnd(); } else if (Command.StartsWith("texture.background", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".aspect", StringComparison.OrdinalIgnoreCase)) { Command = "texture.background.aspect" + Command.Substring(18, Command.Length - 25).TrimEnd(); } else if (Command.StartsWith("structure.back", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".x", StringComparison.OrdinalIgnoreCase)) { Command = "texture.background.x" + Command.Substring(14, Command.Length - 16).TrimEnd(); } else if (Command.StartsWith("structure.back", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".aspect", StringComparison.OrdinalIgnoreCase)) { Command = "texture.background.aspect" + Command.Substring(14, Command.Length - 21).TrimEnd(); } else if (Command.StartsWith("cycle", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".params", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 7).TrimEnd(); } else if (Command.StartsWith("signal", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".load", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 5).TrimEnd(); } else if (Command.StartsWith("train.run", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".set", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 4).TrimEnd(); } else if (Command.StartsWith("train.flange", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".set", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 4).TrimEnd(); } else if (Command.StartsWith("train.timetable", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".day.load", StringComparison.OrdinalIgnoreCase)) { Command = "train.timetable.day" + Command.Substring(15, Command.Length - 24).Trim(); } else if (Command.StartsWith("train.timetable", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".night.load", StringComparison.OrdinalIgnoreCase)) { Command = "train.timetable.night" + Command.Substring(15, Command.Length - 26).Trim(); } else if (Command.StartsWith("train.timetable", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".day", StringComparison.OrdinalIgnoreCase)) { Command = "train.timetable.day" + Command.Substring(15, Command.Length - 19).Trim(); } else if (Command.StartsWith("train.timetable", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".night", StringComparison.OrdinalIgnoreCase)) { Command = "train.timetable.night" + Command.Substring(15, Command.Length - 21).Trim(); } else if (Command.StartsWith("route.signal", StringComparison.OrdinalIgnoreCase) & Command.EndsWith(".set", StringComparison.OrdinalIgnoreCase)) { Command = Command.Substring(0, Command.Length - 4).TrimEnd(); } } // handle indices int CommandIndex1 = 0, CommandIndex2 = 0; if (Command != null && Command.EndsWith(")")) { for (int k = Command.Length - 2; k >= 0; k--) { if (Command[k] == '(') { string Indices = Command.Substring(k + 1, Command.Length - k - 2).TrimStart(); Command = Command.Substring(0, k).TrimEnd(); int h = Indices.IndexOf(";"); if (h >= 0) { string a = Indices.Substring(0, h).TrimEnd(); string b = Indices.Substring(h + 1).TrimStart(); if (a.Length > 0 && !Interface.TryParseIntVb6(a, out CommandIndex1)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid first index appeared at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File + "."); Command = null; break; } else if (b.Length > 0 && !Interface.TryParseIntVb6(b, out CommandIndex2)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid second index appeared at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File + "."); Command = null; break; } } else { if (Indices.Length > 0 && !Interface.TryParseIntVb6(Indices, out CommandIndex1)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid index appeared at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File + "."); Command = null; break; } } break; } } } // process command if (Command != null && Command.Length != 0) { switch (Command.ToLowerInvariant()) { // non-track case "options.blocklength": case "options.unitoflength": case "options.unitofspeed": case "options.objectvisibility": case "options.sectionbehavior": case "options.fogbehavior": case "options.cantbehavior": case "route.comment": case "route.image": case "route.timetable": case "route.change": case "route.gauge": case "train.gauge": case "route.signal": case "route.runinterval": case "train.interval": case "route.accelerationduetogravity": case "route.elevation": case "route.temperature": case "route.pressure": case "route.ambientlight": case "route.directionallight": case "route.lightdirection": case "route.developerid": case "train.folder": case "train.file": case "train.run": case "train.rail": case "train.flange": case "train.timetable.day": case "train.timetable.night": case "train.velocity": case "train.acceleration": case "train.station": case "structure.rail": case "structure.beacon": case "structure.pole": case "structure.ground": case "structure.walll": case "structure.wallr": case "structure.dikel": case "structure.diker": case "structure.forml": case "structure.formr": case "structure.formcl": case "structure.formcr": case "structure.roofl": case "structure.roofr": case "structure.roofcl": case "structure.roofcr": case "structure.crackl": case "structure.crackr": case "structure.freeobj": case "signal": case "texture.background": case "structure.back": case "structure.back.x": case "structure.back.aspect": case "texture.background.x": case "texture.background.aspect": case "cycle.ground": break; // track case "track.railstart": case "track.rail": { if (!PreviewOnly) { int idx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (idx < 1) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is expected to be positive in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (string.Compare(Command, "track.railstart", StringComparison.OrdinalIgnoreCase) == 0) { if (idx < Data.Blocks[BlockIndex].Rail.Length && Data.Blocks[BlockIndex].Rail[idx].RailStart) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is required to reference a non-existing rail in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } } if (Data.Blocks[BlockIndex].Rail.Length <= idx) { Array.Resize(ref Data.Blocks[BlockIndex].Rail, idx + 1); } if (Data.Blocks[BlockIndex].Rail[idx].RailStartRefreshed) { Data.Blocks[BlockIndex].Rail[idx].RailEnd = true; } { Data.Blocks[BlockIndex].Rail[idx].RailStart = true; Data.Blocks[BlockIndex].Rail[idx].RailStartRefreshed = true; if (Arguments.Length >= 2) { if (Arguments[1].Length > 0) { double x; if (!Interface.TryParseDoubleVb6(Arguments[1], UnitOfLength, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); x = 0.0; } Data.Blocks[BlockIndex].Rail[idx].RailStartX = x; } if (!Data.Blocks[BlockIndex].Rail[idx].RailEnd) { Data.Blocks[BlockIndex].Rail[idx].RailEndX = Data.Blocks[BlockIndex].Rail[idx].RailStartX; } } if (Arguments.Length >= 3) { if (Arguments[2].Length > 0) { double y; if (!Interface.TryParseDoubleVb6(Arguments[2], UnitOfLength, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); y = 0.0; } Data.Blocks[BlockIndex].Rail[idx].RailStartY = y; } if (!Data.Blocks[BlockIndex].Rail[idx].RailEnd) { Data.Blocks[BlockIndex].Rail[idx].RailEndY = Data.Blocks[BlockIndex].Rail[idx].RailStartY; } } if (Data.Blocks[BlockIndex].RailType.Length <= idx) { Array.Resize(ref Data.Blocks[BlockIndex].RailType, idx + 1); } if (Arguments.Length >= 4 && Arguments[3].Length != 0) { int sttype; if (!Interface.TryParseIntVb6(Arguments[3], out sttype)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailStructureIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); sttype = 0; } if (sttype < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailStructureIndex is expected to be non-negative in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (sttype >= Data.Structure.Rail.Length || Data.Structure.Rail[sttype] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RailStructureIndex references an object not loaded in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Blocks[BlockIndex].RailType[idx] = sttype; } } } } } } break; case "track.railend": { if (!PreviewOnly) { int idx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (idx < 0 || idx >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx].RailStart) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex references a non-existing rail in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Data.Blocks[BlockIndex].RailType.Length <= idx) { Array.Resize(ref Data.Blocks[BlockIndex].Rail, idx + 1); } Data.Blocks[BlockIndex].Rail[idx].RailStart = false; Data.Blocks[BlockIndex].Rail[idx].RailStartRefreshed = false; Data.Blocks[BlockIndex].Rail[idx].RailEnd = true; if (Arguments.Length >= 2 && Arguments[1].Length > 0) { double x; if (!Interface.TryParseDoubleVb6(Arguments[1], UnitOfLength, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); x = 0.0; } Data.Blocks[BlockIndex].Rail[idx].RailEndX = x; } if (Arguments.Length >= 3 && Arguments[2].Length > 0) { double y; if (!Interface.TryParseDoubleVb6(Arguments[2], UnitOfLength, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); y = 0.0; } Data.Blocks[BlockIndex].Rail[idx].RailEndY = y; } } } } break; case "track.railtype": { if (!PreviewOnly) { int idx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } int sttype = 0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out sttype)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailStructureIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); sttype = 0; } if (idx < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx].RailStart) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex could be out of range in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } if (sttype < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailStructureIndex is expected to be non-negativ in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (sttype >= Data.Structure.Rail.Length || Data.Structure.Rail[sttype] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RailStructureIndex references an object not loaded in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Data.Blocks[BlockIndex].RailType.Length <= idx) { Array.Resize(ref Data.Blocks[BlockIndex].RailType, idx + 1); } Data.Blocks[BlockIndex].RailType[idx] = sttype; } } } } break; case "track.accuracy": { double r = 2.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); r = 2.0; } if (r < 0.0) { r = 0.0; } else if (r > 4.0) { r = 4.0; } Data.Blocks[BlockIndex].Accuracy = r; } break; case "track.pitch": { double p = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out p)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPermille is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); p = 0.0; } Data.Blocks[BlockIndex].Pitch = 0.001 * p; } break; case "track.curve": { double radius = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], UnitOfLength, out radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "Radius is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); radius = 0.0; } double cant = 0.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out cant)) { Interface.AddMessage(Interface.MessageType.Error, false, "CantInMillimeters is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); cant = 0.0; } else { cant *= 0.001; } if (Data.SignedCant) { if (radius != 0.0) { cant *= (double)Math.Sign(radius); } } else { cant = Math.Abs(cant) * (double)Math.Sign(radius); } Data.Blocks[BlockIndex].CurrentTrackState.CurveRadius = radius; Data.Blocks[BlockIndex].CurrentTrackState.CurveCant = cant; Data.Blocks[BlockIndex].CurrentTrackState.CurveCantTangent = 0.0; } break; case "track.turn": { double s = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out s)) { Interface.AddMessage(Interface.MessageType.Error, false, "Ratio is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); s = 0.0; } Data.Blocks[BlockIndex].Turn = s; } break; case "track.adhesion": { double a = 100.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); a = 100.0; } if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); a = 100.0; } Data.Blocks[BlockIndex].AdhesionMultiplier = 0.01 * a; } break; case "track.brightness": { if (!PreviewOnly) { float value = 255.0f; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseFloatVb6(Arguments[0], out value)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); value = 255.0f; } value /= 255.0f; if (value < 0.0f) value = 0.0f; if (value > 1.0f) value = 1.0f; int n = Data.Blocks[BlockIndex].Brightness.Length; Array.Resize(ref Data.Blocks[BlockIndex].Brightness, n + 1); Data.Blocks[BlockIndex].Brightness[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Brightness[n].Value = value; } } break; case "track.fog": { if (!PreviewOnly) { double start = 0.0, end = 0.0; int r = 128, g = 128, b = 128; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out start)) { Interface.AddMessage(Interface.MessageType.Error, false, "StartingDistance is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); start = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out end)) { Interface.AddMessage(Interface.MessageType.Error, false, "EndingDistance is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); end = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); r = 128; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseIntVb6(Arguments[3], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); g = 128; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Interface.TryParseIntVb6(Arguments[4], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); b = 128; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); b = b < 0 ? 0 : 255; } if (start < end) { Data.Blocks[BlockIndex].Fog.Start = (float)start; Data.Blocks[BlockIndex].Fog.End = (float)end; } else { Data.Blocks[BlockIndex].Fog.Start = Game.NoFogStart; Data.Blocks[BlockIndex].Fog.End = Game.NoFogEnd; } Data.Blocks[BlockIndex].Fog.Color = new Color24((byte)r, (byte)g, (byte)b); Data.Blocks[BlockIndex].FogDefined = true; } } break; case "track.section": case "track.sections": { if (!PreviewOnly) { if (Arguments.Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "At least one argument is required in " + Command + "at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { int[] aspects = new int[Arguments.Length]; for (int i = 0; i < Arguments.Length; i++) { if (!Interface.TryParseIntVb6(Arguments[i], out aspects[i])) { Interface.AddMessage(Interface.MessageType.Error, false, "Aspect" + i.ToString(Culture) + " is invalid in " + Command + "at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); aspects[i] = -1; } else if (aspects[i] < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Aspect" + i.ToString(Culture) + " is expected to be non-negative in " + Command + "at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); aspects[i] = -1; } } bool valueBased = ValueBasedSections | string.Equals(Command, "Track.SectionS", StringComparison.OrdinalIgnoreCase); if (valueBased) { Array.Sort(aspects); } int n = Data.Blocks[BlockIndex].Section.Length; Array.Resize
(ref Data.Blocks[BlockIndex].Section, n + 1); Data.Blocks[BlockIndex].Section[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Section[n].Aspects = aspects; Data.Blocks[BlockIndex].Section[n].Type = valueBased ? Game.SectionType.ValueBased : Game.SectionType.IndexBased; Data.Blocks[BlockIndex].Section[n].DepartureStationIndex = -1; if (CurrentStation >= 0 && Game.Stations[CurrentStation].ForceStopSignal) { if (CurrentStation >= 0 & CurrentStop >= 0 & !DepartureSignalUsed) { Data.Blocks[BlockIndex].Section[n].DepartureStationIndex = CurrentStation; DepartureSignalUsed = true; } } CurrentSection++; } } } break; case "track.sigf": { if (!PreviewOnly) { int objidx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out objidx)) { Interface.AddMessage(Interface.MessageType.Error, false, "SignalIndex is invalid in Track.SigF at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); objidx = 0; } if (objidx >= 0 & objidx < Data.SignalData.Length && Data.SignalData[objidx] != null) { int section = 0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out section)) { Interface.AddMessage(Interface.MessageType.Error, false, "Section is invalid in Track.SigF at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); section = 0; } double x = 0.0, y = 0.0; if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], UnitOfLength, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in Track.SigF at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); x = 0.0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[3], UnitOfLength, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in Track.SigF at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); y = 0.0; } double yaw = 0.0, pitch = 0.0, roll = 0.0; if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[4], out yaw)) { Interface.AddMessage(Interface.MessageType.Error, false, "Yaw is invalid in Track.SigF at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); yaw = 0.0; } if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[5], out pitch)) { Interface.AddMessage(Interface.MessageType.Error, false, "Pitch is invalid in Track.SigF at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); pitch = 0.0; } if (Arguments.Length >= 7 && Arguments[6].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[6], out roll)) { Interface.AddMessage(Interface.MessageType.Error, false, "Roll is invalid in Track.SigF at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); roll = 0.0; } int n = Data.Blocks[BlockIndex].Signal.Length; Array.Resize(ref Data.Blocks[BlockIndex].Signal, n + 1); Data.Blocks[BlockIndex].Signal[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Signal[n].Section = CurrentSection + section; Data.Blocks[BlockIndex].Signal[n].SignalCompatibilityObjectIndex = -1; Data.Blocks[BlockIndex].Signal[n].SignalObjectIndex = objidx; Data.Blocks[BlockIndex].Signal[n].X = x; Data.Blocks[BlockIndex].Signal[n].Y = y < 0.0 ? 4.8 : y; Data.Blocks[BlockIndex].Signal[n].Yaw = 0.0174532925199433 * yaw; Data.Blocks[BlockIndex].Signal[n].Pitch = 0.0174532925199433 * pitch; Data.Blocks[BlockIndex].Signal[n].Roll = 0.0174532925199433 * roll; Data.Blocks[BlockIndex].Signal[n].ShowObject = true; Data.Blocks[BlockIndex].Signal[n].ShowPost = y < 0.0; Data.Blocks[BlockIndex].Signal[n].GameSignalIndex = -1; } else { Interface.AddMessage(Interface.MessageType.Error, false, "SignalIndex references a signal object not loaded in Track.SigF at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } } } break; case "track.signal": case "track.sig": { if (!PreviewOnly) { int num = -2; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out num)) { Interface.AddMessage(Interface.MessageType.Error, false, "Aspects is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); num = -2; } if (num != -2 & num != 2 & num != 3 & num != -4 & num != 4 & num != -5 & num != 5 & num != 6) { Interface.AddMessage(Interface.MessageType.Error, false, "Aspects has an unsupported value in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); num = num == -3 | num == -6 ? -num : -4; } double x = 0.0, y = 0.0; if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], UnitOfLength, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); x = 0.0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[3], UnitOfLength, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); y = 0.0; } double yaw = 0.0, pitch = 0.0, roll = 0.0; if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[4], out yaw)) { Interface.AddMessage(Interface.MessageType.Error, false, "Yaw is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); yaw = 0.0; } if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[5], out pitch)) { Interface.AddMessage(Interface.MessageType.Error, false, "Pitch is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); pitch = 0.0; } if (Arguments.Length >= 7 && Arguments[6].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[6], out roll)) { Interface.AddMessage(Interface.MessageType.Error, false, "Roll is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); roll = 0.0; } int[] aspects; int comp; switch (num) { case 2: aspects = new int[] { 0, 2 }; comp = 0; break; case -2: aspects = new int[] { 0, 4 }; comp = 1; break; case 3: aspects = new int[] { 0, 2, 4 }; comp = 2; break; case 4: aspects = new int[] { 0, 1, 2, 4 }; comp = 3; break; case -4: aspects = new int[] { 0, 2, 3, 4 }; comp = 4; break; case 5: aspects = new int[] { 0, 1, 2, 3, 4 }; comp = 5; break; case -5: aspects = new int[] { 0, 2, 3, 4, 5 }; comp = 6; break; case 6: aspects = new int[] { 0, 1, 2, 3, 4, 5 }; comp = 7; break; default: aspects = new int[] { 0, 2 }; comp = 0; break; } int n = Data.Blocks[BlockIndex].Section.Length; Array.Resize
(ref Data.Blocks[BlockIndex].Section, n + 1); Data.Blocks[BlockIndex].Section[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Section[n].Aspects = aspects; Data.Blocks[BlockIndex].Section[n].DepartureStationIndex = -1; Data.Blocks[BlockIndex].Section[n].Invisible = x == 0.0; Data.Blocks[BlockIndex].Section[n].Type = Game.SectionType.ValueBased; if (CurrentStation >= 0 && Game.Stations[CurrentStation].ForceStopSignal) { if (CurrentStation >= 0 & CurrentStop >= 0 & !DepartureSignalUsed) { Data.Blocks[BlockIndex].Section[n].DepartureStationIndex = CurrentStation; DepartureSignalUsed = true; } } CurrentSection++; n = Data.Blocks[BlockIndex].Signal.Length; Array.Resize(ref Data.Blocks[BlockIndex].Signal, n + 1); Data.Blocks[BlockIndex].Signal[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Signal[n].Section = CurrentSection; Data.Blocks[BlockIndex].Signal[n].SignalCompatibilityObjectIndex = comp; Data.Blocks[BlockIndex].Signal[n].SignalObjectIndex = -1; Data.Blocks[BlockIndex].Signal[n].X = x; Data.Blocks[BlockIndex].Signal[n].Y = y < 0.0 ? 4.8 : y; Data.Blocks[BlockIndex].Signal[n].Yaw = 0.0174532925199433 * yaw; Data.Blocks[BlockIndex].Signal[n].Pitch = 0.0174532925199433 * pitch; Data.Blocks[BlockIndex].Signal[n].Roll = 0.0174532925199433 * roll; Data.Blocks[BlockIndex].Signal[n].ShowObject = x != 0.0; Data.Blocks[BlockIndex].Signal[n].ShowPost = x != 0.0 & y < 0.0; Data.Blocks[BlockIndex].Signal[n].GameSignalIndex = -1; } } break; case "track.relay": { if (!PreviewOnly) { double x = 0.0, y = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], UnitOfLength, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in Track.Relay at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); x = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], UnitOfLength, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in Track.Relay at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); y = 0.0; } double yaw = 0.0, pitch = 0.0, roll = 0.0; if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], out yaw)) { Interface.AddMessage(Interface.MessageType.Error, false, "Yaw is invalid in Track.Relay at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); yaw = 0.0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[3], out pitch)) { Interface.AddMessage(Interface.MessageType.Error, false, "Pitch is invalid in Track.Relay at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); pitch = 0.0; } if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[4], out roll)) { Interface.AddMessage(Interface.MessageType.Error, false, "Roll is invalid in Track.Relay at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); roll = 0.0; } int n = Data.Blocks[BlockIndex].Signal.Length; Array.Resize(ref Data.Blocks[BlockIndex].Signal, n + 1); Data.Blocks[BlockIndex].Signal[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Signal[n].Section = CurrentSection + 1; Data.Blocks[BlockIndex].Signal[n].SignalCompatibilityObjectIndex = 8; Data.Blocks[BlockIndex].Signal[n].SignalObjectIndex = -1; Data.Blocks[BlockIndex].Signal[n].X = x; Data.Blocks[BlockIndex].Signal[n].Y = y < 0.0 ? 4.8 : y; Data.Blocks[BlockIndex].Signal[n].Yaw = yaw * 0.0174532925199433; Data.Blocks[BlockIndex].Signal[n].Pitch = pitch * 0.0174532925199433; Data.Blocks[BlockIndex].Signal[n].Roll = roll * 0.0174532925199433; Data.Blocks[BlockIndex].Signal[n].ShowObject = x != 0.0; Data.Blocks[BlockIndex].Signal[n].ShowPost = x != 0.0 & y < 0.0; } } break; case "track.beacon": { if (!PreviewOnly) { int type = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out type)) { Interface.AddMessage(Interface.MessageType.Error, false, "Type is invalid in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); type = 0; } if (type < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Type is expected to be non-positive in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { int structure = 0, section = 0, optional = 0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out structure)) { Interface.AddMessage(Interface.MessageType.Error, false, "BeaconStructureIndex is invalid in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); structure = 0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out section)) { Interface.AddMessage(Interface.MessageType.Error, false, "Section is invalid in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); section = 0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseIntVb6(Arguments[3], out optional)) { Interface.AddMessage(Interface.MessageType.Error, false, "Data is invalid in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); optional = 0; } if (structure < -1) { Interface.AddMessage(Interface.MessageType.Error, false, "BeaconStructureIndex is expected to be non-negative or -1 in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); structure = -1; } else if (structure >= 0 && (structure >= Data.Structure.Beacon.Length || Data.Structure.Beacon[structure] == null)) { Interface.AddMessage(Interface.MessageType.Error, false, "BeaconStructureIndex references an object not loaded in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); structure = -1; } if (section == -1) { //section = (int)TrackManager.TransponderSpecialSection.NextRedSection; } else if (section < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Section is expected to be non-negative or -1 in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); section = CurrentSection + 1; } else { section += CurrentSection; } double x = 0.0, y = 0.0; double yaw = 0.0, pitch = 0.0, roll = 0.0; if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[4], UnitOfLength, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); x = 0.0; } if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[5], UnitOfLength, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); y = 0.0; } if (Arguments.Length >= 7 && Arguments[6].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[6], out yaw)) { Interface.AddMessage(Interface.MessageType.Error, false, "Yaw is invalid in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); yaw = 0.0; } if (Arguments.Length >= 8 && Arguments[7].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[7], out pitch)) { Interface.AddMessage(Interface.MessageType.Error, false, "Pitch is invalid in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); pitch = 0.0; } if (Arguments.Length >= 9 && Arguments[8].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[8], out roll)) { Interface.AddMessage(Interface.MessageType.Error, false, "Roll is invalid in Track.Beacon at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); roll = 0.0; } int n = Data.Blocks[BlockIndex].Transponder.Length; Array.Resize(ref Data.Blocks[BlockIndex].Transponder, n + 1); Data.Blocks[BlockIndex].Transponder[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Transponder[n].Type = type; Data.Blocks[BlockIndex].Transponder[n].Data = optional; Data.Blocks[BlockIndex].Transponder[n].BeaconStructureIndex = structure; Data.Blocks[BlockIndex].Transponder[n].Section = section; Data.Blocks[BlockIndex].Transponder[n].ShowDefaultObject = false; Data.Blocks[BlockIndex].Transponder[n].X = x; Data.Blocks[BlockIndex].Transponder[n].Y = y; Data.Blocks[BlockIndex].Transponder[n].Yaw = yaw * 0.0174532925199433; Data.Blocks[BlockIndex].Transponder[n].Pitch = pitch * 0.0174532925199433; Data.Blocks[BlockIndex].Transponder[n].Roll = roll * 0.0174532925199433; } } } break; case "track.transponder": case "track.tr": { if (!PreviewOnly) { int type = 0, oversig = 0, work = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out type)) { Interface.AddMessage(Interface.MessageType.Error, false, "Type is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); type = 0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out oversig)) { Interface.AddMessage(Interface.MessageType.Error, false, "Signals is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); oversig = 0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out work)) { Interface.AddMessage(Interface.MessageType.Error, false, "SwitchSystems is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); work = 0; } if (oversig < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Signals is expected to be non-negative in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); oversig = 0; } double x = 0.0, y = 0.0; if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[3], UnitOfLength, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); x = 0.0; } if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[4], UnitOfLength, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); y = 0.0; } double yaw = 0.0, pitch = 0.0, roll = 0.0; if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[5], out yaw)) { Interface.AddMessage(Interface.MessageType.Error, false, "Yaw is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); yaw = 0.0; } if (Arguments.Length >= 7 && Arguments[6].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[6], out pitch)) { Interface.AddMessage(Interface.MessageType.Error, false, "Pitch is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); pitch = 0.0; } if (Arguments.Length >= 8 && Arguments[7].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[7], out roll)) { Interface.AddMessage(Interface.MessageType.Error, false, "Roll is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); roll = 0.0; } int n = Data.Blocks[BlockIndex].Transponder.Length; Array.Resize(ref Data.Blocks[BlockIndex].Transponder, n + 1); Data.Blocks[BlockIndex].Transponder[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Transponder[n].Type = type; Data.Blocks[BlockIndex].Transponder[n].Data = work; Data.Blocks[BlockIndex].Transponder[n].ShowDefaultObject = true; Data.Blocks[BlockIndex].Transponder[n].BeaconStructureIndex = -1; Data.Blocks[BlockIndex].Transponder[n].X = x; Data.Blocks[BlockIndex].Transponder[n].Y = y; Data.Blocks[BlockIndex].Transponder[n].Yaw = yaw * 0.0174532925199433; Data.Blocks[BlockIndex].Transponder[n].Pitch = pitch * 0.0174532925199433; Data.Blocks[BlockIndex].Transponder[n].Roll = roll * 0.0174532925199433; Data.Blocks[BlockIndex].Transponder[n].Section = CurrentSection + oversig + 1; } } break; case "track.atssn": { if (!PreviewOnly) { int n = Data.Blocks[BlockIndex].Transponder.Length; Array.Resize(ref Data.Blocks[BlockIndex].Transponder, n + 1); Data.Blocks[BlockIndex].Transponder[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Transponder[n].Type = 0; Data.Blocks[BlockIndex].Transponder[n].Data = 0; Data.Blocks[BlockIndex].Transponder[n].ShowDefaultObject = true; Data.Blocks[BlockIndex].Transponder[n].BeaconStructureIndex = -1; Data.Blocks[BlockIndex].Transponder[n].Section = CurrentSection + 1; } } break; case "track.atsp": { if (!PreviewOnly) { int n = Data.Blocks[BlockIndex].Transponder.Length; Array.Resize(ref Data.Blocks[BlockIndex].Transponder, n + 1); Data.Blocks[BlockIndex].Transponder[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Transponder[n].Type = 3; Data.Blocks[BlockIndex].Transponder[n].Data = 0; Data.Blocks[BlockIndex].Transponder[n].ShowDefaultObject = true; Data.Blocks[BlockIndex].Transponder[n].BeaconStructureIndex = -1; Data.Blocks[BlockIndex].Transponder[n].Section = CurrentSection + 1; } } break; case "track.pattern": { if (!PreviewOnly) { int type = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out type)) { Interface.AddMessage(Interface.MessageType.Error, false, "Type is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); type = 0; } double speed = 0.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out speed)) { Interface.AddMessage(Interface.MessageType.Error, false, "Speed is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); speed = 0.0; } int n = Data.Blocks[BlockIndex].Transponder.Length; Array.Resize(ref Data.Blocks[BlockIndex].Transponder, n + 1); Data.Blocks[BlockIndex].Transponder[n].TrackPosition = Data.TrackPosition; if (type == 0) { Data.Blocks[BlockIndex].Transponder[n].Type = TrackManager.SpecialTransponderTypes.InternalAtsPTemporarySpeedLimit; Data.Blocks[BlockIndex].Transponder[n].Data = speed == 0.0 ? int.MaxValue : (int)Math.Round(speed * Data.UnitOfSpeed * 3.6); } else { Data.Blocks[BlockIndex].Transponder[n].Type = TrackManager.SpecialTransponderTypes.AtsPPermanentSpeedLimit; Data.Blocks[BlockIndex].Transponder[n].Data = speed == 0.0 ? int.MaxValue : (int)Math.Round(speed * Data.UnitOfSpeed * 3.6); } Data.Blocks[BlockIndex].Transponder[n].Section = -1; Data.Blocks[BlockIndex].Transponder[n].BeaconStructureIndex = -1; } } break; case "track.plimit": { if (!PreviewOnly) { double speed = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out speed)) { Interface.AddMessage(Interface.MessageType.Error, false, "Speed is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); speed = 0.0; } int n = Data.Blocks[BlockIndex].Transponder.Length; Array.Resize(ref Data.Blocks[BlockIndex].Transponder, n + 1); Data.Blocks[BlockIndex].Transponder[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Transponder[n].Type = TrackManager.SpecialTransponderTypes.AtsPPermanentSpeedLimit; Data.Blocks[BlockIndex].Transponder[n].Data = speed == 0.0 ? int.MaxValue : (int)Math.Round(speed * Data.UnitOfSpeed * 3.6); Data.Blocks[BlockIndex].Transponder[n].Section = -1; Data.Blocks[BlockIndex].Transponder[n].BeaconStructureIndex = -1; } } break; case "track.limit": { double limit = 0.0; int direction = 0, cource = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out limit)) { Interface.AddMessage(Interface.MessageType.Error, false, "Speed is invalid in Track.Limit at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); limit = 0.0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out direction)) { Interface.AddMessage(Interface.MessageType.Error, false, "Direction is invalid in Track.Limit at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); direction = 0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out cource)) { Interface.AddMessage(Interface.MessageType.Error, false, "Cource is invalid in Track.Limit at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); cource = 0; } int n = Data.Blocks[BlockIndex].Limit.Length; Array.Resize(ref Data.Blocks[BlockIndex].Limit, n + 1); Data.Blocks[BlockIndex].Limit[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Limit[n].Speed = limit <= 0.0 ? double.PositiveInfinity : Data.UnitOfSpeed * limit; Data.Blocks[BlockIndex].Limit[n].Direction = direction; Data.Blocks[BlockIndex].Limit[n].Cource = cource; } break; case "track.stop": if (CurrentStation == -1) { Interface.AddMessage(Interface.MessageType.Error, false, "A stop without a station is invalid in Track.Stop at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { int dir = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out dir)) { Interface.AddMessage(Interface.MessageType.Error, false, "Direction is invalid in Track.Stop at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dir = 0; } double backw = 5.0, forw = 5.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], UnitOfLength, out backw)) { Interface.AddMessage(Interface.MessageType.Error, false, "BackwardTolerance is invalid in Track.Stop at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); backw = 5.0; } else if (backw <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "BackwardTolerance is expected to be positive in Track.Stop at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); backw = 5.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], UnitOfLength, out forw)) { Interface.AddMessage(Interface.MessageType.Error, false, "ForwardTolerance is invalid in Track.Stop at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); forw = 5.0; } else if (forw <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ForwardTolerance is expected to be positive in Track.Stop at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); forw = 5.0; } int cars = 0; if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseIntVb6(Arguments[3], out cars)) { Interface.AddMessage(Interface.MessageType.Error, false, "Cars is invalid in Track.Stop at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); cars = 0; } int n = Data.Blocks[BlockIndex].Stop.Length; Array.Resize(ref Data.Blocks[BlockIndex].Stop, n + 1); Data.Blocks[BlockIndex].Stop[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Stop[n].Station = CurrentStation; Data.Blocks[BlockIndex].Stop[n].Direction = dir; Data.Blocks[BlockIndex].Stop[n].ForwardTolerance = forw; Data.Blocks[BlockIndex].Stop[n].BackwardTolerance = backw; Data.Blocks[BlockIndex].Stop[n].Cars = cars; CurrentStop = cars; } break; case "track.sta": { CurrentStation++; Array.Resize(ref Game.Stations, CurrentStation + 1); Game.Stations[CurrentStation].Name = string.Empty; Game.Stations[CurrentStation].StopMode = Game.StationStopMode.AllStop; Game.Stations[CurrentStation].StationType = Game.StationType.Normal; if (Arguments.Length >= 1 && Arguments[0].Length > 0) { Game.Stations[CurrentStation].Name = Arguments[0]; } double arr = -1.0, dep = -1.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0) { if (string.Equals(Arguments[1], "P", StringComparison.OrdinalIgnoreCase) | string.Equals(Arguments[1], "L", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].StopMode = Game.StationStopMode.AllPass; } else if (string.Equals(Arguments[1], "B", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].StopMode = Game.StationStopMode.PlayerPass; } else if (Arguments[1].StartsWith("B:", StringComparison.InvariantCultureIgnoreCase)) { Game.Stations[CurrentStation].StopMode = Game.StationStopMode.PlayerPass; if (!Interface.TryParseTime(Arguments[1].Substring(2).TrimStart(), out arr)) { Interface.AddMessage(Interface.MessageType.Error, false, "ArrivalTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); arr = -1.0; } } else if (string.Equals(Arguments[1], "S", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].StopMode = Game.StationStopMode.PlayerStop; } else if (Arguments[1].StartsWith("S:", StringComparison.InvariantCultureIgnoreCase)) { Game.Stations[CurrentStation].StopMode = Game.StationStopMode.PlayerStop; if (!Interface.TryParseTime(Arguments[1].Substring(2).TrimStart(), out arr)) { Interface.AddMessage(Interface.MessageType.Error, false, "ArrivalTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); arr = -1.0; } } else if (!Interface.TryParseTime(Arguments[1], out arr)) { Interface.AddMessage(Interface.MessageType.Error, false, "ArrivalTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); arr = -1.0; } } if (Arguments.Length >= 3 && Arguments[2].Length > 0) { if (string.Equals(Arguments[2], "T", StringComparison.OrdinalIgnoreCase) | string.Equals(Arguments[2], "=", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].StationType = Game.StationType.Terminal; } else if (Arguments[2].StartsWith("T:", StringComparison.InvariantCultureIgnoreCase)) { Game.Stations[CurrentStation].StationType = Game.StationType.Terminal; if (!Interface.TryParseTime(Arguments[2].Substring(2).TrimStart(), out dep)) { Interface.AddMessage(Interface.MessageType.Error, false, "DepartureTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dep = -1.0; } } else if (string.Equals(Arguments[2], "C", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].StationType = Game.StationType.ChangeEnds; } else if (Arguments[2].StartsWith("C:", StringComparison.InvariantCultureIgnoreCase)) { Game.Stations[CurrentStation].StationType = Game.StationType.ChangeEnds; if (!Interface.TryParseTime(Arguments[2].Substring(2).TrimStart(), out dep)) { Interface.AddMessage(Interface.MessageType.Error, false, "DepartureTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dep = -1.0; } } else if (!Interface.TryParseTime(Arguments[2], out dep)) { Interface.AddMessage(Interface.MessageType.Error, false, "DepartureTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dep = -1.0; } } int passalarm = 0; if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseIntVb6(Arguments[3], out passalarm)) { Interface.AddMessage(Interface.MessageType.Error, false, "PassAlarm is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); passalarm = 0; } int door = 0; bool doorboth = false; if (Arguments.Length >= 5 && Arguments[4].Length != 0) { switch (Arguments[4].ToUpperInvariant()) { case "L": door = -1; break; case "R": door = 1; break; case "N": door = 0; break; case "B": doorboth = true; break; default: if (!Interface.TryParseIntVb6(Arguments[4], out door)) { Interface.AddMessage(Interface.MessageType.Error, false, "Doors is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); door = 0; } break; } } int stop = 0; if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Interface.TryParseIntVb6(Arguments[5], out stop)) { Interface.AddMessage(Interface.MessageType.Error, false, "ForcedRedSignal is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); stop = 0; } int device = 0; if (Arguments.Length >= 7 && Arguments[6].Length > 0) { if (string.Compare(Arguments[6], "ats", StringComparison.OrdinalIgnoreCase) == 0) { device = 0; } else if (string.Compare(Arguments[6], "atc", StringComparison.OrdinalIgnoreCase) == 0) { device = 1; } else if (!Interface.TryParseIntVb6(Arguments[6], out device)) { Interface.AddMessage(Interface.MessageType.Error, false, "System is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); device = 0; } if (device != 0 & device != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "System is not supported in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); device = 0; } } Sounds.SoundBuffer arrsnd = null; Sounds.SoundBuffer depsnd = null; if (!PreviewOnly) { if (Arguments.Length >= 8 && Arguments[7].Length > 0) { if (Interface.ContainsInvalidPathChars(Arguments[7])) { Interface.AddMessage(Interface.MessageType.Error, false, "ArrivalSound contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { string f = OpenBveApi.Path.CombineFile(SoundPath, Arguments[7]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "ArrivalSound " + f + " not found in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { const double radius = 30.0; arrsnd = Sounds.RegisterBuffer(f, radius); } } } } double halt = 15.0; if (Arguments.Length >= 9 && Arguments[8].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[8], out halt)) { Interface.AddMessage(Interface.MessageType.Error, false, "StopDuration is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); halt = 15.0; } else if (halt < 5.0) { halt = 5.0; } double jam = 100.0; if (!PreviewOnly) { if (Arguments.Length >= 10 && Arguments[9].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[9], out jam)) { Interface.AddMessage(Interface.MessageType.Error, false, "PassengerRatio is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); jam = 100.0; } else if (jam < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "PassengerRatio is expected to be non-negative in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); jam = 100.0; } } if (!PreviewOnly) { if (Arguments.Length >= 11 && Arguments[10].Length > 0) { if (Interface.ContainsInvalidPathChars(Arguments[10])) { Interface.AddMessage(Interface.MessageType.Error, false, "DepartureSound contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { string f = OpenBveApi.Path.CombineFile(SoundPath, Arguments[10]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "DepartureSound " + f + " not found in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { const double radius = 30.0; depsnd = Sounds.RegisterBuffer(f, radius); } } } } int ttidx = -1; Textures.Texture tdt = null, tnt = null; if (!PreviewOnly) { if (Arguments.Length >= 12 && Arguments[11].Length > 0) { if (!Interface.TryParseIntVb6(Arguments[11], out ttidx)) { ttidx = -1; } else { if (ttidx < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "TimetableIndex is expected to be non-negative in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); ttidx = -1; } else if (ttidx >= Data.TimetableDaytime.Length & ttidx >= Data.TimetableNighttime.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "TimetableIndex references textures not loaded in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); ttidx = -1; } tdt = ttidx >= 0 & ttidx < Data.TimetableDaytime.Length ? Data.TimetableDaytime[ttidx] : null; tnt = ttidx >= 0 & ttidx < Data.TimetableNighttime.Length ? Data.TimetableNighttime[ttidx] : null; ttidx = 0; } } else { ttidx = -1; } if (ttidx == -1) { if (CurrentStation > 0) { tdt = Game.Stations[CurrentStation - 1].TimetableDaytimeTexture; tnt = Game.Stations[CurrentStation - 1].TimetableNighttimeTexture; } else if (Data.TimetableDaytime.Length > 0 & Data.TimetableNighttime.Length > 0) { tdt = Data.TimetableDaytime[0]; tnt = Data.TimetableNighttime[0]; } else { tdt = null; tnt = null; } } } if (Game.Stations[CurrentStation].Name.Length == 0 & (Game.Stations[CurrentStation].StopMode == Game.StationStopMode.PlayerStop | Game.Stations[CurrentStation].StopMode == Game.StationStopMode.AllStop)) { Game.Stations[CurrentStation].Name = "Station " + (CurrentStation + 1).ToString(Culture) + ")"; } Game.Stations[CurrentStation].ArrivalTime = arr; Game.Stations[CurrentStation].ArrivalSoundBuffer = arrsnd; Game.Stations[CurrentStation].DepartureTime = dep; Game.Stations[CurrentStation].DepartureSoundBuffer = depsnd; Game.Stations[CurrentStation].StopTime = halt; Game.Stations[CurrentStation].ForceStopSignal = stop == 1; Game.Stations[CurrentStation].OpenLeftDoors = door < 0.0 | doorboth; Game.Stations[CurrentStation].OpenRightDoors = door > 0.0 | doorboth; Game.Stations[CurrentStation].SafetySystem = device == 1 ? Game.SafetySystem.Atc : Game.SafetySystem.Ats; Game.Stations[CurrentStation].Stops = new Game.StationStop[] { }; Game.Stations[CurrentStation].PassengerRatio = 0.01 * jam; Game.Stations[CurrentStation].TimetableDaytimeTexture = tdt; Game.Stations[CurrentStation].TimetableNighttimeTexture = tnt; Game.Stations[CurrentStation].DefaultTrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Station = CurrentStation; Data.Blocks[BlockIndex].StationPassAlarm = passalarm == 1; CurrentStop = -1; DepartureSignalUsed = false; } break; case "track.station": { CurrentStation++; Array.Resize(ref Game.Stations, CurrentStation + 1); Game.Stations[CurrentStation].Name = string.Empty; Game.Stations[CurrentStation].StopMode = Game.StationStopMode.AllStop; Game.Stations[CurrentStation].StationType = Game.StationType.Normal; if (Arguments.Length >= 1 && Arguments[0].Length > 0) { Game.Stations[CurrentStation].Name = Arguments[0]; } double arr = -1.0, dep = -1.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0) { if (string.Equals(Arguments[1], "P", StringComparison.OrdinalIgnoreCase) | string.Equals(Arguments[1], "L", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].StopMode = Game.StationStopMode.AllPass; } else if (string.Equals(Arguments[1], "B", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].StopMode = Game.StationStopMode.PlayerPass; } else if (Arguments[1].StartsWith("B:", StringComparison.InvariantCultureIgnoreCase)) { Game.Stations[CurrentStation].StopMode = Game.StationStopMode.PlayerPass; if (!Interface.TryParseTime(Arguments[1].Substring(2).TrimStart(), out arr)) { Interface.AddMessage(Interface.MessageType.Error, false, "ArrivalTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); arr = -1.0; } } else if (string.Equals(Arguments[1], "S", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].StopMode = Game.StationStopMode.PlayerStop; } else if (Arguments[1].StartsWith("S:", StringComparison.InvariantCultureIgnoreCase)) { Game.Stations[CurrentStation].StopMode = Game.StationStopMode.PlayerStop; if (!Interface.TryParseTime(Arguments[1].Substring(2).TrimStart(), out arr)) { Interface.AddMessage(Interface.MessageType.Error, false, "ArrivalTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); arr = -1.0; } } else if (!Interface.TryParseTime(Arguments[1], out arr)) { Interface.AddMessage(Interface.MessageType.Error, false, "ArrivalTime is invalid in Track.Station at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); arr = -1.0; } } if (Arguments.Length >= 3 && Arguments[2].Length > 0) { if (string.Equals(Arguments[2], "T", StringComparison.OrdinalIgnoreCase) | string.Equals(Arguments[2], "=", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].StationType = Game.StationType.Terminal; } else if (Arguments[2].StartsWith("T:", StringComparison.InvariantCultureIgnoreCase)) { Game.Stations[CurrentStation].StationType = Game.StationType.Terminal; if (!Interface.TryParseTime(Arguments[2].Substring(2).TrimStart(), out dep)) { Interface.AddMessage(Interface.MessageType.Error, false, "DepartureTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dep = -1.0; } } else if (string.Equals(Arguments[2], "C", StringComparison.OrdinalIgnoreCase)) { Game.Stations[CurrentStation].StationType = Game.StationType.ChangeEnds; } else if (Arguments[2].StartsWith("C:", StringComparison.InvariantCultureIgnoreCase)) { Game.Stations[CurrentStation].StationType = Game.StationType.ChangeEnds; if (!Interface.TryParseTime(Arguments[2].Substring(2).TrimStart(), out dep)) { Interface.AddMessage(Interface.MessageType.Error, false, "DepartureTime is invalid in Track.Sta at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dep = -1.0; } } else if (!Interface.TryParseTime(Arguments[2], out dep)) { Interface.AddMessage(Interface.MessageType.Error, false, "DepartureTime is invalid in Track.Station at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dep = -1.0; } } int stop = 0; if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseIntVb6(Arguments[3], out stop)) { Interface.AddMessage(Interface.MessageType.Error, false, "ForcedRedSignal is invalid in Track.Station at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); stop = 0; } int device = 0; if (Arguments.Length >= 5 && Arguments[4].Length > 0) { if (string.Compare(Arguments[4], "ats", StringComparison.OrdinalIgnoreCase) == 0) { device = 0; } else if (string.Compare(Arguments[4], "atc", StringComparison.OrdinalIgnoreCase) == 0) { device = 1; } else if (!Interface.TryParseIntVb6(Arguments[4], out device)) { Interface.AddMessage(Interface.MessageType.Error, false, "System is invalid in Track.Station at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); device = 0; } else if (device != 0 & device != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "System is not supported in Track.Station at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); device = 0; } } Sounds.SoundBuffer depsnd = null; if (!PreviewOnly) { if (Arguments.Length >= 6 && Arguments[5].Length != 0) { if (Interface.ContainsInvalidPathChars(Arguments[5])) { Interface.AddMessage(Interface.MessageType.Error, false, "DepartureSound contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { string f = OpenBveApi.Path.CombineFile(SoundPath, Arguments[5]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "DepartureSound " + f + " not found in Track.Station at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { const double radius = 30.0; depsnd = Sounds.RegisterBuffer(f, radius); } } } } if (Game.Stations[CurrentStation].Name.Length == 0 & (Game.Stations[CurrentStation].StopMode == Game.StationStopMode.PlayerStop | Game.Stations[CurrentStation].StopMode == Game.StationStopMode.AllStop)) { Game.Stations[CurrentStation].Name = "Station " + (CurrentStation + 1).ToString(Culture) + ")"; } Game.Stations[CurrentStation].ArrivalTime = arr; Game.Stations[CurrentStation].ArrivalSoundBuffer = null; Game.Stations[CurrentStation].DepartureTime = dep; Game.Stations[CurrentStation].DepartureSoundBuffer = depsnd; Game.Stations[CurrentStation].StopTime = 15.0; Game.Stations[CurrentStation].ForceStopSignal = stop == 1; Game.Stations[CurrentStation].OpenLeftDoors = true; Game.Stations[CurrentStation].OpenRightDoors = true; Game.Stations[CurrentStation].SafetySystem = device == 1 ? Game.SafetySystem.Atc : Game.SafetySystem.Ats; Game.Stations[CurrentStation].Stops = new Game.StationStop[] { }; Game.Stations[CurrentStation].PassengerRatio = 1.0; Game.Stations[CurrentStation].TimetableDaytimeTexture = null; Game.Stations[CurrentStation].TimetableNighttimeTexture = null; Game.Stations[CurrentStation].DefaultTrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].Station = CurrentStation; Data.Blocks[BlockIndex].StationPassAlarm = false; CurrentStop = -1; DepartureSignalUsed = false; } break; case "track.buffer": { if (!PreviewOnly) { int n = Game.BufferTrackPositions.Length; Array.Resize(ref Game.BufferTrackPositions, n + 1); Game.BufferTrackPositions[n] = Data.TrackPosition; } } break; case "track.form": { if (!PreviewOnly) { int idx1 = 0, idx2 = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx1)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex1 is invalid in Track.Form at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx1 = 0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0) { if (string.Compare(Arguments[1], "L", StringComparison.OrdinalIgnoreCase) == 0) { idx2 = Form.SecondaryRailL; } else if (string.Compare(Arguments[1], "R", StringComparison.OrdinalIgnoreCase) == 0) { idx2 = Form.SecondaryRailR; } else if (IsRW && string.Compare(Arguments[1], "9X", StringComparison.OrdinalIgnoreCase) == 0) { idx2 = int.MaxValue; } else if (!Interface.TryParseIntVb6(Arguments[1], out idx2)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex2 is invalid in Track.Form at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx2 = 0; } } if (IsRW) { if (idx2 == int.MaxValue) { idx2 = 9; } else if (idx2 == -9) { idx2 = Form.SecondaryRailL; } else if (idx2 == 9) { idx2 = Form.SecondaryRailR; } } if (idx1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex1 is expected to be non-negative in Track.Form at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (idx2 < 0 & idx2 != Form.SecondaryRailStub & idx2 != Form.SecondaryRailL & idx2 != Form.SecondaryRailR) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex2 is expected to be greater or equal to -2 in Track.Form at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx1 >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx1].RailStart) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex1 could be out of range in Track.Form at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } if (idx2 != Form.SecondaryRailStub & idx2 != Form.SecondaryRailL & idx2 != Form.SecondaryRailR && (idx2 >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx2].RailStart)) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex2 could be out of range in Track.Form at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } int roof = 0, pf = 0; if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out roof)) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex is invalid in Track.Form at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); roof = 0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseIntVb6(Arguments[3], out pf)) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex is invalid in Track.Form at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); pf = 0; } if (roof != 0 & (roof < 0 || (roof >= Data.Structure.RoofL.Length || Data.Structure.RoofL[roof] == null) || (roof >= Data.Structure.RoofR.Length || Data.Structure.RoofR[roof] == null))) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references an object not loaded in Track.Form at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (pf < 0 | (pf >= Data.Structure.FormL.Length || Data.Structure.FormL[pf] == null) & (pf >= Data.Structure.FormR.Length || Data.Structure.FormR[pf] == null)) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references an object not loaded in Track.Form at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } int n = Data.Blocks[BlockIndex].Form.Length; Array.Resize
(ref Data.Blocks[BlockIndex].Form, n + 1); Data.Blocks[BlockIndex].Form[n].PrimaryRail = idx1; Data.Blocks[BlockIndex].Form[n].SecondaryRail = idx2; Data.Blocks[BlockIndex].Form[n].FormType = pf; Data.Blocks[BlockIndex].Form[n].RoofType = roof; } } } } break; case "track.pole": { if (!PreviewOnly) { int idx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in Track.Pole at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (idx < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is expected to be non-negative in Track.Pole at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx].RailStart) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex could be out of range in Track.Pole at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } if (idx >= Data.Blocks[BlockIndex].RailPole.Length) { Array.Resize(ref Data.Blocks[BlockIndex].RailPole, idx + 1); Data.Blocks[BlockIndex].RailPole[idx].Mode = 0; Data.Blocks[BlockIndex].RailPole[idx].Location = 0; Data.Blocks[BlockIndex].RailPole[idx].Interval = 2.0 * Data.BlockInterval; Data.Blocks[BlockIndex].RailPole[idx].Type = 0; } int typ = Data.Blocks[BlockIndex].RailPole[idx].Mode; int sttype = Data.Blocks[BlockIndex].RailPole[idx].Type; if (Arguments.Length >= 2 && Arguments[1].Length > 0) { if (!Interface.TryParseIntVb6(Arguments[1], out typ)) { Interface.AddMessage(Interface.MessageType.Error, false, "AdditionalRailsCovered is invalid in Track.Pole at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); typ = 0; } } if (Arguments.Length >= 3 && Arguments[2].Length > 0) { double loc; if (!Interface.TryParseDoubleVb6(Arguments[2], out loc)) { Interface.AddMessage(Interface.MessageType.Error, false, "Location is invalid in Track.Pole at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); loc = 0.0; } Data.Blocks[BlockIndex].RailPole[idx].Location = loc; } if (Arguments.Length >= 4 && Arguments[3].Length > 0) { double dist; if (!Interface.TryParseDoubleVb6(Arguments[3], UnitOfLength, out dist)) { Interface.AddMessage(Interface.MessageType.Error, false, "Interval is invalid in Track.Pole at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dist = Data.BlockInterval; } Data.Blocks[BlockIndex].RailPole[idx].Interval = dist; } if (Arguments.Length >= 5 && Arguments[4].Length > 0) { if (!Interface.TryParseIntVb6(Arguments[4], out sttype)) { Interface.AddMessage(Interface.MessageType.Error, false, "PoleStructureIndex is invalid in Track.Pole at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); sttype = 0; } } if (typ < 0 || typ >= Data.Structure.Poles.Length || Data.Structure.Poles[typ] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "PoleStructureIndex references an object not loaded in Track.Pole at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (sttype < 0 || sttype >= Data.Structure.Poles[typ].Length || Data.Structure.Poles[typ][sttype] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "PoleStructureIndex references an object not loaded in Track.Pole at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Blocks[BlockIndex].RailPole[idx].Mode = typ; Data.Blocks[BlockIndex].RailPole[idx].Type = sttype; Data.Blocks[BlockIndex].RailPole[idx].Exists = true; } } } } break; case "track.poleend": { if (!PreviewOnly) { int idx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in Track.PoleEnd at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (idx < 0 | idx >= Data.Blocks[BlockIndex].RailPole.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex does not reference an existing pole in Track.PoleEnd at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx >= Data.Blocks[BlockIndex].Rail.Length || (!Data.Blocks[BlockIndex].Rail[idx].RailStart & !Data.Blocks[BlockIndex].Rail[idx].RailEnd)) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex could be out of range in Track.PoleEnd at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } Data.Blocks[BlockIndex].RailPole[idx].Exists = false; } } } break; case "track.wall": { if (!PreviewOnly) { int idx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in Track.Wall at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (idx < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is expected to be a non-negative integer in Track.Wall at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } int dir = 0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out dir)) { Interface.AddMessage(Interface.MessageType.Error, false, "Direction is invalid in Track.Wall at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dir = 0; } int sttype = 0; if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out sttype)) { Interface.AddMessage(Interface.MessageType.Error, false, "WallStructureIndex is invalid in Track.Wall at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); sttype = 0; } if (sttype < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "WallStructureIndex is expected to be a non-negative integer in Track.Wall at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); sttype = 0; } if (dir <= 0 && (sttype >= Data.Structure.WallL.Length || Data.Structure.WallL[sttype] == null) || dir >= 0 && (sttype >= Data.Structure.WallR.Length || Data.Structure.WallR[sttype] == null)) { Interface.AddMessage(Interface.MessageType.Error, false, "WallStructureIndex references an object not loaded in Track.Wall at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is expected to be non-negative in Track.Wall at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx].RailStart) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex could be out of range in Track.Wall at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } if (idx >= Data.Blocks[BlockIndex].RailWall.Length) { Array.Resize(ref Data.Blocks[BlockIndex].RailWall, idx + 1); } Data.Blocks[BlockIndex].RailWall[idx].Exists = true; Data.Blocks[BlockIndex].RailWall[idx].Type = sttype; Data.Blocks[BlockIndex].RailWall[idx].Direction = dir; } } } } break; case "track.wallend": { if (!PreviewOnly) { int idx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in Track.WallEnd at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (idx < 0 | idx >= Data.Blocks[BlockIndex].RailWall.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex does not reference an existing wall in Track.WallEnd at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx >= Data.Blocks[BlockIndex].Rail.Length || (!Data.Blocks[BlockIndex].Rail[idx].RailStart & !Data.Blocks[BlockIndex].Rail[idx].RailEnd)) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex could be out of range in Track.WallEnd at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } Data.Blocks[BlockIndex].RailWall[idx].Exists = false; } } } break; case "track.dike": { if (!PreviewOnly) { int idx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in Track.Dike at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (idx < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is expected to be a non-negative integer in Track.Dike at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } int dir = 0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out dir)) { Interface.AddMessage(Interface.MessageType.Error, false, "Direction is invalid in Track.Dike at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dir = 0; } int sttype = 0; if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out sttype)) { Interface.AddMessage(Interface.MessageType.Error, false, "DikeStructureIndex is invalid in Track.Dike at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); sttype = 0; } if (sttype < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "DikeStructureIndex is expected to be a non-negative integer in Track.Wall at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); sttype = 0; } if (dir <= 0 && (sttype >= Data.Structure.DikeL.Length || Data.Structure.DikeL[sttype] == null) || dir >= 0 && (sttype >= Data.Structure.DikeR.Length || Data.Structure.DikeR[sttype] == null)) { Interface.AddMessage(Interface.MessageType.Error, false, "DikeStructureIndex references an object not loaded in Track.Dike at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is expected to be non-negative in Track.Dike at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx].RailStart) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex could be out of range in Track.Dike at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } if (idx >= Data.Blocks[BlockIndex].RailDike.Length) { Array.Resize(ref Data.Blocks[BlockIndex].RailDike, idx + 1); } Data.Blocks[BlockIndex].RailDike[idx].Exists = true; Data.Blocks[BlockIndex].RailDike[idx].Type = sttype; Data.Blocks[BlockIndex].RailDike[idx].Direction = dir; } } } } break; case "track.dikeend": { if (!PreviewOnly) { int idx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in Track.DikeEnd at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (idx < 0 | idx >= Data.Blocks[BlockIndex].RailDike.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex does not reference an existing dike in Track.DikeEnd at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx >= Data.Blocks[BlockIndex].Rail.Length || (!Data.Blocks[BlockIndex].Rail[idx].RailStart & !Data.Blocks[BlockIndex].Rail[idx].RailEnd)) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex could be out of range in Track.DikeEnd at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } Data.Blocks[BlockIndex].RailDike[idx].Exists = false; } } } break; case "track.marker": { if (!PreviewOnly) { if (Arguments.Length < 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Track.Marker is expected to have at least one argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { string f = OpenBveApi.Path.CombineFile(ObjectPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in Track.Marker at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double dist = Data.BlockInterval; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], UnitOfLength, out dist)) { Interface.AddMessage(Interface.MessageType.Error, false, "Distance is invalid in Track.Marker at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); dist = Data.BlockInterval; } double start, end; if (dist < 0.0) { start = Data.TrackPosition; end = Data.TrackPosition - dist; } else { start = Data.TrackPosition - dist; end = Data.TrackPosition; } if (start < 0.0) start = 0.0; if (end < 0.0) end = 0.0; if (end <= start) end = start + 0.01; int n = Data.Markers.Length; Array.Resize(ref Data.Markers, n + 1); Data.Markers[n].StartingPosition = start; Data.Markers[n].EndingPosition = end; Textures.RegisterTexture(f, new OpenBveApi.Textures.TextureParameters(null, new Color24(64, 64, 64)), out Data.Markers[n].Texture); } } } } break; case "track.height": { if (!PreviewOnly) { double h = 0.0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], UnitOfLength, out h)) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is invalid in Track.Height at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); h = 0.0; } Data.Blocks[BlockIndex].Height = IsRW ? h + 0.3 : h; } } break; case "track.ground": { if (!PreviewOnly) { int cytype = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out cytype)) { Interface.AddMessage(Interface.MessageType.Error, false, "CycleIndex is invalid in Track.Ground at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); cytype = 0; } if (cytype < Data.Structure.Cycle.Length && Data.Structure.Cycle[cytype] != null) { Data.Blocks[BlockIndex].Cycle = Data.Structure.Cycle[cytype]; } else { if (cytype >= Data.Structure.Ground.Length || Data.Structure.Ground[cytype] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "CycleIndex references an object not loaded in Track.Ground at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Blocks[BlockIndex].Cycle = new int[] { cytype }; } } } } break; case "track.crack": { if (!PreviewOnly) { int idx1 = 0, idx2 = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx1)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex1 is invalid in Track.Crack at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx1 = 0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out idx2)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex2 is invalid in Track.Crack at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx2 = 0; } int sttype = 0; if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out sttype)) { Interface.AddMessage(Interface.MessageType.Error, false, "CrackStructureIndex is invalid in Track.Crack at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); sttype = 0; } if (sttype < 0 || (sttype >= Data.Structure.CrackL.Length || Data.Structure.CrackL[sttype] == null) || (sttype >= Data.Structure.CrackR.Length || Data.Structure.CrackR[sttype] == null)) { Interface.AddMessage(Interface.MessageType.Error, false, "CrackStructureIndex references an object not loaded in Track.Crack at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx1 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex1 is expected to be non-negative in Track.Crack at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (idx2 < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex2 is expected to be non-negative in Track.Crack at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (idx1 == idx2) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex1 is expected to be unequal to Index2 in Track.Crack at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx1 >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx1].RailStart) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex1 could be out of range in Track.Crack at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } if (idx2 >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx2].RailStart) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex2 could be out of range in Track.Crack at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } int n = Data.Blocks[BlockIndex].Crack.Length; Array.Resize(ref Data.Blocks[BlockIndex].Crack, n + 1); Data.Blocks[BlockIndex].Crack[n].PrimaryRail = idx1; Data.Blocks[BlockIndex].Crack[n].SecondaryRail = idx2; Data.Blocks[BlockIndex].Crack[n].Type = sttype; } } } } break; case "track.freeobj": { if (!PreviewOnly) { int idx = 0, sttype = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out sttype)) { Interface.AddMessage(Interface.MessageType.Error, false, "FreeObjStructureIndex is invalid in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); sttype = 0; } if (idx < -1) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is expected to be non-negative or -1 in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (sttype < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "FreeObjStructureIndex is expected to be non-negative in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (idx >= 0 && (idx >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx].RailStart)) { Interface.AddMessage(Interface.MessageType.Warning, false, "RailIndex could be out of range in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } if (sttype >= Data.Structure.FreeObj.Length || Data.Structure.FreeObj[sttype] == null || Data.Structure.FreeObj[sttype] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FreeObjStructureIndex references an object not loaded in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double x = 0.0, y = 0.0; double yaw = 0.0, pitch = 0.0, roll = 0.0; if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], UnitOfLength, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); x = 0.0; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[3], UnitOfLength, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); y = 0.0; } if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[4], out yaw)) { Interface.AddMessage(Interface.MessageType.Error, false, "Yaw is invalid in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); yaw = 0.0; } if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[5], out pitch)) { Interface.AddMessage(Interface.MessageType.Error, false, "Pitch is invalid in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); pitch = 0.0; } if (Arguments.Length >= 7 && Arguments[6].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[6], out roll)) { Interface.AddMessage(Interface.MessageType.Error, false, "Roll is invalid in Track.FreeObj at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); roll = 0.0; } if (idx == -1) { int n; n = Data.Blocks[BlockIndex].GroundFreeObj.Length; Array.Resize(ref Data.Blocks[BlockIndex].GroundFreeObj, n + 1); Data.Blocks[BlockIndex].GroundFreeObj[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].GroundFreeObj[n].Type = sttype; Data.Blocks[BlockIndex].GroundFreeObj[n].X = x; Data.Blocks[BlockIndex].GroundFreeObj[n].Y = y; Data.Blocks[BlockIndex].GroundFreeObj[n].Yaw = yaw * 0.0174532925199433; Data.Blocks[BlockIndex].GroundFreeObj[n].Pitch = pitch * 0.0174532925199433; Data.Blocks[BlockIndex].GroundFreeObj[n].Roll = roll * 0.0174532925199433; } else { if (idx >= Data.Blocks[BlockIndex].RailFreeObj.Length) { Array.Resize(ref Data.Blocks[BlockIndex].RailFreeObj, idx + 1); } int n; if (Data.Blocks[BlockIndex].RailFreeObj[idx] == null) { Data.Blocks[BlockIndex].RailFreeObj[idx] = new FreeObj[1]; n = 0; } else { n = Data.Blocks[BlockIndex].RailFreeObj[idx].Length; Array.Resize(ref Data.Blocks[BlockIndex].RailFreeObj[idx], n + 1); } Data.Blocks[BlockIndex].RailFreeObj[idx][n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].RailFreeObj[idx][n].Type = sttype; Data.Blocks[BlockIndex].RailFreeObj[idx][n].X = x; Data.Blocks[BlockIndex].RailFreeObj[idx][n].Y = y; Data.Blocks[BlockIndex].RailFreeObj[idx][n].Yaw = yaw * 0.0174532925199433; Data.Blocks[BlockIndex].RailFreeObj[idx][n].Pitch = pitch * 0.0174532925199433; Data.Blocks[BlockIndex].RailFreeObj[idx][n].Roll = roll * 0.0174532925199433; } } } } } break; case "track.back": { if (!PreviewOnly) { int typ = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out typ)) { Interface.AddMessage(Interface.MessageType.Error, false, "BackgroundTextureIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); typ = 0; } if (typ < 0 | typ >= Data.Backgrounds.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "BackgroundTextureIndex references a texture not loaded in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else if (Data.Backgrounds[typ].Texture == null) { Interface.AddMessage(Interface.MessageType.Error, false, "BackgroundTextureIndex has not been loaded via Texture.Background in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { Data.Blocks[BlockIndex].Background = typ; } } } break; case "track.announce": { if (!PreviewOnly) { if (Arguments.Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have between 1 and 2 arguments at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { string f = OpenBveApi.Path.CombineFile(SoundPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double speed = 0.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out speed)) { Interface.AddMessage(Interface.MessageType.Error, false, "Speed is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); speed = 0.0; } int n = Data.Blocks[BlockIndex].Sound.Length; Array.Resize(ref Data.Blocks[BlockIndex].Sound, n + 1); Data.Blocks[BlockIndex].Sound[n].TrackPosition = Data.TrackPosition; const double radius = 15.0; Data.Blocks[BlockIndex].Sound[n].SoundBuffer = Sounds.RegisterBuffer(f, radius); Data.Blocks[BlockIndex].Sound[n].Type = speed == 0.0 ? SoundType.TrainStatic : SoundType.TrainDynamic; Data.Blocks[BlockIndex].Sound[n].Speed = speed * Data.UnitOfSpeed; } } } } } break; case "track.doppler": { if (!PreviewOnly) { if (Arguments.Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have between 1 and 3 arguments at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { if (Interface.ContainsInvalidPathChars(Arguments[0])) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { string f = OpenBveApi.Path.CombineFile(SoundPath, Arguments[0]); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double x = 0.0, y = 0.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 & !Interface.TryParseDoubleVb6(Arguments[1], UnitOfLength, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); x = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 & !Interface.TryParseDoubleVb6(Arguments[2], UnitOfLength, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); y = 0.0; } int n = Data.Blocks[BlockIndex].Sound.Length; Array.Resize(ref Data.Blocks[BlockIndex].Sound, n + 1); Data.Blocks[BlockIndex].Sound[n].TrackPosition = Data.TrackPosition; const double radius = 15.0; Data.Blocks[BlockIndex].Sound[n].SoundBuffer = Sounds.RegisterBuffer(f, radius); Data.Blocks[BlockIndex].Sound[n].Type = SoundType.World; Data.Blocks[BlockIndex].Sound[n].X = x; Data.Blocks[BlockIndex].Sound[n].Y = y; Data.Blocks[BlockIndex].Sound[n].Radius = radius; } } } } } break; case "track.pretrain": { if (!PreviewOnly) { if (Arguments.Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, Command + " is expected to have exactly 1 argument at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } else { double time = 0.0; if (Arguments[0].Length > 0 & !Interface.TryParseTime(Arguments[0], out time)) { Interface.AddMessage(Interface.MessageType.Error, false, "Time is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); time = 0.0; } int n = Game.BogusPretrainInstructions.Length; if (n != 0 && Game.BogusPretrainInstructions[n - 1].Time >= time) { Interface.AddMessage(Interface.MessageType.Error, false, "Time is expected to be in ascending order between successive " + Command + " commands at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } Array.Resize(ref Game.BogusPretrainInstructions, n + 1); Game.BogusPretrainInstructions[n].TrackPosition = Data.TrackPosition; Game.BogusPretrainInstructions[n].Time = time; } } } break; case "track.pointofinterest": case "track.poi": { if (!PreviewOnly) { int idx = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out idx)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (idx < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex is expected to be non-negative in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); idx = 0; } if (idx >= 0 && (idx >= Data.Blocks[BlockIndex].Rail.Length || !Data.Blocks[BlockIndex].Rail[idx].RailStart)) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex references a non-existing rail in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); } double x = 0.0, y = 0.0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], UnitOfLength, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); x = 0.0; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[2], UnitOfLength, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); y = 0.0; } double yaw = 0.0, pitch = 0.0, roll = 0.0; if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[3], out yaw)) { Interface.AddMessage(Interface.MessageType.Error, false, "Yaw is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); yaw = 0.0; } if (Arguments.Length >= 5 && Arguments[4].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[4], out pitch)) { Interface.AddMessage(Interface.MessageType.Error, false, "Pitch is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); pitch = 0.0; } if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[5], out roll)) { Interface.AddMessage(Interface.MessageType.Error, false, "Roll is invalid in " + Command + " at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); roll = 0.0; } string text = null; if (Arguments.Length >= 7 && Arguments[6].Length != 0) { text = Arguments[6]; } int n = Data.Blocks[BlockIndex].PointsOfInterest.Length; Array.Resize(ref Data.Blocks[BlockIndex].PointsOfInterest, n + 1); Data.Blocks[BlockIndex].PointsOfInterest[n].TrackPosition = Data.TrackPosition; Data.Blocks[BlockIndex].PointsOfInterest[n].RailIndex = idx; Data.Blocks[BlockIndex].PointsOfInterest[n].X = x; Data.Blocks[BlockIndex].PointsOfInterest[n].Y = y; Data.Blocks[BlockIndex].PointsOfInterest[n].Yaw = 0.0174532925199433 * yaw; Data.Blocks[BlockIndex].PointsOfInterest[n].Pitch = 0.0174532925199433 * pitch; Data.Blocks[BlockIndex].PointsOfInterest[n].Roll = 0.0174532925199433 * roll; Data.Blocks[BlockIndex].PointsOfInterest[n].Text = text; } } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "The command " + Command + " is not supported at line " + Expressions[j].Line.ToString(Culture) + ", column " + Expressions[j].Column.ToString(Culture) + " in file " + Expressions[j].File); break; } } } } } if (!PreviewOnly) { // timetable Timetable.CustomTextures = new Textures.Texture[Data.TimetableDaytime.Length + Data.TimetableNighttime.Length]; int n = 0; for (int i = 0; i < Data.TimetableDaytime.Length; i++) { if (Data.TimetableDaytime[i] != null) { Timetable.CustomTextures[n] = Data.TimetableDaytime[i]; n++; } } for (int i = 0; i < Data.TimetableNighttime.Length; i++) { if (Data.TimetableNighttime[i] != null) { Timetable.CustomTextures[n] = Data.TimetableNighttime[i]; n++; } } Array.Resize(ref Timetable.CustomTextures, n); } // blocks Array.Resize(ref Data.Blocks, BlocksUsed); } // ================================ // create missing blocks private static void CreateMissingBlocks(ref RouteData Data, ref int BlocksUsed, int ToIndex, bool PreviewOnly) { if (ToIndex >= BlocksUsed) { while (Data.Blocks.Length <= ToIndex) { Array.Resize(ref Data.Blocks, Data.Blocks.Length << 1); } for (int i = BlocksUsed; i <= ToIndex; i++) { Data.Blocks[i] = new Block(); if (!PreviewOnly) { Data.Blocks[i].Background = -1; Data.Blocks[i].Brightness = new Brightness[] { }; Data.Blocks[i].Fog = Data.Blocks[i - 1].Fog; Data.Blocks[i].FogDefined = false; Data.Blocks[i].Cycle = Data.Blocks[i - 1].Cycle; Data.Blocks[i].Height = double.NaN; } Data.Blocks[i].RailType = new int[Data.Blocks[i - 1].RailType.Length]; for (int j = 0; j < Data.Blocks[i].RailType.Length; j++) { Data.Blocks[i].RailType[j] = Data.Blocks[i - 1].RailType[j]; } Data.Blocks[i].Rail = new Rail[Data.Blocks[i - 1].Rail.Length]; for (int j = 0; j < Data.Blocks[i].Rail.Length; j++) { Data.Blocks[i].Rail[j].RailStart = Data.Blocks[i - 1].Rail[j].RailStart; Data.Blocks[i].Rail[j].RailStartX = Data.Blocks[i - 1].Rail[j].RailStartX; Data.Blocks[i].Rail[j].RailStartY = Data.Blocks[i - 1].Rail[j].RailStartY; Data.Blocks[i].Rail[j].RailStartRefreshed = false; Data.Blocks[i].Rail[j].RailEnd = false; Data.Blocks[i].Rail[j].RailEndX = Data.Blocks[i - 1].Rail[j].RailStartX; Data.Blocks[i].Rail[j].RailEndY = Data.Blocks[i - 1].Rail[j].RailStartY; } if (!PreviewOnly) { Data.Blocks[i].RailWall = new WallDike[Data.Blocks[i - 1].RailWall.Length]; for (int j = 0; j < Data.Blocks[i].RailWall.Length; j++) { Data.Blocks[i].RailWall[j] = Data.Blocks[i - 1].RailWall[j]; } Data.Blocks[i].RailDike = new WallDike[Data.Blocks[i - 1].RailDike.Length]; for (int j = 0; j < Data.Blocks[i].RailDike.Length; j++) { Data.Blocks[i].RailDike[j] = Data.Blocks[i - 1].RailDike[j]; } Data.Blocks[i].RailPole = new Pole[Data.Blocks[i - 1].RailPole.Length]; for (int j = 0; j < Data.Blocks[i].RailPole.Length; j++) { Data.Blocks[i].RailPole[j] = Data.Blocks[i - 1].RailPole[j]; } Data.Blocks[i].Form = new Form[] { }; Data.Blocks[i].Crack = new Crack[] { }; Data.Blocks[i].Signal = new Signal[] { }; Data.Blocks[i].Section = new Section[] { }; Data.Blocks[i].Sound = new Sound[] { }; Data.Blocks[i].Transponder = new Transponder[] { }; Data.Blocks[i].RailFreeObj = new FreeObj[][] { }; Data.Blocks[i].GroundFreeObj = new FreeObj[] { }; Data.Blocks[i].PointsOfInterest = new PointOfInterest[] { }; } Data.Blocks[i].Pitch = Data.Blocks[i - 1].Pitch; Data.Blocks[i].Limit = new Limit[] { }; Data.Blocks[i].Stop = new Stop[] { }; Data.Blocks[i].Station = -1; Data.Blocks[i].StationPassAlarm = false; Data.Blocks[i].CurrentTrackState = Data.Blocks[i - 1].CurrentTrackState; Data.Blocks[i].Turn = 0.0; Data.Blocks[i].Accuracy = Data.Blocks[i - 1].Accuracy; Data.Blocks[i].AdhesionMultiplier = Data.Blocks[i - 1].AdhesionMultiplier; } BlocksUsed = ToIndex + 1; } } // get mirrored object private static ObjectManager.UnifiedObject GetMirroredObject(ObjectManager.UnifiedObject Prototype) { if (Prototype is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)Prototype; return GetMirroredStaticObject(s); } else if (Prototype is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)Prototype; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int i = 0; i < a.Objects.Length; i++) { Result.Objects[i] = a.Objects[i].Clone(); for (int j = 0; j < a.Objects[i].States.Length; j++) { Result.Objects[i].States[j].Object = GetMirroredStaticObject(a.Objects[i].States[j].Object); } Result.Objects[i].TranslateXDirection.X *= -1.0; Result.Objects[i].TranslateYDirection.X *= -1.0; Result.Objects[i].TranslateZDirection.X *= -1.0; Result.Objects[i].RotateXDirection.X *= -1.0; Result.Objects[i].RotateYDirection.X *= -1.0; Result.Objects[i].RotateZDirection.X *= -1.0; } return Result; } else { return null; } } private static ObjectManager.StaticObject GetMirroredStaticObject(ObjectManager.StaticObject Prototype) { ObjectManager.StaticObject Result = ObjectManager.CloneObject(Prototype); for (int i = 0; i < Result.Mesh.Vertices.Length; i++) { Result.Mesh.Vertices[i].Coordinates.X = -Result.Mesh.Vertices[i].Coordinates.X; } for (int i = 0; i < Result.Mesh.Faces.Length; i++) { for (int k = 0; k < Result.Mesh.Faces[i].Vertices.Length; k++) { Result.Mesh.Faces[i].Vertices[k].Normal.X = -Result.Mesh.Faces[i].Vertices[k].Normal.X; } Result.Mesh.Faces[i].Flip(); } return Result; } // get transformed object private static ObjectManager.StaticObject GetTransformedStaticObject(ObjectManager.StaticObject Prototype, double NearDistance, double FarDistance) { ObjectManager.StaticObject Result = ObjectManager.CloneObject(Prototype); int n = 0; double x2 = 0.0, x3 = 0.0, x6 = 0.0, x7 = 0.0; for (int i = 0; i < Result.Mesh.Vertices.Length; i++) { if (n == 2) { x2 = Result.Mesh.Vertices[i].Coordinates.X; } else if (n == 3) { x3 = Result.Mesh.Vertices[i].Coordinates.X; } else if (n == 6) { x6 = Result.Mesh.Vertices[i].Coordinates.X; } else if (n == 7) { x7 = Result.Mesh.Vertices[i].Coordinates.X; } n++; if (n == 8) { break; } } if (n >= 4) { int m = 0; for (int i = 0; i < Result.Mesh.Vertices.Length; i++) { if (m == 0) { Result.Mesh.Vertices[i].Coordinates.X = NearDistance - x3; } else if (m == 1) { Result.Mesh.Vertices[i].Coordinates.X = FarDistance - x2; if (n < 8) { m = 8; break; } } else if (m == 4) { Result.Mesh.Vertices[i].Coordinates.X = NearDistance - x7; } else if (m == 5) { Result.Mesh.Vertices[i].Coordinates.X = NearDistance - x6; m = 8; break; } m++; if (m == 8) { break; } } } return Result; } // load all textures /// Loads all signal or glow textures. /// The base file. /// Whether to load glow textures. If false, black is the transparent color. If true, the texture is edited according to the CSV route documentation. /// All textures matching the base file. private static Textures.Texture[] LoadAllTextures(string BaseFile, bool IsGlowTexture) { string Folder = System.IO.Path.GetDirectoryName(BaseFile); if (!System.IO.Directory.Exists(Folder)) { return new Textures.Texture[] { }; } string Name = System.IO.Path.GetFileNameWithoutExtension(BaseFile); Textures.Texture[] Textures = new Textures.Texture[] { }; string[] Files = System.IO.Directory.GetFiles(Folder); for (int i = 0; i < Files.Length; i++) { string a = System.IO.Path.GetFileNameWithoutExtension(Files[i]); if (a.StartsWith(Name, StringComparison.OrdinalIgnoreCase)) { if (a.Length > Name.Length) { string b = a.Substring(Name.Length).TrimStart(); int j; if (int.TryParse(b, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out j)) { if (j >= 0) { string c = System.IO.Path.GetExtension(Files[i]); switch (c.ToLowerInvariant()) { case ".bmp": case ".gif": case ".jpg": case ".jpeg": case ".png": case ".tif": case ".tiff": if (j >= Textures.Length) { int n = Textures.Length; Array.Resize(ref Textures, j + 1); for (int k = n; k < j; k++) { Textures[k] = null; } } if (IsGlowTexture) { OpenBveApi.Textures.Texture texture; if (Program.CurrentHost.LoadTexture(Files[i], null, out texture)) { if (texture.BitsPerPixel == 32) { byte[] bytes = texture.Bytes; InvertLightness(bytes); texture = new OpenBveApi.Textures.Texture(texture.Width, texture.Height, 32, bytes); } Textures[j] = OpenBve.Textures.RegisterTexture(texture); } } else { OpenBve.Textures.RegisterTexture(Files[i], new OpenBveApi.Textures.TextureParameters(null, Color24.Black), out Textures[j]); } break; } } } } } } return Textures; } private static void InvertLightness(byte[] bytes) { for (int i = 0; i < bytes.Length; i += 4) { if (bytes[i] != 0 | bytes[i + 1] != 0 | bytes[i + 2] != 0) { int r = bytes[i + 0]; int g = bytes[i + 1]; int b = bytes[i + 2]; if (g <= r & r <= b | b <= r & r <= g) { bytes[i + 0] = (byte)(255 + r - g - b); bytes[i + 1] = (byte)(255 - b); bytes[i + 2] = (byte)(255 - g); } else if (r <= g & g <= b | b <= g & g <= r) { bytes[i + 0] = (byte)(255 - b); bytes[i + 1] = (byte)(255 + g - r - b); bytes[i + 2] = (byte)(255 - r); } else { bytes[i + 0] = (byte)(255 - g); bytes[i + 1] = (byte)(255 - r); bytes[i + 2] = (byte)(255 + b - r - g); } } } } // ================================ // get brightness private static double GetBrightness(ref RouteData Data, double TrackPosition) { double tmin = double.PositiveInfinity; double tmax = double.NegativeInfinity; double bmin = 1.0, bmax = 1.0; for (int i = 0; i < Data.Blocks.Length; i++) { for (int j = 0; j < Data.Blocks[i].Brightness.Length; j++) { if (Data.Blocks[i].Brightness[j].TrackPosition <= TrackPosition) { tmin = Data.Blocks[i].Brightness[j].TrackPosition; bmin = (double)Data.Blocks[i].Brightness[j].Value; } } } for (int i = Data.Blocks.Length - 1; i >= 0; i--) { for (int j = Data.Blocks[i].Brightness.Length - 1; j >= 0; j--) { if (Data.Blocks[i].Brightness[j].TrackPosition >= TrackPosition) { tmax = Data.Blocks[i].Brightness[j].TrackPosition; bmax = (double)Data.Blocks[i].Brightness[j].Value; } } } if (tmin == double.PositiveInfinity & tmax == double.NegativeInfinity) { return 1.0; } else if (tmin == double.PositiveInfinity) { return (bmax - 1.0) * TrackPosition / tmax + 1.0; } else if (tmax == double.NegativeInfinity) { return bmin; } else if (tmin == tmax) { return 0.5 * (bmin + bmax); } else { double n = (TrackPosition - tmin) / (tmax - tmin); return (1.0 - n) * bmin + n * bmax; } } // apply route data private static void ApplyRouteData(string FileName, System.Text.Encoding Encoding, ref RouteData Data, bool PreviewOnly) { string SignalPath, LimitPath, LimitGraphicsPath, TransponderPath; ObjectManager.StaticObject SignalPost, LimitPostStraight, LimitPostLeft, LimitPostRight, LimitPostInfinite; ObjectManager.StaticObject LimitOneDigit, LimitTwoDigits, LimitThreeDigits, StopPost; ObjectManager.StaticObject TransponderS, TransponderSN, TransponderFalseStart, TransponderPOrigin, TransponderPStop; if (!PreviewOnly) { string CompatibilityFolder = Program.FileSystem.GetDataFolder("Compatibility"); // load compatibility objects SignalPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Signals"); SignalPost = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalPath, "signal_post.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Limits"); LimitGraphicsPath = OpenBveApi.Path.CombineDirectory(LimitPath, "Graphics"); LimitPostStraight = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_straight.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitPostLeft = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_left.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitPostRight = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_right.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitPostInfinite = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_infinite.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitOneDigit = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_1_digit.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitTwoDigits = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_2_digits.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitThreeDigits = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_3_digits.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); StopPost = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(CompatibilityFolder, "stop.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); TransponderPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Transponders"); TransponderS = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "s.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); TransponderSN = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "sn.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); TransponderFalseStart = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "falsestart.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); TransponderPOrigin = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "porigin.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); TransponderPStop = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "pstop.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } else { SignalPath = null; LimitPath = null; LimitGraphicsPath = null; TransponderPath = null; SignalPost = null; LimitPostStraight = null; LimitPostLeft = null; LimitPostRight = null; LimitPostInfinite = null; LimitOneDigit = null; LimitTwoDigits = null; LimitThreeDigits = null; StopPost = null; TransponderS = null; TransponderSN = null; TransponderFalseStart = null; TransponderPOrigin = null; TransponderPStop = null; } // initialize System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; int LastBlock = (int)Math.Floor((Data.TrackPosition + 600.0) / Data.BlockInterval + 0.001) + 1; int BlocksUsed = Data.Blocks.Length; CreateMissingBlocks(ref Data, ref BlocksUsed, LastBlock, PreviewOnly); Array.Resize(ref Data.Blocks, BlocksUsed); // interpolate height if (!PreviewOnly) { int z = 0; for (int i = 0; i < Data.Blocks.Length; i++) { if (!double.IsNaN(Data.Blocks[i].Height)) { for (int j = i - 1; j >= 0; j--) { if (!double.IsNaN(Data.Blocks[j].Height)) { double a = Data.Blocks[j].Height; double b = Data.Blocks[i].Height; double d = (b - a) / (double)(i - j); for (int k = j + 1; k < i; k++) { a += d; Data.Blocks[k].Height = a; } break; } } z = i; } } for (int i = z + 1; i < Data.Blocks.Length; i++) { Data.Blocks[i].Height = Data.Blocks[z].Height; } } // background if (!PreviewOnly) { if (Data.Blocks[0].Background >= 0 & Data.Blocks[0].Background < Data.Backgrounds.Length) { World.CurrentBackground = Data.Backgrounds[Data.Blocks[0].Background]; } else { World.CurrentBackground = new World.Background(null, 6, false); } World.TargetBackground = World.CurrentBackground; } // brightness int CurrentBrightnessElement = -1; int CurrentBrightnessEvent = -1; float CurrentBrightnessValue = 1.0f; double CurrentBrightnessTrackPosition = (double)Data.FirstUsedBlock * Data.BlockInterval; if (!PreviewOnly) { for (int i = Data.FirstUsedBlock; i < Data.Blocks.Length; i++) { if (Data.Blocks[i].Brightness != null && Data.Blocks[i].Brightness.Length != 0) { CurrentBrightnessValue = Data.Blocks[i].Brightness[0].Value; CurrentBrightnessTrackPosition = Data.Blocks[i].Brightness[0].Value; break; } } } // create objects and track Vector3 Position = new Vector3(0.0, 0.0, 0.0); World.Vector2D Direction = new World.Vector2D(0.0, 1.0); TrackManager.CurrentTrack = new TrackManager.Track(); TrackManager.CurrentTrack.Elements = new TrackManager.TrackElement[] { }; double CurrentSpeedLimit = double.PositiveInfinity; int CurrentRunIndex = 0; int CurrentFlangeIndex = 0; if (Data.FirstUsedBlock < 0) Data.FirstUsedBlock = 0; TrackManager.CurrentTrack.Elements = new TrackManager.TrackElement[256]; int CurrentTrackLength = 0; int PreviousFogElement = -1; int PreviousFogEvent = -1; Game.Fog PreviousFog = new Game.Fog(Game.NoFogStart, Game.NoFogEnd, new Color24(128, 128, 128), -Data.BlockInterval); Game.Fog CurrentFog = new Game.Fog(Game.NoFogStart, Game.NoFogEnd, new Color24(128, 128, 128), 0.0); // process blocks double progressFactor = Data.Blocks.Length - Data.FirstUsedBlock == 0 ? 0.5 : 0.5 / (double)(Data.Blocks.Length - Data.FirstUsedBlock); for (int i = Data.FirstUsedBlock; i < Data.Blocks.Length; i++) { Loading.RouteProgress = 0.6667 + (double)(i - Data.FirstUsedBlock) * progressFactor; if ((i & 15) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } double StartingDistance = (double)i * Data.BlockInterval; double EndingDistance = StartingDistance + Data.BlockInterval; // normalize World.Normalize(ref Direction.X, ref Direction.Y); // track if (!PreviewOnly) { if (Data.Blocks[i].Cycle.Length == 1 && Data.Blocks[i].Cycle[0] == -1) { if (Data.Structure.Cycle.Length == 0 || Data.Structure.Cycle[0] == null) { Data.Blocks[i].Cycle = new int[] { 0 }; } else { Data.Blocks[i].Cycle = Data.Structure.Cycle[0]; } } } TrackManager.TrackElement WorldTrackElement = Data.Blocks[i].CurrentTrackState; int n = CurrentTrackLength; if (n >= TrackManager.CurrentTrack.Elements.Length) { Array.Resize(ref TrackManager.CurrentTrack.Elements, TrackManager.CurrentTrack.Elements.Length << 1); } CurrentTrackLength++; TrackManager.CurrentTrack.Elements[n] = WorldTrackElement; TrackManager.CurrentTrack.Elements[n].WorldPosition = Position; TrackManager.CurrentTrack.Elements[n].WorldDirection = World.GetVector3(Direction, Data.Blocks[i].Pitch); TrackManager.CurrentTrack.Elements[n].WorldSide = new Vector3(Direction.Y, 0.0, -Direction.X); World.Cross(TrackManager.CurrentTrack.Elements[n].WorldDirection.X, TrackManager.CurrentTrack.Elements[n].WorldDirection.Y, TrackManager.CurrentTrack.Elements[n].WorldDirection.Z, TrackManager.CurrentTrack.Elements[n].WorldSide.X, TrackManager.CurrentTrack.Elements[n].WorldSide.Y, TrackManager.CurrentTrack.Elements[n].WorldSide.Z, out TrackManager.CurrentTrack.Elements[n].WorldUp.X, out TrackManager.CurrentTrack.Elements[n].WorldUp.Y, out TrackManager.CurrentTrack.Elements[n].WorldUp.Z); TrackManager.CurrentTrack.Elements[n].StartingTrackPosition = StartingDistance; TrackManager.CurrentTrack.Elements[n].Events = new TrackManager.GeneralEvent[] { }; TrackManager.CurrentTrack.Elements[n].AdhesionMultiplier = Data.Blocks[i].AdhesionMultiplier; TrackManager.CurrentTrack.Elements[n].CsvRwAccuracyLevel = Data.Blocks[i].Accuracy; // background if (!PreviewOnly) { if (Data.Blocks[i].Background >= 0) { int typ; if (i == Data.FirstUsedBlock) { typ = Data.Blocks[i].Background; } else { typ = Data.Backgrounds.Length > 0 ? 0 : -1; for (int j = i - 1; j >= Data.FirstUsedBlock; j--) { if (Data.Blocks[j].Background >= 0) { typ = Data.Blocks[j].Background; break; } } } if (typ >= 0 & typ < Data.Backgrounds.Length) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.BackgroundChangeEvent(0.0, Data.Backgrounds[typ], Data.Backgrounds[Data.Blocks[i].Background]); } } } // brightness if (!PreviewOnly) { for (int j = 0; j < Data.Blocks[i].Brightness.Length; j++) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Blocks[i].Brightness[j].TrackPosition - StartingDistance; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.BrightnessChangeEvent(d, Data.Blocks[i].Brightness[j].Value, CurrentBrightnessValue, Data.Blocks[i].Brightness[j].TrackPosition - CurrentBrightnessTrackPosition, Data.Blocks[i].Brightness[j].Value, 0.0); if (CurrentBrightnessElement >= 0 & CurrentBrightnessEvent >= 0) { TrackManager.BrightnessChangeEvent bce = (TrackManager.BrightnessChangeEvent)TrackManager.CurrentTrack.Elements[CurrentBrightnessElement].Events[CurrentBrightnessEvent]; bce.NextBrightness = Data.Blocks[i].Brightness[j].Value; bce.NextDistance = Data.Blocks[i].Brightness[j].TrackPosition - CurrentBrightnessTrackPosition; } CurrentBrightnessElement = n; CurrentBrightnessEvent = m; CurrentBrightnessValue = Data.Blocks[i].Brightness[j].Value; CurrentBrightnessTrackPosition = Data.Blocks[i].Brightness[j].TrackPosition; } } // fog if (!PreviewOnly) { if (Data.FogTransitionMode) { if (Data.Blocks[i].FogDefined) { Data.Blocks[i].Fog.TrackPosition = StartingDistance; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.FogChangeEvent(0.0, PreviousFog, Data.Blocks[i].Fog, Data.Blocks[i].Fog); if (PreviousFogElement >= 0 & PreviousFogEvent >= 0) { TrackManager.FogChangeEvent e = (TrackManager.FogChangeEvent)TrackManager.CurrentTrack.Elements[PreviousFogElement].Events[PreviousFogEvent]; e.NextFog = Data.Blocks[i].Fog; } else { Game.PreviousFog = PreviousFog; Game.CurrentFog = PreviousFog; Game.NextFog = Data.Blocks[i].Fog; } PreviousFog = Data.Blocks[i].Fog; PreviousFogElement = n; PreviousFogEvent = m; } } else { Data.Blocks[i].Fog.TrackPosition = StartingDistance + Data.BlockInterval; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.FogChangeEvent(0.0, PreviousFog, CurrentFog, Data.Blocks[i].Fog); PreviousFog = CurrentFog; CurrentFog = Data.Blocks[i].Fog; } } // rail sounds if (!PreviewOnly) { int j = Data.Blocks[i].RailType[0]; int r = j < Data.Structure.Run.Length ? Data.Structure.Run[j] : 0; int f = j < Data.Structure.Flange.Length ? Data.Structure.Flange[j] : 0; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.RailSoundsChangeEvent(0.0, CurrentRunIndex, CurrentFlangeIndex, r, f); CurrentRunIndex = r; CurrentFlangeIndex = f; } // point sound if (!PreviewOnly) { if (i < Data.Blocks.Length - 1) { bool q = false; for (int j = 0; j < Data.Blocks[i].Rail.Length; j++) { if (Data.Blocks[i].Rail[j].RailStart & Data.Blocks[i + 1].Rail.Length > j) { bool qx = Math.Sign(Data.Blocks[i].Rail[j].RailStartX) != Math.Sign(Data.Blocks[i + 1].Rail[j].RailEndX); bool qy = Data.Blocks[i].Rail[j].RailStartY * Data.Blocks[i + 1].Rail[j].RailEndY <= 0.0; if (qx & qy) { q = true; break; } } } if (q) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.SoundEvent(0.0, null, false, false, true, new Vector3(0.0, 0.0, 0.0), 12.5); } } } // station if (Data.Blocks[i].Station >= 0) { // station int s = Data.Blocks[i].Station; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.StationStartEvent(0.0, s); double dx, dy = 3.0; if (Game.Stations[s].OpenLeftDoors & !Game.Stations[s].OpenRightDoors) { dx = -5.0; } else if (!Game.Stations[s].OpenLeftDoors & Game.Stations[s].OpenRightDoors) { dx = 5.0; } else { dx = 0.0; } Game.Stations[s].SoundOrigin.X = Position.X + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.X + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.X; Game.Stations[s].SoundOrigin.Y = Position.Y + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Y + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Y; Game.Stations[s].SoundOrigin.Z = Position.Z + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Z + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Z; // passalarm if (!PreviewOnly) { if (Data.Blocks[i].StationPassAlarm) { int b = i - 6; if (b >= 0) { int j = b - Data.FirstUsedBlock; if (j >= 0) { m = TrackManager.CurrentTrack.Elements[j].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[j].Events, m + 1); TrackManager.CurrentTrack.Elements[j].Events[m] = new TrackManager.StationPassAlarmEvent(0.0); } } } } } // stop for (int j = 0; j < Data.Blocks[i].Stop.Length; j++) { int s = Data.Blocks[i].Stop[j].Station; int t = Game.Stations[s].Stops.Length; Array.Resize(ref Game.Stations[s].Stops, t + 1); Game.Stations[s].Stops[t].TrackPosition = Data.Blocks[i].Stop[j].TrackPosition; Game.Stations[s].Stops[t].ForwardTolerance = Data.Blocks[i].Stop[j].ForwardTolerance; Game.Stations[s].Stops[t].BackwardTolerance = Data.Blocks[i].Stop[j].BackwardTolerance; Game.Stations[s].Stops[t].Cars = Data.Blocks[i].Stop[j].Cars; double dx, dy = 2.0; if (Game.Stations[s].OpenLeftDoors & !Game.Stations[s].OpenRightDoors) { dx = -5.0; } else if (!Game.Stations[s].OpenLeftDoors & Game.Stations[s].OpenRightDoors) { dx = 5.0; } else { dx = 0.0; } Game.Stations[s].SoundOrigin.X = Position.X + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.X + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.X; Game.Stations[s].SoundOrigin.Y = Position.Y + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Y + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Y; Game.Stations[s].SoundOrigin.Z = Position.Z + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Z + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Z; } // limit for (int j = 0; j < Data.Blocks[i].Limit.Length; j++) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Blocks[i].Limit[j].TrackPosition - StartingDistance; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.LimitChangeEvent(d, CurrentSpeedLimit, Data.Blocks[i].Limit[j].Speed); CurrentSpeedLimit = Data.Blocks[i].Limit[j].Speed; } // marker if (!PreviewOnly) { for (int j = 0; j < Data.Markers.Length; j++) { if (Data.Markers[j].StartingPosition >= StartingDistance & Data.Markers[j].StartingPosition < EndingDistance) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Markers[j].StartingPosition - StartingDistance; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.MarkerStartEvent(d, Data.Markers[j].Texture); } if (Data.Markers[j].EndingPosition >= StartingDistance & Data.Markers[j].EndingPosition < EndingDistance) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Markers[j].EndingPosition - StartingDistance; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.MarkerEndEvent(d, Data.Markers[j].Texture); } } } // sound if (!PreviewOnly) { for (int j = 0; j < Data.Blocks[i].Sound.Length; j++) { if (Data.Blocks[i].Sound[j].Type == SoundType.TrainStatic | Data.Blocks[i].Sound[j].Type == SoundType.TrainDynamic) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Blocks[i].Sound[j].TrackPosition - StartingDistance; switch (Data.Blocks[i].Sound[j].Type) { case SoundType.TrainStatic: TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.SoundEvent(d, Data.Blocks[i].Sound[j].SoundBuffer, true, true, false, new Vector3(0.0, 0.0, 0.0), 0.0); break; case SoundType.TrainDynamic: TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.SoundEvent(d, Data.Blocks[i].Sound[j].SoundBuffer, false, false, true, new Vector3(0.0, 0.0, 0.0), Data.Blocks[i].Sound[j].Speed); break; } } } } // turn if (Data.Blocks[i].Turn != 0.0) { double ag = -Math.Atan(Data.Blocks[i].Turn); double cosag = Math.Cos(ag); double sinag = Math.Sin(ag); World.Rotate(ref Direction, cosag, sinag); World.RotatePlane(ref TrackManager.CurrentTrack.Elements[n].WorldDirection, cosag, sinag); World.RotatePlane(ref TrackManager.CurrentTrack.Elements[n].WorldSide, cosag, sinag); World.Cross(TrackManager.CurrentTrack.Elements[n].WorldDirection.X, TrackManager.CurrentTrack.Elements[n].WorldDirection.Y, TrackManager.CurrentTrack.Elements[n].WorldDirection.Z, TrackManager.CurrentTrack.Elements[n].WorldSide.X, TrackManager.CurrentTrack.Elements[n].WorldSide.Y, TrackManager.CurrentTrack.Elements[n].WorldSide.Z, out TrackManager.CurrentTrack.Elements[n].WorldUp.X, out TrackManager.CurrentTrack.Elements[n].WorldUp.Y, out TrackManager.CurrentTrack.Elements[n].WorldUp.Z); } // curves double a = 0.0; double c = Data.BlockInterval; double h = 0.0; if (WorldTrackElement.CurveRadius != 0.0 & Data.Blocks[i].Pitch != 0.0) { double d = Data.BlockInterval; double p = Data.Blocks[i].Pitch; double r = WorldTrackElement.CurveRadius; double s = d / Math.Sqrt(1.0 + p * p); h = s * p; double b = s / Math.Abs(r); c = Math.Sqrt(2.0 * r * r * (1.0 - Math.Cos(b))); a = 0.5 * (double)Math.Sign(r) * b; World.Rotate(ref Direction, Math.Cos(-a), Math.Sin(-a)); } else if (WorldTrackElement.CurveRadius != 0.0) { double d = Data.BlockInterval; double r = WorldTrackElement.CurveRadius; double b = d / Math.Abs(r); c = Math.Sqrt(2.0 * r * r * (1.0 - Math.Cos(b))); a = 0.5 * (double)Math.Sign(r) * b; World.Rotate(ref Direction, Math.Cos(-a), Math.Sin(-a)); } else if (Data.Blocks[i].Pitch != 0.0) { double p = Data.Blocks[i].Pitch; double d = Data.BlockInterval; c = d / Math.Sqrt(1.0 + p * p); h = c * p; } double TrackYaw = Math.Atan2(Direction.X, Direction.Y); double TrackPitch = Math.Atan(Data.Blocks[i].Pitch); World.Transformation GroundTransformation = new World.Transformation(TrackYaw, 0.0, 0.0); World.Transformation TrackTransformation = new World.Transformation(TrackYaw, TrackPitch, 0.0); World.Transformation NullTransformation = new World.Transformation(0.0, 0.0, 0.0); // ground if (!PreviewOnly) { int cb = (int)Math.Floor((double)i + 0.001); int ci = (cb % Data.Blocks[i].Cycle.Length + Data.Blocks[i].Cycle.Length) % Data.Blocks[i].Cycle.Length; int gi = Data.Blocks[i].Cycle[ci]; if (gi >= 0 & gi < Data.Structure.Ground.Length) { if (Data.Structure.Ground[gi] != null) { ObjectManager.CreateObject(Data.Structure.Ground[Data.Blocks[i].Cycle[ci]], Position + new Vector3(0.0, -Data.Blocks[i].Height, 0.0), GroundTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } // ground-aligned free objects if (!PreviewOnly) { for (int j = 0; j < Data.Blocks[i].GroundFreeObj.Length; j++) { int sttype = Data.Blocks[i].GroundFreeObj[j].Type; double d = Data.Blocks[i].GroundFreeObj[j].TrackPosition - StartingDistance; double dx = Data.Blocks[i].GroundFreeObj[j].X; double dy = Data.Blocks[i].GroundFreeObj[j].Y; Vector3 wpos = Position + new Vector3(Direction.X * d + Direction.Y * dx, dy - Data.Blocks[i].Height, Direction.Y * d - Direction.X * dx); double tpos = Data.Blocks[i].GroundFreeObj[j].TrackPosition; ObjectManager.CreateObject(Data.Structure.FreeObj[sttype], wpos, GroundTransformation, new World.Transformation(Data.Blocks[i].GroundFreeObj[j].Yaw, Data.Blocks[i].GroundFreeObj[j].Pitch, Data.Blocks[i].GroundFreeObj[j].Roll), Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos); } } // rail-aligned objects if (!PreviewOnly) { for (int j = 0; j < Data.Blocks[i].Rail.Length; j++) { if (j > 0 && !Data.Blocks[i].Rail[j].RailStart) continue; // rail Vector3 pos; World.Transformation RailTransformation; double planar, updown; if (j == 0) { // rail 0 pos = Position; planar = 0.0; updown = 0.0; RailTransformation = new World.Transformation(TrackTransformation, planar, updown, 0.0); pos = Position; } else { // rails 1-infinity double x = Data.Blocks[i].Rail[j].RailStartX; double y = Data.Blocks[i].Rail[j].RailStartY; Vector3 offset = new Vector3(Direction.Y * x, y, -Direction.X * x); pos = Position + offset; double dh; if (i < Data.Blocks.Length - 1 && Data.Blocks[i + 1].Rail.Length > j) { // take orientation of upcoming block into account World.Vector2D Direction2 = Direction; Vector3 Position2 = Position; Position2.X += Direction.X * c; Position2.Y += h; Position2.Z += Direction.Y * c; if (a != 0.0) { World.Rotate(ref Direction2, Math.Cos(-a), Math.Sin(-a)); } if (Data.Blocks[i + 1].Turn != 0.0) { double ag = -Math.Atan(Data.Blocks[i + 1].Turn); double cosag = Math.Cos(ag); double sinag = Math.Sin(ag); World.Rotate(ref Direction2, cosag, sinag); } double a2 = 0.0; double c2 = Data.BlockInterval; double h2 = 0.0; if (Data.Blocks[i + 1].CurrentTrackState.CurveRadius != 0.0 & Data.Blocks[i + 1].Pitch != 0.0) { double d2 = Data.BlockInterval; double p2 = Data.Blocks[i + 1].Pitch; double r2 = Data.Blocks[i + 1].CurrentTrackState.CurveRadius; double s2 = d2 / Math.Sqrt(1.0 + p2 * p2); h2 = s2 * p2; double b2 = s2 / Math.Abs(r2); c2 = Math.Sqrt(2.0 * r2 * r2 * (1.0 - Math.Cos(b2))); a2 = 0.5 * (double)Math.Sign(r2) * b2; World.Rotate(ref Direction2, Math.Cos(-a2), Math.Sin(-a2)); } else if (Data.Blocks[i + 1].CurrentTrackState.CurveRadius != 0.0) { double d2 = Data.BlockInterval; double r2 = Data.Blocks[i + 1].CurrentTrackState.CurveRadius; double b2 = d2 / Math.Abs(r2); c2 = Math.Sqrt(2.0 * r2 * r2 * (1.0 - Math.Cos(b2))); a2 = 0.5 * (double)Math.Sign(r2) * b2; World.Rotate(ref Direction2, Math.Cos(-a2), Math.Sin(-a2)); } else if (Data.Blocks[i + 1].Pitch != 0.0) { double p2 = Data.Blocks[i + 1].Pitch; double d2 = Data.BlockInterval; c2 = d2 / Math.Sqrt(1.0 + p2 * p2); h2 = c2 * p2; } double TrackYaw2 = Math.Atan2(Direction2.X, Direction2.Y); double TrackPitch2 = Math.Atan(Data.Blocks[i + 1].Pitch); World.Transformation GroundTransformation2 = new World.Transformation(TrackYaw2, 0.0, 0.0); World.Transformation TrackTransformation2 = new World.Transformation(TrackYaw2, TrackPitch2, 0.0); double x2 = Data.Blocks[i + 1].Rail[j].RailEndX; double y2 = Data.Blocks[i + 1].Rail[j].RailEndY; Vector3 offset2 = new Vector3(Direction2.Y * x2, y2, -Direction2.X * x2); Vector3 pos2 = Position2 + offset2; double rx = pos2.X - pos.X; double ry = pos2.Y - pos.Y; double rz = pos2.Z - pos.Z; World.Normalize(ref rx, ref ry, ref rz); RailTransformation.Z = new Vector3(rx, ry, rz); RailTransformation.X = new Vector3(rz, 0.0, -rx); World.Normalize(ref RailTransformation.X.X, ref RailTransformation.X.Z); RailTransformation.Y = Vector3.Cross(RailTransformation.Z, RailTransformation.X); double dx = Data.Blocks[i + 1].Rail[j].RailEndX - Data.Blocks[i].Rail[j].RailStartX; double dy = Data.Blocks[i + 1].Rail[j].RailEndY - Data.Blocks[i].Rail[j].RailStartY; planar = Math.Atan(dx / c); dh = dy / c; updown = Math.Atan(dh); } else { planar = 0.0; dh = 0.0; updown = 0.0; RailTransformation = new World.Transformation(TrackTransformation, 0.0, 0.0, 0.0); } } if (Data.Blocks[i].RailType[j] < Data.Structure.Rail.Length) { if (Data.Structure.Rail[Data.Blocks[i].RailType[j]] != null) { ObjectManager.CreateObject(Data.Structure.Rail[Data.Blocks[i].RailType[j]], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } // points of interest for (int k = 0; k < Data.Blocks[i].PointsOfInterest.Length; k++) { if (Data.Blocks[i].PointsOfInterest[k].RailIndex == j) { double d = Data.Blocks[i].PointsOfInterest[k].TrackPosition - StartingDistance; double x = Data.Blocks[i].PointsOfInterest[k].X; double y = Data.Blocks[i].PointsOfInterest[k].Y; int m = Game.PointsOfInterest.Length; Array.Resize(ref Game.PointsOfInterest, m + 1); Game.PointsOfInterest[m].TrackPosition = Data.Blocks[i].PointsOfInterest[k].TrackPosition; if (i < Data.Blocks.Length - 1 && Data.Blocks[i + 1].Rail.Length > j) { double dx = Data.Blocks[i + 1].Rail[j].RailEndX - Data.Blocks[i].Rail[j].RailStartX; double dy = Data.Blocks[i + 1].Rail[j].RailEndY - Data.Blocks[i].Rail[j].RailStartY; dx = Data.Blocks[i].Rail[j].RailStartX + d / Data.BlockInterval * dx; dy = Data.Blocks[i].Rail[j].RailStartY + d / Data.BlockInterval * dy; Game.PointsOfInterest[m].TrackOffset = new Vector3(x + dx, y + dy, 0.0); } else { double dx = Data.Blocks[i].Rail[j].RailStartX; double dy = Data.Blocks[i].Rail[j].RailStartY; Game.PointsOfInterest[m].TrackOffset = new Vector3(x + dx, y + dy, 0.0); } Game.PointsOfInterest[m].TrackYaw = Data.Blocks[i].PointsOfInterest[k].Yaw + planar; Game.PointsOfInterest[m].TrackPitch = Data.Blocks[i].PointsOfInterest[k].Pitch + updown; Game.PointsOfInterest[m].TrackRoll = Data.Blocks[i].PointsOfInterest[k].Roll; Game.PointsOfInterest[m].Text = Data.Blocks[i].PointsOfInterest[k].Text; } } // poles if (Data.Blocks[i].RailPole.Length > j && Data.Blocks[i].RailPole[j].Exists) { double dz = StartingDistance / Data.Blocks[i].RailPole[j].Interval; dz -= Math.Floor(dz + 0.5); if (dz >= -0.01 & dz <= 0.01) { if (Data.Blocks[i].RailPole[j].Mode == 0) { if (Data.Blocks[i].RailPole[j].Location <= 0.0) { ObjectManager.CreateObject(Data.Structure.Poles[0][Data.Blocks[i].RailPole[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } else { ObjectManager.UnifiedObject Pole = GetMirroredObject(Data.Structure.Poles[0][Data.Blocks[i].RailPole[j].Type]); ObjectManager.CreateObject(Pole, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } else { int m = Data.Blocks[i].RailPole[j].Mode; double dx = -Data.Blocks[i].RailPole[j].Location * 3.8; double wa = Math.Atan2(Direction.Y, Direction.X) - planar; double wx = Math.Cos(wa); double wy = Math.Tan(updown); double wz = Math.Sin(wa); World.Normalize(ref wx, ref wy, ref wz); double sx = Direction.Y; double sy = 0.0; double sz = -Direction.X; Vector3 wpos = pos + new Vector3(sx * dx + wx * dz, sy * dx + wy * dz, sz * dx + wz * dz); int type = Data.Blocks[i].RailPole[j].Type; ObjectManager.CreateObject(Data.Structure.Poles[m][type], wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } // walls if (Data.Blocks[i].RailWall.Length > j && Data.Blocks[i].RailWall[j].Exists) { if (Data.Blocks[i].RailWall[j].Direction <= 0) { ObjectManager.CreateObject(Data.Structure.WallL[Data.Blocks[i].RailWall[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].RailWall[j].Direction >= 0) { ObjectManager.CreateObject(Data.Structure.WallR[Data.Blocks[i].RailWall[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } // dikes if (Data.Blocks[i].RailDike.Length > j && Data.Blocks[i].RailDike[j].Exists) { if (Data.Blocks[i].RailDike[j].Direction <= 0) { ObjectManager.CreateObject(Data.Structure.DikeL[Data.Blocks[i].RailDike[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].RailDike[j].Direction >= 0) { ObjectManager.CreateObject(Data.Structure.DikeR[Data.Blocks[i].RailDike[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } // sounds if (j == 0) { for (int k = 0; k < Data.Blocks[i].Sound.Length; k++) { if (Data.Blocks[i].Sound[k].Type == SoundType.World) { if (Data.Blocks[i].Sound[k].SoundBuffer != null) { double d = Data.Blocks[i].Sound[k].TrackPosition - StartingDistance; double dx = Data.Blocks[i].Sound[k].X; double dy = Data.Blocks[i].Sound[k].Y; double wa = Math.Atan2(Direction.Y, Direction.X) - planar; double wx = Math.Cos(wa); double wy = Math.Tan(updown); double wz = Math.Sin(wa); World.Normalize(ref wx, ref wy, ref wz); double sx = Direction.Y; double sy = 0.0; double sz = -Direction.X; double ux, uy, uz; World.Cross(wx, wy, wz, sx, sy, sz, out ux, out uy, out uz); Vector3 wpos = pos + new Vector3(sx * dx + ux * dy + wx * d, sy * dx + uy * dy + wy * d, sz * dx + uz * dy + wz * d); Sounds.PlaySound(Data.Blocks[i].Sound[k].SoundBuffer, 1.0, 1.0, wpos, true); } } } } // forms for (int k = 0; k < Data.Blocks[i].Form.Length; k++) { // primary rail if (Data.Blocks[i].Form[k].PrimaryRail == j) { if (Data.Blocks[i].Form[k].SecondaryRail == Form.SecondaryRailStub) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } } else if (Data.Blocks[i].Form[k].SecondaryRail == Form.SecondaryRailL) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCL.Length || Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateStaticObject(Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCL.Length || Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateStaticObject(Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } else if (Data.Blocks[i].Form[k].SecondaryRail == Form.SecondaryRailR) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormR.Length || Data.Structure.FormR[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCR.Length || Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateStaticObject(Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofR.Length || Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCR.Length || Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateStaticObject(Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } else if (Data.Blocks[i].Form[k].SecondaryRail > 0) { int p = Data.Blocks[i].Form[k].PrimaryRail; double px0 = p > 0 ? Data.Blocks[i].Rail[p].RailStartX : 0.0; double px1 = p > 0 ? Data.Blocks[i + 1].Rail[p].RailEndX : 0.0; int s = Data.Blocks[i].Form[k].SecondaryRail; if (s < 0 || s >= Data.Blocks[i].Rail.Length || !Data.Blocks[i].Rail[s].RailStart) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex2 is out of range in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName); } else { double sx0 = Data.Blocks[i].Rail[s].RailStartX; double sx1 = Data.Blocks[i + 1].Rail[s].RailEndX; double d0 = sx0 - px0; double d1 = sx1 - px1; if (d0 < 0.0) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCL.Length || Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject FormC = GetTransformedStaticObject(Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType], d0, d1); ObjectManager.CreateStaticObject(FormC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCL.Length || Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject RoofC = GetTransformedStaticObject(Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType], d0, d1); ObjectManager.CreateStaticObject(RoofC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } else if (d0 > 0.0) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormR.Length || Data.Structure.FormR[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCR.Length || Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject FormC = GetTransformedStaticObject(Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType], d0, d1); ObjectManager.CreateStaticObject(FormC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofR.Length || Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCR.Length || Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject RoofC = GetTransformedStaticObject(Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType], d0, d1); ObjectManager.CreateStaticObject(RoofC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } } } } // secondary rail if (Data.Blocks[i].Form[k].SecondaryRail == j) { int p = Data.Blocks[i].Form[k].PrimaryRail; double px = p > 0 ? Data.Blocks[i].Rail[p].RailStartX : 0.0; int s = Data.Blocks[i].Form[k].SecondaryRail; double sx = Data.Blocks[i].Rail[s].RailStartX; double d = px - sx; if (d < 0.0) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } else { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormR.Length || Data.Structure.FormR[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofR.Length || Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } } } // cracks for (int k = 0; k < Data.Blocks[i].Crack.Length; k++) { if (Data.Blocks[i].Crack[k].PrimaryRail == j) { int p = Data.Blocks[i].Crack[k].PrimaryRail; double px0 = p > 0 ? Data.Blocks[i].Rail[p].RailStartX : 0.0; double px1 = p > 0 ? Data.Blocks[i + 1].Rail[p].RailEndX : 0.0; int s = Data.Blocks[i].Crack[k].SecondaryRail; if (s < 0 || s >= Data.Blocks[i].Rail.Length || !Data.Blocks[i].Rail[s].RailStart) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex2 is out of range in Track.Crack at track position " + StartingDistance.ToString(Culture) + " in file " + FileName); } else { double sx0 = Data.Blocks[i].Rail[s].RailStartX; double sx1 = Data.Blocks[i + 1].Rail[s].RailEndX; double d0 = sx0 - px0; double d1 = sx1 - px1; if (d0 < 0.0) { if (Data.Blocks[i].Crack[k].Type >= Data.Structure.CrackL.Length || Data.Structure.CrackL[Data.Blocks[i].Crack[k].Type] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "CrackStructureIndex references a CrackL not loaded in Track.Crack at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject Crack = GetTransformedStaticObject(Data.Structure.CrackL[Data.Blocks[i].Crack[k].Type], d0, d1); ObjectManager.CreateStaticObject(Crack, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } else if (d0 > 0.0) { if (Data.Blocks[i].Crack[k].Type >= Data.Structure.CrackR.Length || Data.Structure.CrackR[Data.Blocks[i].Crack[k].Type] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "CrackStructureIndex references a CrackR not loaded in Track.Crack at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject Crack = GetTransformedStaticObject(Data.Structure.CrackR[Data.Blocks[i].Crack[k].Type], d0, d1); ObjectManager.CreateStaticObject(Crack, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } } } // free objects if (Data.Blocks[i].RailFreeObj.Length > j && Data.Blocks[i].RailFreeObj[j] != null) { for (int k = 0; k < Data.Blocks[i].RailFreeObj[j].Length; k++) { int sttype = Data.Blocks[i].RailFreeObj[j][k].Type; double dx = Data.Blocks[i].RailFreeObj[j][k].X; double dy = Data.Blocks[i].RailFreeObj[j][k].Y; double dz = Data.Blocks[i].RailFreeObj[j][k].TrackPosition - StartingDistance; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dy * RailTransformation.Y.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dy * RailTransformation.Y.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dy * RailTransformation.Y.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].RailFreeObj[j][k].TrackPosition; ObjectManager.CreateObject(Data.Structure.FreeObj[sttype], wpos, RailTransformation, new World.Transformation(Data.Blocks[i].RailFreeObj[j][k].Yaw, Data.Blocks[i].RailFreeObj[j][k].Pitch, Data.Blocks[i].RailFreeObj[j][k].Roll), -1, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, 1.0, false); } } // transponder objects if (j == 0) { for (int k = 0; k < Data.Blocks[i].Transponder.Length; k++) { ObjectManager.UnifiedObject obj = null; if (Data.Blocks[i].Transponder[k].ShowDefaultObject) { switch (Data.Blocks[i].Transponder[k].Type) { case 0: obj = TransponderS; break; case 1: obj = TransponderSN; break; case 2: obj = TransponderFalseStart; break; case 3: obj = TransponderPOrigin; break; case 4: obj = TransponderPStop; break; } } else { int b = Data.Blocks[i].Transponder[k].BeaconStructureIndex; if (b >= 0 & b < Data.Structure.Beacon.Length) { obj = Data.Structure.Beacon[b]; } } if (obj != null) { double dx = Data.Blocks[i].Transponder[k].X; double dy = Data.Blocks[i].Transponder[k].Y; double dz = Data.Blocks[i].Transponder[k].TrackPosition - StartingDistance; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dy * RailTransformation.Y.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dy * RailTransformation.Y.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dy * RailTransformation.Y.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].Transponder[k].TrackPosition; if (Data.Blocks[i].Transponder[k].ShowDefaultObject) { double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos); ObjectManager.CreateObject(obj, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Transponder[k].Yaw, Data.Blocks[i].Transponder[k].Pitch, Data.Blocks[i].Transponder[k].Roll), -1, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } else { ObjectManager.CreateObject(obj, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Transponder[k].Yaw, Data.Blocks[i].Transponder[k].Pitch, Data.Blocks[i].Transponder[k].Roll), Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos); } } } } // sections/signals/transponders if (j == 0) { // signals for (int k = 0; k < Data.Blocks[i].Signal.Length; k++) { SignalData sd; if (Data.Blocks[i].Signal[k].SignalCompatibilityObjectIndex >= 0) { sd = Data.CompatibilitySignalData[Data.Blocks[i].Signal[k].SignalCompatibilityObjectIndex]; } else { sd = Data.SignalData[Data.Blocks[i].Signal[k].SignalObjectIndex]; } // objects double dz = Data.Blocks[i].Signal[k].TrackPosition - StartingDistance; if (Data.Blocks[i].Signal[k].ShowPost) { // post double dx = Data.Blocks[i].Signal[k].X; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].Signal[k].TrackPosition; double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos); ObjectManager.CreateStaticObject(SignalPost, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } if (Data.Blocks[i].Signal[k].ShowObject) { // signal object double dx = Data.Blocks[i].Signal[k].X; double dy = Data.Blocks[i].Signal[k].Y; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dy * RailTransformation.Y.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dy * RailTransformation.Y.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dy * RailTransformation.Y.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].Signal[k].TrackPosition; if (sd is AnimatedObjectSignalData) { AnimatedObjectSignalData aosd = (AnimatedObjectSignalData)sd; ObjectManager.CreateObject(aosd.Objects, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Signal[k].Yaw, Data.Blocks[i].Signal[k].Pitch, Data.Blocks[i].Signal[k].Roll), Data.Blocks[i].Signal[k].Section, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, 1.0, false); } else if (sd is CompatibilitySignalData) { CompatibilitySignalData csd = (CompatibilitySignalData)sd; if (csd.Numbers.Length != 0) { double brightness = 0.25 + 0.75 * GetBrightness(ref Data, tpos); ObjectManager.AnimatedObjectCollection aoc = new ObjectManager.AnimatedObjectCollection(); aoc.Objects = new ObjectManager.AnimatedObject[1]; aoc.Objects[0] = new ObjectManager.AnimatedObject(); aoc.Objects[0].States = new ObjectManager.AnimatedObjectState[csd.Numbers.Length]; for (int l = 0; l < csd.Numbers.Length; l++) { aoc.Objects[0].States[l].Object = ObjectManager.CloneObject(csd.Objects[l]); } string expr = ""; for (int l = 0; l < csd.Numbers.Length - 1; l++) { expr += "section " + csd.Numbers[l].ToString(Culture) + " <= " + l.ToString(Culture) + " "; } expr += (csd.Numbers.Length - 1).ToString(Culture); for (int l = 0; l < csd.Numbers.Length - 1; l++) { expr += " ?"; } aoc.Objects[0].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(expr); aoc.Objects[0].RefreshRate = 1.0 + 0.01 * Program.RandomNumberGenerator.NextDouble(); ObjectManager.CreateObject(aoc, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Signal[k].Yaw, Data.Blocks[i].Signal[k].Pitch, Data.Blocks[i].Signal[k].Roll), Data.Blocks[i].Signal[k].Section, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, brightness, false); } } else if (sd is Bve4SignalData) { Bve4SignalData b4sd = (Bve4SignalData)sd; if (b4sd.SignalTextures.Length != 0) { int m = Math.Max(b4sd.SignalTextures.Length, b4sd.GlowTextures.Length); int zn = 0; for (int l = 0; l < m; l++) { if (l < b4sd.SignalTextures.Length && b4sd.SignalTextures[l] != null || l < b4sd.GlowTextures.Length && b4sd.GlowTextures[l] != null) { zn++; } } ObjectManager.AnimatedObjectCollection aoc = new ObjectManager.AnimatedObjectCollection(); aoc.Objects = new ObjectManager.AnimatedObject[1]; aoc.Objects[0] = new ObjectManager.AnimatedObject(); aoc.Objects[0].States = new ObjectManager.AnimatedObjectState[zn]; int zi = 0; string expr = ""; for (int l = 0; l < m; l++) { bool qs = l < b4sd.SignalTextures.Length && b4sd.SignalTextures[l] != null; bool qg = l < b4sd.GlowTextures.Length && b4sd.GlowTextures[l] != null; if (qs & qg) { ObjectManager.StaticObject so = ObjectManager.CloneObject(b4sd.BaseObject, b4sd.SignalTextures[l], null); ObjectManager.StaticObject go = ObjectManager.CloneObject(b4sd.GlowObject, b4sd.GlowTextures[l], null); ObjectManager.JoinObjects(ref so, go); aoc.Objects[0].States[zi].Object = so; } else if (qs) { ObjectManager.StaticObject so = ObjectManager.CloneObject(b4sd.BaseObject, b4sd.SignalTextures[l], null); aoc.Objects[0].States[zi].Object = so; } else if (qg) { ObjectManager.StaticObject go = ObjectManager.CloneObject(b4sd.GlowObject, b4sd.GlowTextures[l], null); aoc.Objects[0].States[zi].Object = go; } if (qs | qg) { if (zi < zn - 1) { expr += "section " + l.ToString(Culture) + " <= " + zi.ToString(Culture) + " "; } else { expr += zi.ToString(Culture); } zi++; } } for (int l = 0; l < zn - 1; l++) { expr += " ?"; } aoc.Objects[0].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(expr); aoc.Objects[0].RefreshRate = 1.0 + 0.01 * Program.RandomNumberGenerator.NextDouble(); ObjectManager.CreateObject(aoc, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Signal[k].Yaw, Data.Blocks[i].Signal[k].Pitch, Data.Blocks[i].Signal[k].Roll), Data.Blocks[i].Signal[k].Section, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, 1.0, false); } } } } // sections for (int k = 0; k < Data.Blocks[i].Section.Length; k++) { int m = Game.Sections.Length; Array.Resize(ref Game.Sections, m + 1); Game.Sections[m].SignalIndices = new int[] { }; // create associated transponders for (int g = 0; g <= i; g++) { for (int l = 0; l < Data.Blocks[g].Transponder.Length; l++) { if (Data.Blocks[g].Transponder[l].Type != -1 & Data.Blocks[g].Transponder[l].Section == m) { int o = TrackManager.CurrentTrack.Elements[n - i + g].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n - i + g].Events, o + 1); double dt = Data.Blocks[g].Transponder[l].TrackPosition - StartingDistance + (double)(i - g) * Data.BlockInterval; TrackManager.CurrentTrack.Elements[n - i + g].Events[o] = new TrackManager.TransponderEvent(dt, Data.Blocks[g].Transponder[l].Type, Data.Blocks[g].Transponder[l].Data, m); Data.Blocks[g].Transponder[l].Type = -1; } } } // create section Game.Sections[m].TrackPosition = Data.Blocks[i].Section[k].TrackPosition; Game.Sections[m].Aspects = new Game.SectionAspect[Data.Blocks[i].Section[k].Aspects.Length]; for (int l = 0; l < Data.Blocks[i].Section[k].Aspects.Length; l++) { Game.Sections[m].Aspects[l].Number = Data.Blocks[i].Section[k].Aspects[l]; if (Data.Blocks[i].Section[k].Aspects[l] >= 0 & Data.Blocks[i].Section[k].Aspects[l] < Data.SignalSpeeds.Length) { Game.Sections[m].Aspects[l].Speed = Data.SignalSpeeds[Data.Blocks[i].Section[k].Aspects[l]]; } else { Game.Sections[m].Aspects[l].Speed = double.PositiveInfinity; } } Game.Sections[m].Type = Data.Blocks[i].Section[k].Type; Game.Sections[m].CurrentAspect = -1; if (m > 0) { Game.Sections[m].PreviousSection = m - 1; Game.Sections[m - 1].NextSection = m; } else { Game.Sections[m].PreviousSection = -1; } Game.Sections[m].NextSection = -1; Game.Sections[m].StationIndex = Data.Blocks[i].Section[k].DepartureStationIndex; Game.Sections[m].Invisible = Data.Blocks[i].Section[k].Invisible; Game.Sections[m].Trains = new TrainManager.Train[] { }; // create section change event double d = Data.Blocks[i].Section[k].TrackPosition - StartingDistance; int p = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, p + 1); TrackManager.CurrentTrack.Elements[n].Events[p] = new TrackManager.SectionChangeEvent(d, m - 1, m); } // transponders introduced after corresponding sections for (int l = 0; l < Data.Blocks[i].Transponder.Length; l++) { if (Data.Blocks[i].Transponder[l].Type != -1) { int t = Data.Blocks[i].Transponder[l].Section; if (t >= 0 & t < Game.Sections.Length) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double dt = Data.Blocks[i].Transponder[l].TrackPosition - StartingDistance; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.TransponderEvent(dt, Data.Blocks[i].Transponder[l].Type, Data.Blocks[i].Transponder[l].Data, t); Data.Blocks[i].Transponder[l].Type = -1; } } } } // limit if (j == 0) { for (int k = 0; k < Data.Blocks[i].Limit.Length; k++) { if (Data.Blocks[i].Limit[k].Direction != 0) { double dx = 2.2 * (double)Data.Blocks[i].Limit[k].Direction; double dz = Data.Blocks[i].Limit[k].TrackPosition - StartingDistance; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].Limit[k].TrackPosition; double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos); if (Data.Blocks[i].Limit[k].Speed <= 0.0 | Data.Blocks[i].Limit[k].Speed >= 1000.0) { ObjectManager.CreateStaticObject(LimitPostInfinite, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } else { if (Data.Blocks[i].Limit[k].Cource < 0) { ObjectManager.CreateStaticObject(LimitPostLeft, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } else if (Data.Blocks[i].Limit[k].Cource > 0) { ObjectManager.CreateStaticObject(LimitPostRight, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } else { ObjectManager.CreateStaticObject(LimitPostStraight, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } double lim = Data.Blocks[i].Limit[k].Speed / Data.UnitOfSpeed; if (lim < 10.0) { int d0 = (int)Math.Round(lim); int o = ObjectManager.CreateStaticObject(LimitOneDigit, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, true); if (ObjectManager.Objects[o].Mesh.Materials.Length >= 1) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d0 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[0].DaytimeTexture); } } else if (lim < 100.0) { int d1 = (int)Math.Round(lim); int d0 = d1 % 10; d1 /= 10; int o = ObjectManager.CreateStaticObject(LimitTwoDigits, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, true); if (ObjectManager.Objects[o].Mesh.Materials.Length >= 1) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d1 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[0].DaytimeTexture); } if (ObjectManager.Objects[o].Mesh.Materials.Length >= 2) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d0 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[1].DaytimeTexture); } } else { int d2 = (int)Math.Round(lim); int d0 = d2 % 10; int d1 = (d2 / 10) % 10; d2 /= 100; int o = ObjectManager.CreateStaticObject(LimitThreeDigits, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, true); if (ObjectManager.Objects[o].Mesh.Materials.Length >= 1) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d2 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[0].DaytimeTexture); } if (ObjectManager.Objects[o].Mesh.Materials.Length >= 2) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d1 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[1].DaytimeTexture); } if (ObjectManager.Objects[o].Mesh.Materials.Length >= 3) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d0 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[2].DaytimeTexture); } } } } } } // stop if (j == 0) { for (int k = 0; k < Data.Blocks[i].Stop.Length; k++) { if (Data.Blocks[i].Stop[k].Direction != 0) { double dx = 1.8 * (double)Data.Blocks[i].Stop[k].Direction; double dz = Data.Blocks[i].Stop[k].TrackPosition - StartingDistance; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].Stop[k].TrackPosition; double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos); ObjectManager.CreateStaticObject(StopPost, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } } } } } // finalize block Position.X += Direction.X * c; Position.Y += h; Position.Z += Direction.Y * c; if (a != 0.0) { World.Rotate(ref Direction, Math.Cos(-a), Math.Sin(-a)); } } // orphaned transponders if (!PreviewOnly) { for (int i = Data.FirstUsedBlock; i < Data.Blocks.Length; i++) { for (int j = 0; j < Data.Blocks[i].Transponder.Length; j++) { if (Data.Blocks[i].Transponder[j].Type != -1) { int n = i - Data.FirstUsedBlock; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Blocks[i].Transponder[j].TrackPosition - TrackManager.CurrentTrack.Elements[n].StartingTrackPosition; int s = Data.Blocks[i].Transponder[j].Section; if (s >= 0) s = -1; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.TransponderEvent(d, Data.Blocks[i].Transponder[j].Type, Data.Blocks[i].Transponder[j].Data, s); Data.Blocks[i].Transponder[j].Type = -1; } } } } // insert station end events for (int i = 0; i < Game.Stations.Length; i++) { int j = Game.Stations[i].Stops.Length - 1; if (j >= 0) { double p = Game.Stations[i].Stops[j].TrackPosition + Game.Stations[i].Stops[j].ForwardTolerance + Data.BlockInterval; int k = (int)Math.Floor(p / (double)Data.BlockInterval) - Data.FirstUsedBlock; if (k >= 0 & k < Data.Blocks.Length) { double d = p - (double)(k + Data.FirstUsedBlock) * (double)Data.BlockInterval; int m = TrackManager.CurrentTrack.Elements[k].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[k].Events, m + 1); TrackManager.CurrentTrack.Elements[k].Events[m] = new TrackManager.StationEndEvent(d, i); } } } // create default point of interests if (Game.PointsOfInterest.Length == 0) { Game.PointsOfInterest = new OpenBve.Game.PointOfInterest[Game.Stations.Length]; int n = 0; for (int i = 0; i < Game.Stations.Length; i++) { if (Game.Stations[i].Stops.Length != 0) { Game.PointsOfInterest[n].Text = Game.Stations[i].Name; Game.PointsOfInterest[n].TrackPosition = Game.Stations[i].Stops[0].TrackPosition; Game.PointsOfInterest[n].TrackOffset = new Vector3(0.0, 2.8, 0.0); if (Game.Stations[i].OpenLeftDoors & !Game.Stations[i].OpenRightDoors) { Game.PointsOfInterest[n].TrackOffset.X = -2.5; } else if (!Game.Stations[i].OpenLeftDoors & Game.Stations[i].OpenRightDoors) { Game.PointsOfInterest[n].TrackOffset.X = 2.5; } n++; } } Array.Resize(ref Game.PointsOfInterest, n); } // convert block-based cant into point-based cant for (int i = CurrentTrackLength - 1; i >= 1; i--) { if (TrackManager.CurrentTrack.Elements[i].CurveCant == 0.0) { TrackManager.CurrentTrack.Elements[i].CurveCant = TrackManager.CurrentTrack.Elements[i - 1].CurveCant; } else if (TrackManager.CurrentTrack.Elements[i - 1].CurveCant != 0.0) { if (Math.Sign(TrackManager.CurrentTrack.Elements[i - 1].CurveCant) == Math.Sign(TrackManager.CurrentTrack.Elements[i].CurveCant)) { if (Math.Abs(TrackManager.CurrentTrack.Elements[i - 1].CurveCant) > Math.Abs(TrackManager.CurrentTrack.Elements[i].CurveCant)) { TrackManager.CurrentTrack.Elements[i].CurveCant = TrackManager.CurrentTrack.Elements[i - 1].CurveCant; } } else { TrackManager.CurrentTrack.Elements[i].CurveCant = 0.5 * (TrackManager.CurrentTrack.Elements[i].CurveCant + TrackManager.CurrentTrack.Elements[i - 1].CurveCant); } } } // finalize Array.Resize(ref TrackManager.CurrentTrack.Elements, CurrentTrackLength); for (int i = 0; i < Game.Stations.Length; i++) { if (Game.Stations[i].Stops.Length == 0 & Game.Stations[i].StopMode != Game.StationStopMode.AllPass) { Interface.AddMessage(Interface.MessageType.Warning, false, "Station " + Game.Stations[i].Name + " expects trains to stop but does not define stop points at track position " + Game.Stations[i].DefaultTrackPosition.ToString(Culture) + " in file " + FileName); Game.Stations[i].StopMode = Game.StationStopMode.AllPass; } if (Game.Stations[i].StationType == Game.StationType.ChangeEnds) { if (i < Game.Stations.Length - 1) { if (Game.Stations[i + 1].StopMode != Game.StationStopMode.AllStop) { Interface.AddMessage(Interface.MessageType.Warning, false, "Station " + Game.Stations[i].Name + " is marked as \"change ends\" but the subsequent station does not expect all trains to stop in file " + FileName); Game.Stations[i + 1].StopMode = Game.StationStopMode.AllStop; } } else { Interface.AddMessage(Interface.MessageType.Warning, false, "Station " + Game.Stations[i].Name + " is marked as \"change ends\" but there is no subsequent station defined in file " + FileName); Game.Stations[i].StationType = Game.StationType.Terminal; } } } if (Game.Stations.Length != 0) { Game.Stations[Game.Stations.Length - 1].StationType = Game.StationType.Terminal; } if (TrackManager.CurrentTrack.Elements.Length != 0) { int n = TrackManager.CurrentTrack.Elements.Length - 1; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.TrackEndEvent(Data.BlockInterval); } // insert compatibility beacons if (!PreviewOnly) { List transponders = new List(); bool atc = false; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (!atc) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent station = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Atc) { Array.Resize(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 0, 0); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 1, 0); atc = true; } } } else { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent station = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Ats) { Array.Resize(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 2, 0); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 3, 0); } } else if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationEndEvent) { TrackManager.StationEndEvent station = (TrackManager.StationEndEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Atc) { Array.Resize(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 1, 0); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 2, 0); } else if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Ats) { Array.Resize(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 3, 0); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 0, 0); atc = false; } } else if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.LimitChangeEvent) { TrackManager.LimitChangeEvent limit = (TrackManager.LimitChangeEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; int speed = (int)Math.Round(Math.Min(4095.0, 3.6 * limit.NextSpeedLimit)); int distance = Math.Min(1048575, (int)Math.Round(TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + limit.TrackPositionDelta)); unchecked { int value = (int)((uint)speed | ((uint)distance << 12)); transponders.Add(new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcSpeedLimit, value, 0)); } } } if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.TransponderEvent) { TrackManager.TransponderEvent transponder = TrackManager.CurrentTrack.Elements[i].Events[j] as TrackManager.TransponderEvent; if (transponder.Type == TrackManager.SpecialTransponderTypes.InternalAtsPTemporarySpeedLimit) { int speed = Math.Min(4095, transponder.Data); int distance = Math.Min(1048575, (int)Math.Round(TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + transponder.TrackPositionDelta)); unchecked { int value = (int)((uint)speed | ((uint)distance << 12)); transponder.DontTriggerAnymore = true; } } } } } int n = TrackManager.CurrentTrack.Elements[0].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[0].Events, n + transponders.Count); for (int i = 0; i < transponders.Count; i++) { TrackManager.CurrentTrack.Elements[0].Events[n + i] = transponders[i]; } } // cant if (!PreviewOnly) { ComputeCantTangents(); int subdivisions = (int)Math.Floor(Data.BlockInterval / 5.0); if (subdivisions >= 2) { SmoothenOutTurns(subdivisions); ComputeCantTangents(); } } } // ------------------ // compute cant tangents private static void ComputeCantTangents() { if (TrackManager.CurrentTrack.Elements.Length == 1) { TrackManager.CurrentTrack.Elements[0].CurveCantTangent = 0.0; } else if (TrackManager.CurrentTrack.Elements.Length != 0) { double[] deltas = new double[TrackManager.CurrentTrack.Elements.Length - 1]; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length - 1; i++) { deltas[i] = TrackManager.CurrentTrack.Elements[i + 1].CurveCant - TrackManager.CurrentTrack.Elements[i].CurveCant; } double[] tangents = new double[TrackManager.CurrentTrack.Elements.Length]; tangents[0] = deltas[0]; tangents[TrackManager.CurrentTrack.Elements.Length - 1] = deltas[TrackManager.CurrentTrack.Elements.Length - 2]; for (int i = 1; i < TrackManager.CurrentTrack.Elements.Length - 1; i++) { tangents[i] = 0.5 * (deltas[i - 1] + deltas[i]); } for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length - 1; i++) { if (deltas[i] == 0.0) { tangents[i] = 0.0; tangents[i + 1] = 0.0; } else { double a = tangents[i] / deltas[i]; double b = tangents[i + 1] / deltas[i]; if (a * a + b * b > 9.0) { double t = 3.0 / Math.Sqrt(a * a + b * b); tangents[i] = t * a * deltas[i]; tangents[i + 1] = t * b * deltas[i]; } } } for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { TrackManager.CurrentTrack.Elements[i].CurveCantTangent = tangents[i]; } } } // ------------------ // smoothen out turns private static void SmoothenOutTurns(int subdivisions) { if (subdivisions < 2) { throw new InvalidOperationException(); } // subdivide track int length = TrackManager.CurrentTrack.Elements.Length; int newLength = (length - 1) * subdivisions + 1; double[] midpointsTrackPositions = new double[newLength]; Vector3[] midpointsWorldPositions = new Vector3[newLength]; Vector3[] midpointsWorldDirections = new Vector3[newLength]; Vector3[] midpointsWorldUps = new Vector3[newLength]; Vector3[] midpointsWorldSides = new Vector3[newLength]; double[] midpointsCant = new double[newLength]; for (int i = 0; i < newLength; i++) { int m = i % subdivisions; if (m != 0) { int q = i / subdivisions; TrackManager.TrackFollower follower = new TrackManager.TrackFollower(); double r = (double)m / (double)subdivisions; double p = (1.0 - r) * TrackManager.CurrentTrack.Elements[q].StartingTrackPosition + r * TrackManager.CurrentTrack.Elements[q + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, -1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); midpointsTrackPositions[i] = p; midpointsWorldPositions[i] = follower.WorldPosition; midpointsWorldDirections[i] = follower.WorldDirection; midpointsWorldUps[i] = follower.WorldUp; midpointsWorldSides[i] = follower.WorldSide; midpointsCant[i] = follower.CurveCant; } } Array.Resize(ref TrackManager.CurrentTrack.Elements, newLength); for (int i = length - 1; i >= 1; i--) { TrackManager.CurrentTrack.Elements[subdivisions * i] = TrackManager.CurrentTrack.Elements[i]; } for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { int m = i % subdivisions; if (m != 0) { int q = i / subdivisions; int j = q * subdivisions; TrackManager.CurrentTrack.Elements[i] = TrackManager.CurrentTrack.Elements[j]; TrackManager.CurrentTrack.Elements[i].Events = new TrackManager.GeneralEvent[] { }; TrackManager.CurrentTrack.Elements[i].StartingTrackPosition = midpointsTrackPositions[i]; TrackManager.CurrentTrack.Elements[i].WorldPosition = midpointsWorldPositions[i]; TrackManager.CurrentTrack.Elements[i].WorldDirection = midpointsWorldDirections[i]; TrackManager.CurrentTrack.Elements[i].WorldUp = midpointsWorldUps[i]; TrackManager.CurrentTrack.Elements[i].WorldSide = midpointsWorldSides[i]; TrackManager.CurrentTrack.Elements[i].CurveCant = midpointsCant[i]; TrackManager.CurrentTrack.Elements[i].CurveCantTangent = 0.0; } } // find turns bool[] isTurn = new bool[TrackManager.CurrentTrack.Elements.Length]; { TrackManager.TrackFollower follower = new TrackManager.TrackFollower(); for (int i = 1; i < TrackManager.CurrentTrack.Elements.Length - 1; i++) { int m = i % subdivisions; if (m == 0) { double p = 0.00000001 * TrackManager.CurrentTrack.Elements[i - 1].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p, true, false); Vector3 d1 = TrackManager.CurrentTrack.Elements[i].WorldDirection; Vector3 d2 = follower.WorldDirection; Vector3 d = d1 - d2; double t = d.X * d.X + d.Z * d.Z; const double e = 0.0001; if (t > e) { isTurn[i] = true; } } } } // replace turns by curves double totalShortage = 0.0; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { if (isTurn[i]) { // estimate radius Vector3 AP = TrackManager.CurrentTrack.Elements[i - 1].WorldPosition; Vector3 AS = TrackManager.CurrentTrack.Elements[i - 1].WorldSide; Vector3 BP = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition; Vector3 BS = TrackManager.CurrentTrack.Elements[i + 1].WorldSide; Vector3 S = AS - BS; double rx; if (S.X * S.X > 0.000001) { rx = (BP.X - AP.X) / S.X; } else { rx = 0.0; } double rz; if (S.Z * S.Z > 0.000001) { rz = (BP.Z - AP.Z) / S.Z; } else { rz = 0.0; } if (rx != 0.0 | rz != 0.0) { double r; if (rx != 0.0 & rz != 0.0) { if (Math.Sign(rx) == Math.Sign(rz)) { double f = rx / rz; if (f > -1.1 & f < -0.9 | f > 0.9 & f < 1.1) { r = Math.Sqrt(Math.Abs(rx * rz)) * Math.Sign(rx); } else { r = 0.0; } } else { r = 0.0; } } else if (rx != 0.0) { r = rx; } else { r = rz; } if (r * r > 1.0) { // apply radius TrackManager.TrackFollower follower = new TrackManager.TrackFollower(); TrackManager.CurrentTrack.Elements[i - 1].CurveRadius = r; double p = 0.00000001 * TrackManager.CurrentTrack.Elements[i - 1].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); TrackManager.CurrentTrack.Elements[i].CurveRadius = r; //TrackManager.CurrentTrack.Elements[i].CurveCant = TrackManager.CurrentTrack.Elements[i].CurveCant; //TrackManager.CurrentTrack.Elements[i].CurveCantInterpolation = TrackManager.CurrentTrack.Elements[i].CurveCantInterpolation; TrackManager.CurrentTrack.Elements[i].WorldPosition = follower.WorldPosition; TrackManager.CurrentTrack.Elements[i].WorldDirection = follower.WorldDirection; TrackManager.CurrentTrack.Elements[i].WorldUp = follower.WorldUp; TrackManager.CurrentTrack.Elements[i].WorldSide = follower.WorldSide; // iterate to shorten track element length p = 0.00000001 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); Vector3 d = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; double bestT = d.X * d.X + d.Y * d.Y + d.Z * d.Z; int bestJ = 0; int n = 1000; double a = 1.0 / (double)n * (TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition - TrackManager.CurrentTrack.Elements[i].StartingTrackPosition); for (int j = 1; j < n - 1; j++) { TrackManager.UpdateTrackFollower(ref follower, TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition - (double)j * a, true, false); d = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; double t = d.X * d.X + d.Y * d.Y + d.Z * d.Z; if (t < bestT) { bestT = t; bestJ = j; } else { break; } } double s = (double)bestJ * a; for (int j = i + 1; j < TrackManager.CurrentTrack.Elements.Length; j++) { TrackManager.CurrentTrack.Elements[j].StartingTrackPosition -= s; } totalShortage += s; // introduce turn to compensate for curve p = 0.00000001 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); Vector3 AB = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; Vector3 AC = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - TrackManager.CurrentTrack.Elements[i].WorldPosition; Vector3 BC = follower.WorldPosition - TrackManager.CurrentTrack.Elements[i].WorldPosition; double sa = Math.Sqrt(BC.X * BC.X + BC.Z * BC.Z); double sb = Math.Sqrt(AC.X * AC.X + AC.Z * AC.Z); double sc = Math.Sqrt(AB.X * AB.X + AB.Z * AB.Z); double denominator = 2.0 * sa * sb; if (denominator != 0.0) { double originalAngle; { double value = (sa * sa + sb * sb - sc * sc) / denominator; if (value < -1.0) { originalAngle = Math.PI; } else if (value > 1.0) { originalAngle = 0; } else { originalAngle = Math.Acos(value); } } TrackManager.TrackElement originalTrackElement = TrackManager.CurrentTrack.Elements[i]; bestT = double.MaxValue; bestJ = 0; for (int j = -1; j <= 1; j++) { double g = (double)j * originalAngle; double cosg = Math.Cos(g); double sing = Math.Sin(g); TrackManager.CurrentTrack.Elements[i] = originalTrackElement; World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldDirection.X, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Y, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Z, 0.0, 1.0, 0.0, cosg, sing); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldUp.X, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Y, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Z, 0.0, 1.0, 0.0, cosg, sing); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldSide.X, ref TrackManager.CurrentTrack.Elements[i].WorldSide.Y, ref TrackManager.CurrentTrack.Elements[i].WorldSide.Z, 0.0, 1.0, 0.0, cosg, sing); p = 0.00000001 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); d = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; double t = d.X * d.X + d.Y * d.Y + d.Z * d.Z; if (t < bestT) { bestT = t; bestJ = j; } } { double newAngle = (double)bestJ * originalAngle; double cosg = Math.Cos(newAngle); double sing = Math.Sin(newAngle); TrackManager.CurrentTrack.Elements[i] = originalTrackElement; World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldDirection.X, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Y, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Z, 0.0, 1.0, 0.0, cosg, sing); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldUp.X, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Y, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Z, 0.0, 1.0, 0.0, cosg, sing); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldSide.X, ref TrackManager.CurrentTrack.Elements[i].WorldSide.Y, ref TrackManager.CurrentTrack.Elements[i].WorldSide.Z, 0.0, 1.0, 0.0, cosg, sing); } // iterate again to further shorten track element length p = 0.00000001 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); d = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; bestT = d.X * d.X + d.Y * d.Y + d.Z * d.Z; bestJ = 0; n = 1000; a = 1.0 / (double)n * (TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition - TrackManager.CurrentTrack.Elements[i].StartingTrackPosition); for (int j = 1; j < n - 1; j++) { TrackManager.UpdateTrackFollower(ref follower, TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition - (double)j * a, true, false); d = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - follower.WorldPosition; double t = d.X * d.X + d.Y * d.Y + d.Z * d.Z; if (t < bestT) { bestT = t; bestJ = j; } else { break; } } s = (double)bestJ * a; for (int j = i + 1; j < TrackManager.CurrentTrack.Elements.Length; j++) { TrackManager.CurrentTrack.Elements[j].StartingTrackPosition -= s; } totalShortage += s; } // compensate for height difference p = 0.00000001 * TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + 0.99999999 * TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; TrackManager.UpdateTrackFollower(ref follower, p - 1.0, true, false); TrackManager.UpdateTrackFollower(ref follower, p, true, false); Vector3 d1 = TrackManager.CurrentTrack.Elements[i + 1].WorldPosition - TrackManager.CurrentTrack.Elements[i].WorldPosition; double a1 = Math.Atan(d1.Y / Math.Sqrt(d1.X * d1.X + d1.Z * d1.Z)); Vector3 d2 = follower.WorldPosition - TrackManager.CurrentTrack.Elements[i].WorldPosition; double a2 = Math.Atan(d2.Y / Math.Sqrt(d2.X * d2.X + d2.Z * d2.Z)); double b = a2 - a1; if (b * b > 0.00000001) { double cosa = Math.Cos(b); double sina = Math.Sin(b); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldDirection.X, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Y, ref TrackManager.CurrentTrack.Elements[i].WorldDirection.Z, TrackManager.CurrentTrack.Elements[i].WorldSide.X, TrackManager.CurrentTrack.Elements[i].WorldSide.Y, TrackManager.CurrentTrack.Elements[i].WorldSide.Z, cosa, sina); World.Rotate(ref TrackManager.CurrentTrack.Elements[i].WorldUp.X, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Y, ref TrackManager.CurrentTrack.Elements[i].WorldUp.Z, TrackManager.CurrentTrack.Elements[i].WorldSide.X, TrackManager.CurrentTrack.Elements[i].WorldSide.Y, TrackManager.CurrentTrack.Elements[i].WorldSide.Z, cosa, sina); } } } } } // correct events for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length - 1; i++) { double startingTrackPosition = TrackManager.CurrentTrack.Elements[i].StartingTrackPosition; double endingTrackPosition = TrackManager.CurrentTrack.Elements[i + 1].StartingTrackPosition; for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { double p = startingTrackPosition + TrackManager.CurrentTrack.Elements[i].Events[j].TrackPositionDelta; if (p >= endingTrackPosition) { int len = TrackManager.CurrentTrack.Elements[i + 1].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[i + 1].Events, len + 1); TrackManager.CurrentTrack.Elements[i + 1].Events[len] = TrackManager.CurrentTrack.Elements[i].Events[j]; TrackManager.CurrentTrack.Elements[i + 1].Events[len].TrackPositionDelta += startingTrackPosition - endingTrackPosition; for (int k = j; k < TrackManager.CurrentTrack.Elements[i].Events.Length - 1; k++) { TrackManager.CurrentTrack.Elements[i].Events[k] = TrackManager.CurrentTrack.Elements[i].Events[k + 1]; } len = TrackManager.CurrentTrack.Elements[i].Events.Length; Array.Resize(ref TrackManager.CurrentTrack.Elements[i].Events, len - 1); j--; } } } } } }openbve-1.4.0.10/openBVE/OpenBve/OldParsers/ExtensionsCfgParser.cs000066400000000000000000000341661171674032100246020ustar00rootroot00000000000000using System; namespace OpenBve { internal static class ExtensionsCfgParser { // parse extensions config internal static void ParseExtensionsConfig(string TrainPath, System.Text.Encoding Encoding, out ObjectManager.UnifiedObject[] CarObjects, TrainManager.Train Train) { CarObjects = new ObjectManager.UnifiedObject[Train.Cars.Length]; bool[] CarObjectsReversed = new bool[Train.Cars.Length]; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "extensions.cfg"); if (System.IO.File.Exists(FileName)) { // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); 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(); } } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[exterior]": // exterior i++; 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(); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { if (Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } else { Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + a + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; break; default: if (Lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = Lines[i].Substring(4, Lines[i].Length - 5); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { bool DefinedLength = false; bool DefinedAxles = false; i++; 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 "object": if (Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } else { Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "length": { double m; if (double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out m)) { if (m > 0.0) { Train.Cars[n].Length = m; Train.Cars[n].BeaconReceiverPosition = 0.5 * m; DefinedLength = true; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "axles": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Interface.AddMessage(Interface.MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (rear >= front) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Cars[n].RearAxlePosition = rear; Train.Cars[n].FrontAxlePosition = front; DefinedAxles = true; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "reversed": CarObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered 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); } } i++; } i--; if (DefinedLength & !DefinedAxles) { double AxleDistance = 0.4 * Train.Cars[n].Length; Train.Cars[n].RearAxlePosition = -AxleDistance; Train.Cars[n].FrontAxlePosition = AxleDistance; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else if (Lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // coupler string t = Lines[i].Substring(8, Lines[i].Length - 9); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Couplers.Length) { i++; 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 "distances": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double min, max; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out min)) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out max)) { Interface.AddMessage(Interface.MessageType.Error, false, "Maximum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (min > max) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum is expected to be less than Maximum in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Couplers[n].MinimumDistanceBetweenCars = min; Train.Couplers[n].MaximumDistanceBetweenCars = max; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered 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); } } i++; } i--; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The coupler index " + t + " does not reference an existing coupler at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The coupler index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { // default Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; } } } // check for car objects and reverse if necessary int carObjects = 0; for (int i = 0; i < Train.Cars.Length; i++) { if (CarObjects[i] != null) { carObjects++; if (CarObjectsReversed[i]) { { // reverse axle positions double temp = Train.Cars[i].FrontAxlePosition; Train.Cars[i].FrontAxlePosition = -Train.Cars[i].RearAxlePosition; Train.Cars[i].RearAxlePosition = -temp; } if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)CarObjects[i]; CsvB3dObjectParser.ApplyScale(obj, -1.0, 1.0, -1.0); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { CsvB3dObjectParser.ApplyScale(obj.Objects[j].States[h].Object, -1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } if (carObjects > 0 & carObjects < Train.Cars.Length) { Interface.AddMessage(Interface.MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName); } } } } }openbve-1.4.0.10/openBVE/OpenBve/OldParsers/Panel2CfgParser.cs000066400000000000000000001671141171674032100235640ustar00rootroot00000000000000using System; using OpenBveApi.Colors; using OpenBveApi.Math; namespace OpenBve { internal static class Panel2CfgParser { // constants internal static double StackDistance = 0.000001; /// EyeDistance is required to be 1.0 by UpdateCarSectionElement and by UpdateCameraRestriction, thus cannot be easily changed. internal const double EyeDistance = 1.0; // parse panel config internal static void ParsePanel2Config(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { // read lines System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg"); string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { Lines[i] = Lines[i].Trim(); int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).TrimEnd(); } } // initialize double DriverX = Train.Cars[Train.DriverCar].DriverX; double DriverY = Train.Cars[Train.DriverCar].DriverY; double DriverZ = Train.Cars[Train.DriverCar].DriverZ; double PanelResolution = 1024.0; double PanelLeft = 0.0, PanelRight = 1024.0; double PanelTop = 0.0, PanelBottom = 1024.0; double PanelCenterX = 0.0, PanelCenterY = 512.0; double PanelOriginX = 0.0, PanelOriginY = 512.0; double PanelBitmapWidth = 1024.0, PanelBitmapHeight = 1024.0; string PanelDaytimeImage = null; string PanelNighttimeImage = null; Color24 PanelTransparentColor = new Color24(0, 0, 255); // parse lines for panel for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length > 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // panel case "this": i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "resolution": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelResolution)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "left": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelLeft)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line" + (i + 1).ToString(Culture) + " in " + FileName); } break; case "right": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelRight)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "top": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelTop)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "bottom": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelBottom)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { PanelDaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(PanelDaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + PanelDaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); PanelDaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { PanelNighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(PanelNighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + PanelNighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); PanelNighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out PanelTransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "center": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out PanelCenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out PanelCenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } case "origin": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out PanelOriginX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out PanelOriginY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } } i++; } i--; break; } } } } { // camera restriction double WorldWidth, WorldHeight; if (Screen.Width >= Screen.Height) { WorldWidth = 2.0 * Math.Tan(0.5 * World.HorizontalViewingAngle) * EyeDistance; WorldHeight = WorldWidth / World.AspectRatio; } else { WorldHeight = 2.0 * Math.Tan(0.5 * World.VerticalViewingAngle) * EyeDistance / World.AspectRatio; WorldWidth = WorldHeight * World.AspectRatio; } double x0 = (PanelLeft - PanelCenterX) / PanelResolution; double x1 = (PanelRight - PanelCenterX) / PanelResolution; double y0 = (PanelCenterY - PanelBottom) / PanelResolution * World.AspectRatio; double y1 = (PanelCenterY - PanelTop) / PanelResolution * World.AspectRatio; World.CameraRestrictionBottomLeft = new Vector3(x0 * WorldWidth, y0 * WorldHeight, EyeDistance); World.CameraRestrictionTopRight = new Vector3(x1 * WorldWidth, y1 * WorldHeight, EyeDistance); Train.Cars[Train.DriverCar].DriverYaw = Math.Atan((PanelCenterX - PanelOriginX) * WorldWidth / PanelResolution); Train.Cars[Train.DriverCar].DriverPitch = Math.Atan((PanelOriginY - PanelCenterY) * WorldWidth / PanelResolution); } // create panel if (PanelDaytimeImage != null) { if (!System.IO.File.Exists(PanelDaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "The daytime panel bitmap could not be found in " + FileName); PanelDaytimeImage = null; } else { Textures.Texture tday; Textures.RegisterTexture(PanelDaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(PanelTransparentColor.R, PanelTransparentColor.G, PanelTransparentColor.B)), out tday); Textures.Texture tnight = null; if (PanelNighttimeImage != null) { if (!System.IO.File.Exists(PanelNighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "The nighttime panel bitmap could not be found in " + FileName); PanelNighttimeImage = null; } else { Textures.RegisterTexture(PanelNighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(PanelTransparentColor.R, PanelTransparentColor.G, PanelTransparentColor.B)), out tnight); } } Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); PanelBitmapWidth = (double)tday.Width; PanelBitmapHeight = (double)tday.Height; CreateElement(Train, 0.0, 0.0, PanelBitmapWidth, PanelBitmapHeight, 0.5, 0.5, 0.0, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, new Color32(255, 255, 255, 255), false); } } // parse lines for rest double invfac = Lines.Length == 0 ? Loading.TrainProgressCurrentWeight : Loading.TrainProgressCurrentWeight / (double)Lines.Length; for (int i = 0; i < Lines.Length; i++) { Loading.TrainProgress = Loading.TrainProgressCurrentSum + invfac * (double)i; if ((i & 7) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } if (Lines[i].Length > 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // pilotlamp case "pilotlamp": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; Color24 TransparentColor = new Color24(0, 0, 255); int Layer = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null) { Textures.Texture tday; Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday); Textures.Texture tnight = null; if (NighttimeImage != null) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight); } Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); int w = tday.Width; int h = tday.Height; int j = CreateElement(Train, LocationX, LocationY, w, h, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, new Color32(255, 255, 255, 255), false); string f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f + " 1 == --"); } } break; // needle case "needle": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; Color32 Color = new Color32(255, 255, 255, 255); Color24 TransparentColor = new Color24(0, 0, 255); double OriginX = -1.0, OriginY = -1.0; bool OriginDefined = false; double Layer = 0.0, Radius = 0.0; double InitialAngle = -2.0943951023932, LastAngle = 2.0943951023932; double Minimum = 0.0, Maximum = 1000.0; double NaturalFrequency = -1.0, DampingRatio = -1.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterX is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterY is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } break; case "radius": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Radius == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is expected to be non-zero in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 16.0; } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "color": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out Color)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "origin": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out OriginX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out OriginY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); OriginX = -OriginX; } OriginDefined = true; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } break; case "initialangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out InitialAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "lastangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out LastAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "minimum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "maximum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "naturalfreq": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out NaturalFrequency)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (NaturalFrequency < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NaturalFrequency = -NaturalFrequency; } break; case "dampingratio": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out DampingRatio)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (DampingRatio < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DampingRatio = -DampingRatio; } break; case "layer": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null) { Textures.Texture tday; Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday); Textures.Texture tnight = null; if (NighttimeImage != null) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight); } Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)tday.Width; double h = (double)tday.Height; if (!OriginDefined) { OriginX = 0.5 * w; OriginY = 0.5 * h; } double ox = OriginX / w; double oy = OriginY / h; double n = Radius == 0.0 | OriginY == 0.0 ? 1.0 : Radius / OriginY; double nx = n * w; double ny = n * h; int j = CreateElement(Train, LocationX - ox * nx, LocationY - oy * ny, nx, ny, ox, oy, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, Color, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); string f; switch (Subject.ToLowerInvariant()) { case "hour": f = "0.000277777777777778 time * floor"; break; case "min": f = "0.0166666666666667 time * floor"; break; case "sec": f = "time floor"; break; default: f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); break; } InitialAngle *= 0.0174532925199433; LastAngle *= 0.0174532925199433; double a0 = (InitialAngle * Maximum - LastAngle * Minimum) / (Maximum - Minimum); double a1 = (LastAngle - InitialAngle) / (Maximum - Minimum); f += " " + a1.ToString(Culture) + " * " + a0.ToString(Culture) + " +"; if (NaturalFrequency >= 0.0 & DampingRatio >= 0.0) { Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(NaturalFrequency, DampingRatio); } Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f); } } break; // digitalnumber case "digitalnumber": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; Color24 TransparentColor = new Color24(0, 0, 255); double Layer = 0.0; int Interval = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "interval": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Interval)) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Interval <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is expected to be non-negative in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } if (Interval <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Interval is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null & Interval > 0) { int wday, hday; Program.CurrentHost.QueryTextureDimensions(DaytimeImage, out wday, out hday); if (wday > 0 & hday > 0) { int nday = hday / Interval; Textures.Texture[] tday = new Textures.Texture[nday]; Textures.Texture[] tnight; for (int k = 0; k < nday; k++) { Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(0, k * Interval, wday, Interval), new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday[k]); } if (NighttimeImage != null) { int wnight, hnight; Program.CurrentHost.QueryTextureDimensions(NighttimeImage, out wnight, out hnight); int nnight = hnight / Interval; if (nnight > nday) nnight = nday; tnight = new Textures.Texture[nday]; for (int k = 0; k < nnight; k++) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(0, k * Interval, wday, Interval), new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight[k]); } for (int k = nnight; k < nday; k++) { tnight[k] = null; } } else { tnight = new Textures.Texture[nday]; for (int k = 0; k < nday; k++) { tnight[k] = null; } } int j = -1; for (int k = 0; k < tday.Length; k++) { int l = CreateElement(Train, LocationX, LocationY, (double)wday, (double)Interval, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday[k], tnight[k], new Color32(255, 255, 255, 255), k != 0); if (k == 0) j = l; } string f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f); } } } break; // digitalgauge case "digitalgauge": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; Color32 Color = new Color32(0, 0, 0, 255); double Radius = 0.0; int Layer = 0; double InitialAngle = -2.0943951023932, LastAngle = 2.0943951023932; double Minimum = 0.0, Maximum = 1000.0; double Step = 0.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterX is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterY is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "radius": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Radius == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is expected to be non-zero in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 16.0; } break; case "color": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out Color)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "initialangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out InitialAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { InitialAngle *= 0.0174532925199433; } break; case "lastangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out LastAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { LastAngle *= 0.0174532925199433; } break; case "minimum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "maximum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "step": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Step)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (Radius == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Radius is required to be non-zero in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (Minimum == Maximum) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum and Maximum must not be equal in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 0.0; } if (Math.Abs(InitialAngle - LastAngle) > 6.28318531) { Interface.AddMessage(Interface.MessageType.Warning, false, "The absolute difference between InitialAngle and LastAngle exceeds 360 degrees in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (Radius != 0.0) { // create element int j = CreateElement(Train, LocationX - Radius, LocationY - Radius, 2.0 * Radius, 2.0 * Radius, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, null, null, Color, false); InitialAngle = InitialAngle + Math.PI; LastAngle = LastAngle + Math.PI; double x0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.X; double y0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Y; double z0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Z; double x1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.X; double y1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Y; double z1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Z; double x2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.X; double y2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Y; double z2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Z; double x3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.X; double y3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Y; double z3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Z; double cx = 0.25 * (x0 + x1 + x2 + x3); double cy = 0.25 * (y0 + y1 + y2 + y3); double cz = 0.25 * (z0 + z1 + z2 + z3); World.Vertex[] vertices = new World.Vertex[11]; int[][] faces = new int[][] { new int[] { 0, 1, 2 }, new int[] { 0, 3, 4 }, new int[] { 0, 5, 6 }, new int[] { 0, 7, 8 }, new int[] { 0, 9, 10 } }; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh = new World.Mesh(vertices, faces, Color); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDClockwiseWinding = InitialAngle <= LastAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDInitialAngle = InitialAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDLastAngle = LastAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDVectors = new Vector3[] { new Vector3(x0, y0, z0), new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), new Vector3(x3, y3, z3), new Vector3(cx, cy, cz) }; string f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); double a0 = (InitialAngle * Maximum - LastAngle * Minimum) / (Maximum - Minimum); double a1 = (LastAngle - InitialAngle) / (Maximum - Minimum); if (Step == 1.0) { f += " floor"; } else if (Step != 0.0) { string s = (1.0 / Step).ToString(Culture); string t = Step.ToString(Culture); f += " " + s + " * floor " + t + " *"; } f += " " + a1.ToString(Culture) + " " + a0.ToString(Culture) + " fma"; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f); } else { Interface.AddMessage(Interface.MessageType.Error, false, "Radius is required to be specified in " + Section + " in " + FileName); } } break; // timetable case "timetable": { double LocationX = 0.0, LocationY = 0.0; double Width = 0.0, Height = 0.0; Color24 TransparentColor = new Color24(0, 0, 255); double Layer = 0.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "width": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Width <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is required to be positive in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "height": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Height)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Height <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is required to be positive in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; // create element if (Width <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is required to be specified in " + Section + " in " + FileName); } if (Height <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is required to be specified in " + Section + " in " + FileName); } if (Width > 0.0 & Height > 0.0) { int j = CreateElement(Train, LocationX, LocationY, Width, Height, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, null, null, new Color32(255, 255, 255, 255), false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("timetable"); Timetable.AddObjectForCustomTimetable(Train.Cars[Train.DriverCar].CarSections[0].Elements[j]); } } break; } } } } } // get stack language from subject private static string GetStackLanguageFromSubject(TrainManager.Train Train, string Subject, string ErrorLocation) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string Suffix = ""; { // detect d# suffix int i; for (i = Subject.Length - 1; i >= 0; i--) { int a = char.ConvertToUtf32(Subject, i); if (a < 48 | a > 57) break; } if (i >= 0 & i < Subject.Length - 1) { if (Subject[i] == 'd' | Subject[i] == 'D') { int n; if (int.TryParse(Subject.Substring(i + 1), System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n == 0) { Suffix = " floor 10 mod"; } else { string t0 = Math.Pow(10.0, (double)n).ToString(Culture); string t1 = Math.Pow(10.0, (double)-n).ToString(Culture); Suffix = " ~ " + t0 + " >= <> " + t1 + " * floor 10 mod 10 ?"; } Subject = Subject.Substring(0, i); i--; } } } } // transform subject string Code; switch (Subject.ToLowerInvariant()) { case "acc": Code = "acceleration"; break; case "motor": Code = "accelerationmotor"; break; case "true": Code = "1"; break; case "kmph": Code = "speedometer abs 3.6 *"; break; case "mph": Code = "speedometer abs 2.2369362920544 *"; break; case "ms": Code = "speedometer abs"; break; case "bc": Code = "brakecylinder 0.001 *"; break; case "mr": Code = "mainreservoir 0.001 *"; break; case "sap": Code = "straightairpipe 0.001 *"; break; case "bp": Code = "brakepipe 0.001 *"; break; case "er": Code = "equalizingreservoir 0.001 *"; break; case "door": Code = "1 doors -"; break; case "csc": Code = "constSpeed"; break; case "power": Code = "brakeNotchLinear 0 powerNotch ?"; break; case "brake": Code = "brakeNotchLinear"; break; case "rev": Code = "reverserNotch ++"; break; case "hour": Code = "0.000277777777777778 time * 24 mod floor"; break; case "min": Code = "0.0166666666666667 time * 60 mod floor"; break; case "sec": Code = "time 60 mod floor"; break; case "atc": Code = "271 pluginstate"; break; default: { Code = "0"; bool unsupported = true; if (Subject.StartsWith("ats", StringComparison.OrdinalIgnoreCase)) { string a = Subject.Substring(3); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out n)) { if (n >= 0 & n <= 255) { Code = n.ToString(Culture) + " pluginstate"; unsupported = false; } } } else if (Subject.StartsWith("doorl", StringComparison.OrdinalIgnoreCase)) { string a = Subject.Substring(5); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out n)) { if (n >= 0 & n < Train.Cars.Length) { Code = n.ToString(Culture) + " leftdoorsindex ceiling"; unsupported = false; } else { Code = "2"; unsupported = false; } } } else if (Subject.StartsWith("doorr", StringComparison.OrdinalIgnoreCase)) { string a = Subject.Substring(5); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out n)) { if (n >= 0 & n < Train.Cars.Length) { Code = n.ToString(Culture) + " rightdoorsindex ceiling"; unsupported = false; } else { Code = "2"; unsupported = false; } } } if (unsupported) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid subject " + Subject + " encountered in " + ErrorLocation); } } break; } return Code + Suffix; } // create element private static int CreateElement(TrainManager.Train Train, double Left, double Top, double Width, double Height, double RelativeRotationCenterX, double RelativeRotationCenterY, double Distance, double PanelResolution, double PanelLeft, double PanelRight, double PanelTop, double PanelBottom, double PanelBitmapWidth, double PanelBitmapHeight, double PanelCenterX, double PanelCenterY, double PanelOriginX, double PanelOriginY, double DriverX, double DriverY, double DriverZ, Textures.Texture DaytimeTexture, Textures.Texture NighttimeTexture, Color32 Color, bool AddStateToLastElement) { double WorldWidth, WorldHeight; if (Screen.Width >= Screen.Height) { WorldWidth = 2.0 * Math.Tan(0.5 * World.HorizontalViewingAngle) * EyeDistance; WorldHeight = WorldWidth / World.AspectRatio; } else { WorldHeight = 2.0 * Math.Tan(0.5 * World.VerticalViewingAngle) * EyeDistance / World.AspectRatio; WorldWidth = WorldHeight * World.AspectRatio; } double x0 = Left / PanelResolution; double x1 = (Left + Width) / PanelResolution; double y0 = (PanelBottom - Top) / PanelResolution * World.AspectRatio; double y1 = (PanelBottom - (Top + Height)) / PanelResolution * World.AspectRatio; double xd = 0.5 - PanelCenterX / PanelResolution; x0 += xd; x1 += xd; double yt = PanelBottom - PanelResolution / World.AspectRatio; double yd = (PanelCenterY - yt) / (PanelBottom - yt) - 0.5; y0 += yd; y1 += yd; x0 = (x0 - 0.5) * WorldWidth; x1 = (x1 - 0.5) * WorldWidth; y0 = (y0 - 0.5) * WorldHeight; y1 = (y1 - 0.5) * WorldHeight; double xm = x0 * (1.0 - RelativeRotationCenterX) + x1 * RelativeRotationCenterX; double ym = y0 * (1.0 - RelativeRotationCenterY) + y1 * RelativeRotationCenterY; Vector3[] v = new Vector3[4]; v[0] = new Vector3(x0 - xm, y1 - ym, 0); v[1] = new Vector3(x0 - xm, y0 - ym, 0); v[2] = new Vector3(x1 - xm, y0 - ym, 0); v[3] = new Vector3(x1 - xm, y1 - ym, 0); World.Vertex t0 = new World.Vertex(v[0], new World.Vector2Df(0.0f, 1.0f)); World.Vertex t1 = new World.Vertex(v[1], new World.Vector2Df(0.0f, 0.0f)); World.Vertex t2 = new World.Vertex(v[2], new World.Vector2Df(1.0f, 0.0f)); World.Vertex t3 = new World.Vertex(v[3], new World.Vector2Df(1.0f, 1.0f)); ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); Object.Mesh.Vertices = new World.Vertex[] { t0, t1, t2, t3 }; Object.Mesh.Faces = new World.MeshFace[] { new World.MeshFace(new int[] { 0, 1, 2, 3 }) }; Object.Mesh.Materials = new World.MeshMaterial[1]; Object.Mesh.Materials[0].Flags = (byte)(DaytimeTexture != null ? World.MeshMaterial.TransparentColorMask : 0); Object.Mesh.Materials[0].Color = Color; Object.Mesh.Materials[0].TransparentColor = new Color24(0, 0, 255); Object.Mesh.Materials[0].DaytimeTexture = DaytimeTexture; Object.Mesh.Materials[0].NighttimeTexture = NighttimeTexture; Object.Dynamic = true; // calculate offset Vector3 o; o.X = xm + DriverX; o.Y = ym + DriverY; o.Z = EyeDistance - Distance + DriverZ; // add object if (AddStateToLastElement) { int n = Train.Cars[Train.DriverCar].CarSections[0].Elements.Length - 1; int j = Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States.Length; Array.Resize(ref Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States, j + 1); Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[j].Position = o; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[j].Object = Object; return n; } else { int n = Train.Cars[Train.DriverCar].CarSections[0].Elements.Length; Array.Resize(ref Train.Cars[Train.DriverCar].CarSections[0].Elements, n + 1); Train.Cars[Train.DriverCar].CarSections[0].Elements[n] = new ObjectManager.AnimatedObject(); Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States = new ObjectManager.AnimatedObjectState[1]; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[0].Position = o; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[0].Object = Object; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].CurrentState = 0; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].ObjectIndex = ObjectManager.CreateDynamicObject(); ObjectManager.Objects[Train.Cars[Train.DriverCar].CarSections[0].Elements[n].ObjectIndex] = ObjectManager.CloneObject(Object); return n; } } } }openbve-1.4.0.10/openBVE/OpenBve/OldParsers/PanelCfgParser.cs000066400000000000000000002267221171674032100235030ustar00rootroot00000000000000using System; using OpenBveApi.Colors; using OpenBveApi.Math; namespace OpenBve { internal static class PanelCfgParser { // constants internal static double StackDistance = 0.000001; /// EyeDistance is required to be 1.0 by UpdateCarSectionElement and by UpdateCameraRestriction, thus cannot be easily changed internal const double EyeDistance = 1.0; // parse panel config internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { // read lines System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg"); string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { Lines[i] = Lines[i].Trim(); int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).TrimEnd(); } } // initialize double FullWidth = 480, FullHeight = 440, SemiHeight = 240; double AspectRatio = FullWidth / FullHeight; double WorldWidth, WorldHeight; if (Screen.Width >= Screen.Height) { WorldWidth = 2.0 * Math.Tan(0.5 * World.HorizontalViewingAngle) * EyeDistance; WorldHeight = WorldWidth / AspectRatio; } else { WorldHeight = 2.0 * Math.Tan(0.5 * World.VerticalViewingAngle) * EyeDistance; WorldWidth = WorldHeight * AspectRatio; } World.CameraRestrictionBottomLeft = new Vector3(-0.5 * WorldWidth, -0.5 * WorldHeight, EyeDistance); World.CameraRestrictionTopRight = new Vector3(0.5 * WorldWidth, 0.5 * WorldHeight, EyeDistance); double WorldLeft = Train.Cars[Train.DriverCar].DriverX - 0.5 * WorldWidth; double WorldTop = Train.Cars[Train.DriverCar].DriverY + 0.5 * WorldHeight; double WorldZ = Train.Cars[Train.DriverCar].DriverZ; const double UpDownAngleConstant = -0.191986217719376; double PanelYaw = 0.0; double PanelPitch = UpDownAngleConstant; string PanelBackground = OpenBveApi.Path.CombineFile(TrainPath, "panel.bmp"); // parse lines for panel and view for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length > 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // panel case "panel": i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "background": if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; PanelBackground = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(PanelBackground)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + PanelBackground + "could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } break; } } i++; } i--; break; // view case "view": i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "yaw": { double yaw = 0.0; if (Value.Length > 0 && !Interface.TryParseDoubleVb6(Value, out yaw)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); yaw = 0.0; } PanelYaw = Math.Atan(yaw); } break; case "pitch": { double pitch = 0.0; if (Value.Length > 0 && !Interface.TryParseDoubleVb6(Value, out pitch)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); pitch = 0.0; } PanelPitch = Math.Atan(pitch) + UpDownAngleConstant; } break; } } i++; } i--; break; } } } } Train.Cars[Train.DriverCar].DriverYaw = PanelYaw; Train.Cars[Train.DriverCar].DriverPitch = PanelPitch; // panel { if (!System.IO.File.Exists(PanelBackground)) { Interface.AddMessage(Interface.MessageType.Error, true, "The panel image could not be found in " + FileName); } else { Textures.Texture t; Textures.RegisterTexture(PanelBackground, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; SemiHeight = FullHeight - h; CreateElement(Train, 0, SemiHeight, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } } // parse lines for rest double invfac = Lines.Length == 0 ? Loading.TrainProgressCurrentWeight : Loading.TrainProgressCurrentWeight / (double)Lines.Length; for (int i = 0; i < Lines.Length; i++) { Loading.TrainProgress = Loading.TrainProgressCurrentSum + invfac * (double)i; if ((i & 7) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } if (Lines[i].Length != 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // pressuregauge case "pressuregauge": case "pressuremeter": case "pressureindicator": case "圧力計": { int Type = 0; Color32[] NeedleColor = new Color32[] { new Color32(0, 0, 0, 255), new Color32(0, 0, 0, 255) }; int[] NeedleType = new int[] { 0, 0 }; double CenterX = 0.0, CenterY = 0.0, Radius = 16.0; string Background = null, Cover = null; double Angle = 0.785398163397449, Minimum = 0.0, Maximum = 1000.0; double UnitFactor = 1000.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "type": case "形態": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Type)) { Interface.AddMessage(Interface.MessageType.Error, false, "Type is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } else if (Type != 0 & Type != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Type must be either 0 or 1 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } break; case "lowerneedle": case "lowerhand": case "下針": case "upperneedle": case "upperhand": case "上針": int k = Key.ToLowerInvariant() == "lowerneedle" | Key.ToLowerInvariant() == "lowerhand" | Key == "下針" ? 0 : 1; if (Arguments.Length >= 1 && Arguments[0].Length > 0) { switch (Arguments[0].ToLowerInvariant()) { case "bc": case "ブレーキシリンダ": NeedleType[k] = 1; break; case "sap": case "直通管": NeedleType[k] = 2; break; case "bp": case "ブレーキ管": case "制動管": NeedleType[k] = 3; break; case "er": case "釣り合い空気溜め": case "釣り合い空気ダメ": case "つりあい空気溜め": case "ツリアイ空気ダメ": NeedleType[k] = 4; break; case "mr": case "元空気溜め": case "元空気ダメ": NeedleType[k] = 5; break; default: { int a; if (!Interface.TryParseIntVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Subject is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); a = 0; } NeedleType[k] = a; } break; } } int r = 0, g = 0, b = 0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = 0; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Key + " in " + Section + Key + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = 0; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseIntVb6(Arguments[3], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Key + " in " + Section + Key + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = 0; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = b < 0 ? 0 : 255; } NeedleColor[k] = new Color32((byte)r, (byte)g, (byte)b, 255); break; case "center": case "中心": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterY = 0.0; } break; case "radius": case "半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 1.0; } break; case "background": case "背景": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Background = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Background)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Background + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Background = null; } } break; case "cover": case "ふた": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Cover = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Cover)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Cover + "could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Cover = null; } } break; case "unit": case "単位": if (Arguments.Length >= 1 && Arguments[0].Length > 0) { string a = Arguments[0].ToLowerInvariant(); int Unit = 0; if (a == "kpa") { Unit = 0; } else if (a == "kgf/cm2" | a == "kgf/cm^2" | a == "kg/cm2" | a == "kg/cm^2") { Unit = 1; } else if (!Interface.TryParseIntVb6(Arguments[0], out Unit)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } else if (Unit != 0 & Unit != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Value must be either 0 or 1 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } if (Unit == 1) { UnitFactor = 98066.5; } else { UnitFactor = 1000.0; } } break; case "maximum": case "最大": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "PressureValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Maximum = 1000.0; } break; case "minimum": case "最小": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "PressureValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Minimum = 0.0; } break; case "angle": case "角度": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Angle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Angle = 0.785398163397449; } else { Angle *= 0.0174532925199433; } break; } } i++; } i--; // units Minimum *= UnitFactor; Maximum *= UnitFactor; // background if (Background != null) { Textures.Texture t; Textures.RegisterTexture(Background, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 3.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } // cover if (Cover != null) { Textures.Texture t; Textures.RegisterTexture(Cover, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 6.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } if (Type == 0) { // needles for (int k = 0; k < 2; k++) { if (NeedleType[k] != 0) { string Folder = Program.FileSystem.GetDataFolder("Compatibility"); string File = OpenBveApi.Path.CombineFile(Folder, k == 0 ? "needle_pressuregauge_lower.png" : "needle_pressuregauge_upper.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - (double)(4 + k) * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, NeedleColor[k], false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); double c0 = (Angle * (Maximum - Minimum) - 2.0 * Minimum * Math.PI) / (Maximum - Minimum) + Math.PI; double c1 = 2.0 * (Math.PI - Angle) / (Maximum - Minimum); string Variable = "0"; switch (NeedleType[k]) { case 1: Variable = "brakecylinder"; break; case 2: Variable = "straightairpipe"; break; case 3: Variable = "brakepipe"; break; case 4: Variable = "equalizingreservoir"; break; case 5: Variable = "mainreservoir"; break; } Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(Variable + " " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } } } else if (Type == 1) { // leds if (NeedleType[1] != 0) { int j = CreateElement(Train, CenterX - Radius, CenterY + SemiHeight - Radius, 2.0 * Radius, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, null, NeedleColor[1], false); double x0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.X; double y0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Y; double z0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Z; double x1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.X; double y1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Y; double z1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Z; double x2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.X; double y2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Y; double z2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Z; double x3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.X; double y3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Y; double z3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Z; double cx = 0.25 * (x0 + x1 + x2 + x3); double cy = 0.25 * (y0 + y1 + y2 + y3); double cz = 0.25 * (z0 + z1 + z2 + z3); World.Vertex[] vertices = new World.Vertex[11]; int[][] faces = new int[][] { new int[] { 0, 1, 2 }, new int[] { 0, 3, 4 }, new int[] { 0, 5, 6 }, new int[] { 0, 7, 8 }, new int[] { 0, 9, 10 } }; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh = new World.Mesh(vertices, faces, NeedleColor[1]); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDClockwiseWinding = true; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDInitialAngle = Angle - 2.0 * Math.PI; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDLastAngle = 2.0 * Math.PI - Angle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDVectors = new Vector3[] { new Vector3(x0, y0, z0), new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), new Vector3(x3, y3, z3), new Vector3(cx, cy, cz) }; double c0 = (Angle * (Maximum - Minimum) - 2.0 * Minimum * Math.PI) / (Maximum - Minimum); double c1 = 2.0 * (Math.PI - Angle) / (Maximum - Minimum); string Variable; switch (NeedleType[1]) { case 1: Variable = "brakecylinder"; break; case 2: Variable = "straightairpipe"; break; case 3: Variable = "brakepipe"; break; case 4: Variable = "equalizingreservoir"; break; case 5: Variable = "mainreservoir"; break; default: Variable = "0"; break; } Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(Variable + " " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } } } break; // speedometer case "speedometer": case "speedindicator": case "速度計": { int Type = 0; Color32 Needle = new Color32(255, 255, 255, 255); bool NeedleOverridden = false; double CenterX = 0.0, CenterY = 0.0, Radius = 16.0; string Background = null, Cover = null, Atc = null; double Angle = 1.0471975511966, Maximum = 33.3333333333333, AtcRadius = 0.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "type": case "形態": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Type)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } else if (Type != 0 & Type != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Value must be either 0 or 1 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } break; case "background": case "背景": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Background = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Background)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Background + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Background = null; } } break; case "needle": case "hand": case "針": { int r = 0, g = 0, b = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = 255; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = 255; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = 255; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = b < 0 ? 0 : 255; } Needle = new Color32((byte)r, (byte)g, (byte)b, 255); NeedleOverridden = true; } break; case "cover": case "ふた": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; Cover = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Cover)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + Cover + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Cover = null; } break; case "atc": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; Atc = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Atc)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + Atc + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Atc = null; } break; case "atcradius": case "atc半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out AtcRadius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); AtcRadius = 0.0; } break; case "center": case "中心": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterY = 0.0; } break; case "radius": case "半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 0.0; } break; case "angle": case "角度": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Angle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Angle = 1.0471975511966; } else { Angle *= 0.0174532925199433; } break; case "maximum": case "最大": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "SpeedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Maximum = 33.3333333333333; } else { Maximum *= 0.277777777777778; } break; } } i++; } i--; if (Background != null) { // background/led Textures.Texture t; Textures.RegisterTexture(Background, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 3.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } if (Cover != null) { // cover Textures.Texture t; Textures.RegisterTexture(Cover, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 6.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } if (Atc != null) { // atc int w, h; Program.CurrentHost.QueryTextureDimensions(Atc, out w, out h); if (w > 0 & h > 0) { int n = w / h; int k = -1; for (int j = 0; j < n; j++) { double s; switch (j) { case 1: s = 0.0; break; case 2: s = 15.0; break; case 3: s = 25.0; break; case 4: s = 45.0; break; case 5: s = 55.0; break; case 6: s = 65.0; break; case 7: s = 75.0; break; case 8: s = 90.0; break; case 9: s = 100.0; break; case 10: s = 110.0; break; case 11: s = 120.0; break; default: s = -1.0; break; } s *= 0.277777777777778; double a; if (s >= 0.0) { a = 2.0 * s * (Math.PI - Angle) / Maximum + Angle + Math.PI; } else { a = Math.PI; } double x = CenterX - 0.5 * h + Math.Sin(a) * AtcRadius; double y = CenterY - 0.5 * h - Math.Cos(a) * AtcRadius + SemiHeight; Textures.Texture t; Textures.RegisterTexture(Atc, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(j * h, 0, h, h), Color24.Blue), out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); if (j == 0) { k = CreateElement(Train, x, y, (double)h, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 4.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, x, y, (double)h, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 4.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("271 pluginstate"); } } if (Type == 0) { // needle string Folder = Program.FileSystem.GetDataFolder("Compatibility"); string File = OpenBveApi.Path.CombineFile(Folder, "needle_speedometer.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); double c0 = Angle + Math.PI; double c1 = 2.0 * (Math.PI - Angle) / Maximum; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } else if (Type == 1) { // led if (!NeedleOverridden) Needle = new Color32(0, 0, 0, 255); int j = CreateElement(Train, CenterX - Radius, CenterY + SemiHeight - Radius, 2.0 * Radius, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, null, Needle, false); double x0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.X; double y0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Y; double z0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Z; double x1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.X; double y1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Y; double z1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Z; double x2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.X; double y2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Y; double z2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Z; double x3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.X; double y3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Y; double z3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Z; double cx = 0.25 * (x0 + x1 + x2 + x3); double cy = 0.25 * (y0 + y1 + y2 + y3); double cz = 0.25 * (z0 + z1 + z2 + z3); World.Vertex[] vertices = new World.Vertex[11]; int[][] faces = new int[][] { new int[] { 0, 1, 2 }, new int[] { 0, 3, 4 }, new int[] { 0, 5, 6 }, new int[] { 0, 7, 8 }, new int[] { 0, 9, 10 } }; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh = new World.Mesh(vertices, faces, Needle); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDClockwiseWinding = true; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDInitialAngle = Angle - 2.0 * Math.PI; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDLastAngle = 2.0 * Math.PI - Angle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDVectors = new Vector3[] { new Vector3(x0, y0, z0), new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), new Vector3(x3, y3, z3), new Vector3(cx, cy, cz) }; double c0 = Angle; double c1 = 2.0 * (Math.PI - Angle) / Maximum; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } } break; // digitalindicator case "digitalindicator": { string Number = null; double CornerX = 0.0, CornerY = 0.0; int Width = 0, Height = 0; double UnitFactor = 3.6; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "number": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Number = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Number)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Number + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Number = null; } } break; case "corner": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CornerX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CornerY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerY = 0.0; } break; case "size": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Width = 0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out Height)) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Height = 0; } break; case "unit": if (Arguments.Length >= 1 && Arguments[0].Length > 0) { string a = Arguments[0].ToLowerInvariant(); int Unit = 0; if (a == "km/h") { Unit = 0; } else if (a == "mph") { Unit = 1; } else if (a == "m/s") { Unit = 2; } else if (!Interface.TryParseIntVb6(Arguments[0], out Unit)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } else if (Unit < 0 | Unit > 2) { Interface.AddMessage(Interface.MessageType.Error, false, "Value must be between 0 and 2 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } if (Unit == 1) { UnitFactor = 2.2369362920544; } else if (Unit == 2) { UnitFactor = 1.0; } else { UnitFactor = 3.6; } } break; } } i++; } i--; if (Number == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Number is required to be specified in " + Section + " in " + FileName); } if (Width <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is required to be specified in " + Section + " in " + FileName); } if (Height <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is required to be specified in " + Section + " in " + FileName); } if (Number != null & Width > 0 & Height > 0) { int w, h; Program.CurrentHost.QueryTextureDimensions(Number, out w, out h); if (w > 0 & h > 0) { int n = h / Height; Textures.Texture[] t = new Textures.Texture[n]; for (int j = 0; j < n; j++) { Textures.RegisterTexture(Number, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(w - Width, j * Height, Width, Height), Color24.Blue), out t[j]); //TextureManager.UseTexture(t[j], TextureManager.UseMode.Normal); } { // hundreds int k = -1; for (int j = 0; j < n; j++) { if (j == 0) { k = CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + UnitFactor.ToString(Culture) + " * ~ 100 >= <> 100 quotient 10 mod 10 ?"); } { // tens int k = -1; for (int j = 0; j < n; j++) { if (j == 0) { k = CreateElement(Train, CornerX + (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX + (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + UnitFactor.ToString(Culture) + " * ~ 10 >= <> 10 quotient 10 mod 10 ?"); } { // ones int k = -1; for (int j = 0; j < n; j++) { if (j == 0) { k = CreateElement(Train, CornerX + 2.0 * (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX + 2.0 * (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + UnitFactor.ToString(Culture) + " * floor 10 mod"); } } } } break; // pilotlamp case "pilotlamp": case "知らせ灯": { double CornerX = 0.0, CornerY = 0.0; string TurnOn = null, TurnOff = null; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "turnon": case "点灯": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { TurnOn = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(TurnOn)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + TurnOn + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); TurnOn = null; } } break; case "turnoff": case "消灯": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { TurnOff = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(TurnOff)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + TurnOff + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); TurnOff = null; } } break; case "corner": case "左上": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CornerX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CornerY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerY = 0.0; } break; } } i++; } i--; if (TurnOn != null & TurnOff != null) { Textures.Texture t0, t1; Textures.RegisterTexture(TurnOn, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t0); Textures.RegisterTexture(TurnOff, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t1); Textures.LoadTexture(t0, Textures.OpenGlTextureWrapMode.ClampClamp); Textures.LoadTexture(t1, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t0.Width; double h = (double)t0.Height; int j = CreateElement(Train, CornerX, CornerY + SemiHeight, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 2.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t0, new Color32(255, 255, 255, 255), false); w = (double)t1.Width; h = (double)t1.Height; CreateElement(Train, CornerX, CornerY + SemiHeight, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 2.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t1, new Color32(255, 255, 255, 255), true); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("doors 0 !="); } } break; // watch case "watch": case "時計": { Color32 Needle = new Color32(0, 0, 0, 255); double CenterX = 0.0, CenterY = 0.0, Radius = 16.0; string Background = null; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "background": case "背景": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Background = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Background)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + Background + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Background = null; } } break; case "needle": case "hand": case "針": { int r = 0, g = 0, b = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = 0; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = 0; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = 0; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = b < 0 ? 0 : 255; } Needle = new Color32((byte)r, (byte)g, (byte)b, 255); } break; case "center": case "中心": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterY = 0.0; } break; case "radius": case "半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 16.0; } break; } } i++; } i--; if (Background != null) { Textures.Texture t; Textures.RegisterTexture(Background, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 3.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } string Folder = Program.FileSystem.GetDataFolder("Compatibility"); { // hour string File = OpenBveApi.Path.CombineFile(Folder, "needle_hour.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 4.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("time 0.000277777777777778 * floor 0.523598775598298 *"); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(20.0, 0.4); } { // minute string File = OpenBveApi.Path.CombineFile(Folder, "needle_minute.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("time 0.0166666666666667 * floor 0.10471975511966 *"); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(20.0, 0.4); } { // second string File = OpenBveApi.Path.CombineFile(Folder, "needle_second.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 6.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("time floor 0.10471975511966 *"); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(20.0, 0.4); } } break; // brakeindicator case "brakeindicator": { double CornerX = 0.0, CornerY = 0.0; string Image = null; int Width = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "image": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Image = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Image)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Image + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Image = null; } } break; case "corner": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CornerX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CornerY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerY = 0.0; } break; case "width": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Width = 1; } else if (Width <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is expected to be positive in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Width = 1; } break; } } i++; } i--; if (Image == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Image is required to be specified in " + Section + " in " + FileName); } if (Width <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is required to be specified in " + Section + " in " + FileName); } if (Image != null & Width > 0) { int w, h; Program.CurrentHost.QueryTextureDimensions(Image, out w, out h); if (w > 0 & h > 0) { int n = w / Width; int k = -1; for (int j = 0; j < n; j++) { Textures.Texture t; OpenBveApi.Textures.TextureClipRegion clip = new OpenBveApi.Textures.TextureClipRegion(j * Width, 0, Width, h); Textures.RegisterTexture(Image, new OpenBveApi.Textures.TextureParameters(clip, Color24.Blue), out t); //TextureManager.UseTexture(t, TextureManager.UseMode.Normal); if (j == 0) { k = CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), true); } } if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { int maxpow = Train.Specs.MaximumPowerNotch; int em = maxpow + 3; Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("emergencyBrake " + em.ToString(Culture) + " brakeNotch 0 > " + maxpow.ToString(Culture) + " BrakeNotch + " + maxpow.ToString(Culture) + " powerNotch - ? ?"); } else { if (Train.Specs.HasHoldBrake) { int em = Train.Specs.MaximumPowerNotch + 2 + Train.Specs.MaximumBrakeNotch; int maxpow = Train.Specs.MaximumPowerNotch; int maxpowp1 = maxpow + 1; Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("emergencyBrake " + em.ToString(Culture) + " holdBrake " + maxpowp1.ToString(Culture) + " brakeNotch 0 > brakeNotch " + maxpowp1.ToString(Culture) + " + " + maxpow.ToString(Culture) + " powerNotch - ? ? ?"); } else { int em = Train.Specs.MaximumPowerNotch + 1 + Train.Specs.MaximumBrakeNotch; int maxpow = Train.Specs.MaximumPowerNotch; Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("emergencyBrake " + em.ToString(Culture) + " brakeNotch 0 > brakeNotch " + maxpow.ToString(Culture) + " + " + maxpow.ToString(Culture) + " powerNotch - ? ?"); } } } } } break; } } } } } // get arguments private static string[] GetArguments(string Expression) { string[] Arguments = new string[16]; int UsedArguments = 0; int Start = 0; for (int i = 0; i < Expression.Length; i++) { if (Expression[i] == ',' | Expression[i] == ':') { if (UsedArguments >= Arguments.Length) Array.Resize(ref Arguments, Arguments.Length << 1); Arguments[UsedArguments] = Expression.Substring(Start, i - Start).TrimStart(); UsedArguments++; Start = i + 1; } else if (Expression[i] == ';') { if (UsedArguments >= Arguments.Length) Array.Resize(ref Arguments, Arguments.Length << 1); Arguments[UsedArguments] = Expression.Substring(Start, i - Start).TrimStart(); UsedArguments++; Start = Expression.Length; break; } } if (Start < Expression.Length) { if (UsedArguments >= Arguments.Length) Array.Resize(ref Arguments, Arguments.Length << 1); Arguments[UsedArguments] = Expression.Substring(Start).Trim(); UsedArguments++; } Array.Resize(ref Arguments, UsedArguments); return Arguments; } // create element private static int CreateElement(TrainManager.Train Train, double Left, double Top, double Width, double Height, double FullWidth, double FullHeight, double WorldLeft, double WorldTop, double WorldWidth, double WorldHeight, double WorldZ, double DriverX, double DriverY, double DriverZ, Textures.Texture Texture, Color32 Color, bool AddStateToLastElement) { // create object ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); Vector3[] v = new Vector3[4]; double sx = 0.5 * WorldWidth * Width / FullWidth; double sy = 0.5 * WorldHeight * Height / FullHeight; v[0] = new Vector3(-sx, -sy, 0); v[1] = new Vector3(-sx, sy, 0); v[2] = new Vector3(sx, sy, 0); v[3] = new Vector3(sx, -sy, 0); World.Vertex t0 = new World.Vertex(v[0], new World.Vector2Df(0.0f, 1.0f)); World.Vertex t1 = new World.Vertex(v[1], new World.Vector2Df(0.0f, 0.0f)); World.Vertex t2 = new World.Vertex(v[2], new World.Vector2Df(1.0f, 0.0f)); World.Vertex t3 = new World.Vertex(v[3], new World.Vector2Df(1.0f, 1.0f)); Object.Mesh.Vertices = new World.Vertex[] { t0, t1, t2, t3 }; Object.Mesh.Faces = new World.MeshFace[] { new World.MeshFace(new int[] { 0, 1, 2, 3 }) }; Object.Mesh.Materials = new World.MeshMaterial[1]; Object.Mesh.Materials[0].Flags = Texture != null ? (byte)World.MeshMaterial.TransparentColorMask : (byte)0; Object.Mesh.Materials[0].Color = Color; Object.Mesh.Materials[0].TransparentColor = new Color24(0, 0, 255); Object.Mesh.Materials[0].DaytimeTexture = Texture; Object.Mesh.Materials[0].NighttimeTexture = null; Object.Dynamic = true; // calculate offset Vector3 o; o.X = WorldLeft + sx + WorldWidth * Left / FullWidth; o.Y = WorldTop - sy - WorldHeight * Top / FullHeight; o.Z = WorldZ; // add object if (AddStateToLastElement) { int n = Train.Cars[Train.DriverCar].CarSections[0].Elements.Length - 1; int j = Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States.Length; Array.Resize(ref Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States, j + 1); Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[j].Position = o; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[j].Object = Object; return n; } else { int n = Train.Cars[Train.DriverCar].CarSections[0].Elements.Length; Array.Resize(ref Train.Cars[Train.DriverCar].CarSections[0].Elements, n + 1); Train.Cars[Train.DriverCar].CarSections[0].Elements[n] = new ObjectManager.AnimatedObject(); Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States = new ObjectManager.AnimatedObjectState[1]; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[0].Position = o; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[0].Object = Object; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].CurrentState = 0; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].ObjectIndex = ObjectManager.CreateDynamicObject(); ObjectManager.Objects[Train.Cars[Train.DriverCar].CarSections[0].Elements[n].ObjectIndex] = ObjectManager.CloneObject(Object); return n; } } } }openbve-1.4.0.10/openBVE/OpenBve/OldParsers/SoundCfgParser.cs000066400000000000000000001204631171674032100235270ustar00rootroot00000000000000using System; using OpenBveApi.Math; namespace OpenBve { internal static class SoundCfgParser { // parse sound config internal static void ParseSoundConfig(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { string FileName = OpenBveApi.Path.CombineFile(TrainPath, "sound.cfg"); if (System.IO.File.Exists(FileName)) { LoadBve4Sound(FileName, TrainPath, Encoding, Train); } else { LoadBve2Sound(TrainPath, Train); } } internal static void LoadDefaultPluginSounds(TrainManager.Train train, string trainFolder) { Vector3 position = new Vector3(train.Cars[train.DriverCar].DriverX, train.Cars[train.DriverCar].DriverY, train.Cars[train.DriverCar].DriverZ + 1.0); const double radius = 2.0; train.Cars[train.DriverCar].Sounds.Plugin = new TrainManager.CarSound[] { TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "ats.wav"), position, radius), TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "atscnt.wav"), position, radius), TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "ding.wav"), position, radius), TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "toats.wav"), position, radius), TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "toatc.wav"), position, radius), TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "eb.wav"), position, radius) }; } // load no sound internal static void LoadNoSound(TrainManager.Train train) { // initialize for (int i = 0; i < train.Cars.Length; i++) { train.Cars[i].Sounds.Run = new TrainManager.CarSound[] { }; train.Cars[i].Sounds.Flange = new TrainManager.CarSound[] { }; train.Cars[i].Sounds.Adjust = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Air = TrainManager.CarSound.Empty; train.Cars[i].Sounds.AirHigh = TrainManager.CarSound.Empty; train.Cars[i].Sounds.AirZero = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Brake = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BrakeHandleApply = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BrakeHandleMin = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BrakeHandleMax = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BrakeHandleRelease = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BreakerResume = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BreakerResumeOrInterrupt = TrainManager.CarSound.Empty; train.Cars[i].Sounds.CpEnd = TrainManager.CarSound.Empty; train.Cars[i].Sounds.CpLoop = TrainManager.CarSound.Empty; train.Cars[i].Sounds.CpStart = TrainManager.CarSound.Empty; train.Cars[i].Sounds.DoorCloseL = TrainManager.CarSound.Empty; train.Cars[i].Sounds.DoorCloseR = TrainManager.CarSound.Empty; train.Cars[i].Sounds.DoorOpenL = TrainManager.CarSound.Empty; train.Cars[i].Sounds.DoorOpenR = TrainManager.CarSound.Empty; train.Cars[i].Sounds.EmrBrake = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Flange = new TrainManager.CarSound[] { }; train.Cars[i].Sounds.FlangeVolume = new double[] { }; train.Cars[i].Sounds.Halt = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Horns = new TrainManager.Horn[] { TrainManager.Horn.Empty, TrainManager.Horn.Empty, TrainManager.Horn.Empty }; train.Cars[i].Sounds.Loop = TrainManager.CarSound.Empty; train.Cars[i].Sounds.MasterControllerUp = TrainManager.CarSound.Empty; train.Cars[i].Sounds.MasterControllerDown = TrainManager.CarSound.Empty; train.Cars[i].Sounds.MasterControllerMin = TrainManager.CarSound.Empty; train.Cars[i].Sounds.MasterControllerMax = TrainManager.CarSound.Empty; train.Cars[i].Sounds.PilotLampOn = TrainManager.CarSound.Empty; train.Cars[i].Sounds.PilotLampOff = TrainManager.CarSound.Empty; train.Cars[i].Sounds.PointFrontAxle = TrainManager.CarSound.Empty; train.Cars[i].Sounds.PointRearAxle = TrainManager.CarSound.Empty; train.Cars[i].Sounds.ReverserOn = TrainManager.CarSound.Empty; train.Cars[i].Sounds.ReverserOff = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Rub = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Run = new TrainManager.CarSound[] { }; train.Cars[i].Sounds.RunVolume = new double[] { }; train.Cars[i].Sounds.SpringL = TrainManager.CarSound.Empty; train.Cars[i].Sounds.SpringR = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Plugin = new TrainManager.CarSound[] { }; } } // load bve 2 sound private static void LoadBve2Sound(string TrainPath, TrainManager.Train train) { // set sound positions and radii Vector3 front = new Vector3(0.0, 0.0, 0.5 * train.Cars[train.DriverCar].Length); Vector3 center = new Vector3(0.0, 0.0, 0.0); Vector3 left = new Vector3(-1.3, 0.0, 0.0); Vector3 right = new Vector3(1.3, 0.0, 0.0); Vector3 cab = new Vector3(-train.Cars[train.DriverCar].DriverX, train.Cars[train.DriverCar].DriverY, train.Cars[train.DriverCar].DriverZ - 0.5); Vector3 panel = new Vector3(train.Cars[train.DriverCar].DriverX, train.Cars[train.DriverCar].DriverY, train.Cars[train.DriverCar].DriverZ + 1.0); const double large = 30.0; const double medium = 10.0; const double small = 5.0; const double tiny = 2.0; LoadNoSound(train); // load sounds for driver's car train.Cars[train.DriverCar].Sounds.Adjust = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Adjust.wav"), panel, tiny); train.Cars[train.DriverCar].Sounds.Brake = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Brake.wav"), center, small); train.Cars[train.DriverCar].Sounds.Halt = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Halt.wav"), cab, tiny); train.Cars[train.DriverCar].Sounds.Horns[0].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Klaxon0.wav"), front, large); train.Cars[train.DriverCar].Sounds.Horns[0].Loop = false; if (train.Cars[train.DriverCar].Sounds.Horns[0].Sound.Buffer == null) { train.Cars[train.DriverCar].Sounds.Horns[0].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Klaxon.wav"), front, large); } train.Cars[train.DriverCar].Sounds.Horns[1].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Klaxon1.wav"), front, large); train.Cars[train.DriverCar].Sounds.Horns[1].Loop = false; train.Cars[train.DriverCar].Sounds.Horns[2].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Klaxon2.wav"), front, medium); train.Cars[train.DriverCar].Sounds.Horns[2].Loop = true; train.Cars[train.DriverCar].Sounds.PilotLampOn = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Leave.wav"), cab, tiny); train.Cars[train.DriverCar].Sounds.PilotLampOff = TrainManager.CarSound.Empty; // load sounds for all cars for (int i = 0; i < train.Cars.Length; i++) { Vector3 frontaxle = new Vector3(0.0, 0.0, train.Cars[i].FrontAxlePosition); Vector3 rearaxle = new Vector3(0.0, 0.0, train.Cars[i].RearAxlePosition); train.Cars[i].Sounds.Air = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Air.wav"), center, small); train.Cars[i].Sounds.AirHigh = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "AirHigh.wav"), center, small); train.Cars[i].Sounds.AirZero = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "AirZero.wav"), center, small); if (train.Cars[i].Specs.AirBrake.Type == TrainManager.AirBrakeType.Main) { train.Cars[i].Sounds.CpEnd = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "CpEnd.wav"), center, medium); train.Cars[i].Sounds.CpLoop = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "CpLoop.wav"), center, medium); train.Cars[i].Sounds.CpStart = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "CpStart.wav"), center, medium); } train.Cars[i].Sounds.BreakerResume = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BreakerResumeOrInterrupt = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BreakerResumed = false; train.Cars[i].Sounds.DoorCloseL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorClsL.wav"), left, small); train.Cars[i].Sounds.DoorCloseR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorClsR.wav"), right, small); if (train.Cars[i].Sounds.DoorCloseL.Buffer == null) { train.Cars[i].Sounds.DoorCloseL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorCls.wav"), left, small); } if (train.Cars[i].Sounds.DoorCloseR.Buffer == null) { train.Cars[i].Sounds.DoorCloseR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorCls.wav"), right, small); } train.Cars[i].Sounds.DoorOpenL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorOpnL.wav"), left, small); train.Cars[i].Sounds.DoorOpenR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorOpnR.wav"), right, small); if (train.Cars[i].Sounds.DoorOpenL.Buffer == null) { train.Cars[i].Sounds.DoorOpenL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorOpn.wav"), left, small); } if (train.Cars[i].Sounds.DoorOpenR.Buffer == null) { train.Cars[i].Sounds.DoorOpenR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorOpn.wav"), right, small); } train.Cars[i].Sounds.EmrBrake = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "EmrBrake.wav"), center, medium); train.Cars[i].Sounds.Flange = TryLoadSoundArray(TrainPath, "Flange", ".wav", center, medium); train.Cars[i].Sounds.FlangeVolume = new double[train.Cars[i].Sounds.Flange.Length]; train.Cars[i].Sounds.Loop = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Loop.wav"), center, medium); train.Cars[i].Sounds.PointFrontAxle = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Point.wav"), frontaxle, small); train.Cars[i].Sounds.PointRearAxle = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Point.wav"), rearaxle, small); train.Cars[i].Sounds.Rub = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Rub.wav"), center, medium); train.Cars[i].Sounds.Run = TryLoadSoundArray(TrainPath, "Run", ".wav", center, medium); train.Cars[i].Sounds.RunVolume = new double[train.Cars[i].Sounds.Run.Length]; train.Cars[i].Sounds.SpringL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "SpringL.wav"), left, small); train.Cars[i].Sounds.SpringR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "SpringR.wav"), right, small); // motor sound if (train.Cars[i].Specs.IsMotorCar) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; train.Cars[i].Sounds.Motor.Position = center; for (int j = 0; j < train.Cars[i].Sounds.Motor.Tables.Length; j++) { for (int k = 0; k < train.Cars[i].Sounds.Motor.Tables[j].Entries.Length; k++) { int idx = train.Cars[i].Sounds.Motor.Tables[j].Entries[k].SoundIndex; if (idx >= 0) { TrainManager.CarSound snd = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Motor" + idx.ToString(Culture) + ".wav"), center, medium); train.Cars[i].Sounds.Motor.Tables[j].Entries[k].Buffer = snd.Buffer; } } } } } } // load bve 4 sound private static void LoadBve4Sound(string FileName, string TrainPath, System.Text.Encoding Encoding, TrainManager.Train train) { // set sound positions and radii Vector3 center = new Vector3(0.0, 0.0, 0.0); Vector3 left = new Vector3(-1.3, 0.0, 0.0); Vector3 right = new Vector3(1.3, 0.0, 0.0); Vector3 front = new Vector3(0.0, 0.0, 0.5 * train.Cars[train.DriverCar].Length); Vector3 cab = new Vector3(-train.Cars[train.DriverCar].DriverX, train.Cars[train.DriverCar].DriverY, train.Cars[train.DriverCar].DriverZ - 0.5); Vector3 panel = new Vector3(train.Cars[train.DriverCar].DriverX, train.Cars[train.DriverCar].DriverY, train.Cars[train.DriverCar].DriverZ + 1.0); double large = 30.0; double medium = 10.0; double small = 5.0; double tiny = 2.0; LoadNoSound(train); // parse configuration file System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); 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.Length < 1 || string.Compare(Lines[0], "version 1.0", StringComparison.OrdinalIgnoreCase) != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid file format encountered in " + FileName + ". The first line is expected to be \"Version 1.0\"."); } string[] MotorFiles = new string[] { }; double invfac = Lines.Length == 0 ? Loading.TrainProgressCurrentWeight : Loading.TrainProgressCurrentWeight / (double)Lines.Length; for (int i = 0; i < Lines.Length; i++) { Loading.TrainProgress = Loading.TrainProgressCurrentSum + invfac * (double)i; if ((i & 7) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } switch (Lines[i].ToLowerInvariant()) { case "[run]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { for (int c = 0; c < train.Cars.Length; c++) { int n = train.Cars[c].Sounds.Run.Length; if (k >= n) { Array.Resize(ref train.Cars[c].Sounds.Run, k + 1); for (int h = n; h < k; h++) { train.Cars[c].Sounds.Run[h] = TrainManager.CarSound.Empty; } } train.Cars[c].Sounds.Run[k] = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Index must be greater or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } i++; } i--; break; case "[flange]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { for (int c = 0; c < train.Cars.Length; c++) { int n = train.Cars[c].Sounds.Flange.Length; if (k >= n) { Array.Resize(ref train.Cars[c].Sounds.Flange, k + 1); for (int h = n; h < k; h++) { train.Cars[c].Sounds.Flange[h] = TrainManager.CarSound.Empty; } } train.Cars[c].Sounds.Flange[k] = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Index must be greater or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } i++; } i--; break; case "[motor]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { if (k >= MotorFiles.Length) { Array.Resize(ref MotorFiles, k + 1); } MotorFiles[k] = OpenBveApi.Path.CombineFile(TrainPath, b); if (!System.IO.File.Exists(MotorFiles[k])) { Interface.AddMessage(Interface.MessageType.Error, true, "File " + MotorFiles[k] + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); MotorFiles[k] = null; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Index is invalid at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } i++; } i--; break; case "[switch]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (a == "0") { for (int c = 0; c < train.Cars.Length; c++) { Vector3 frontaxle = new Vector3(0.0, 0.0, train.Cars[c].FrontAxlePosition); Vector3 rearaxle = new Vector3(0.0, 0.0, train.Cars[c].RearAxlePosition); train.Cars[c].Sounds.PointFrontAxle = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), frontaxle, small); train.Cars[c].Sounds.PointRearAxle = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), rearaxle, small); } } else { Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported index " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; break; case "[brake]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "bc release high": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.AirHigh = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, small); } break; case "bc release": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.Air = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, small); } break; case "bc release full": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.AirZero = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, small); } break; case "emergency": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.EmrBrake = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); } break; case "bp decomp": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.Brake = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, small); } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[compressor]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].Specs.AirBrake.Type == TrainManager.AirBrakeType.Main) { switch (a.ToLowerInvariant()) { case "attack": train.Cars[c].Sounds.CpStart = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); break; case "loop": train.Cars[c].Sounds.CpLoop = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); break; case "release": train.Cars[c].Sounds.CpEnd = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } } } i++; } i--; break; case "[suspension]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "left": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.SpringL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), left, small); } break; case "right": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.SpringR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), right, small); } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[horn]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "primary": train.Cars[train.DriverCar].Sounds.Horns[0].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), front, large); train.Cars[train.DriverCar].Sounds.Horns[0].Loop = false; break; case "secondary": train.Cars[train.DriverCar].Sounds.Horns[1].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), front, large); train.Cars[train.DriverCar].Sounds.Horns[1].Loop = false; break; case "music": train.Cars[train.DriverCar].Sounds.Horns[2].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), front, medium); train.Cars[train.DriverCar].Sounds.Horns[2].Loop = true; break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[door]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "open left": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.DoorOpenL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), left, small); } break; case "open right": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.DoorOpenR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), left, small); } break; case "close left": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.DoorCloseL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), left, small); } break; case "close right": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.DoorCloseR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), left, small); } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[ats]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { int n = train.Cars[train.DriverCar].Sounds.Plugin.Length; if (k >= n) { Array.Resize(ref train.Cars[train.DriverCar].Sounds.Plugin, k + 1); for (int h = n; h < k; h++) { train.Cars[train.DriverCar].Sounds.Plugin[h] = TrainManager.CarSound.Empty; } } train.Cars[train.DriverCar].Sounds.Plugin[k] = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); } else { Interface.AddMessage(Interface.MessageType.Warning, false, "Index must be greater or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } } i++; } i--; break; case "[buzzer]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "correct": train.Cars[train.DriverCar].Sounds.Adjust = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[pilot lamp]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "on": train.Cars[train.DriverCar].Sounds.PilotLampOn = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "off": train.Cars[train.DriverCar].Sounds.PilotLampOff = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[brake handle]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "apply": train.Cars[train.DriverCar].Sounds.BrakeHandleApply = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "release": train.Cars[train.DriverCar].Sounds.BrakeHandleRelease = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "min": train.Cars[train.DriverCar].Sounds.BrakeHandleMin = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "max": train.Cars[train.DriverCar].Sounds.BrakeHandleMax = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[master controller]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "up": train.Cars[train.DriverCar].Sounds.MasterControllerUp = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "down": train.Cars[train.DriverCar].Sounds.MasterControllerDown = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "min": train.Cars[train.DriverCar].Sounds.MasterControllerMin = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "max": train.Cars[train.DriverCar].Sounds.MasterControllerMax = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[reverser]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "on": train.Cars[train.DriverCar].Sounds.ReverserOn = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "off": train.Cars[train.DriverCar].Sounds.ReverserOff = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[breaker]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "on": train.Cars[train.DriverCar].Sounds.BreakerResume = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, small); break; case "off": train.Cars[train.DriverCar].Sounds.BreakerResumeOrInterrupt = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, small); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[others]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "noise": for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].Specs.IsMotorCar | c == train.DriverCar) { train.Cars[c].Sounds.Loop = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); } } break; case "shoe": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.Rub = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; } } for (int i = 0; i < train.Cars.Length; i++) { train.Cars[i].Sounds.RunVolume = new double[train.Cars[i].Sounds.Run.Length]; train.Cars[i].Sounds.FlangeVolume = new double[train.Cars[i].Sounds.Flange.Length]; } // motor sound for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].Specs.IsMotorCar) { train.Cars[c].Sounds.Motor.Position = center; for (int i = 0; i < train.Cars[c].Sounds.Motor.Tables.Length; i++) { train.Cars[c].Sounds.Motor.Tables[i].Buffer = null; train.Cars[c].Sounds.Motor.Tables[i].Source = null; for (int j = 0; j < train.Cars[c].Sounds.Motor.Tables[i].Entries.Length; j++) { int index = train.Cars[c].Sounds.Motor.Tables[i].Entries[j].SoundIndex; if (index >= 0 && index < MotorFiles.Length && MotorFiles[index] != null) { train.Cars[c].Sounds.Motor.Tables[i].Entries[j].Buffer = Sounds.RegisterBuffer(MotorFiles[index], medium); } } } } } } // try load sound private static TrainManager.CarSound TryLoadSound(string FileName, Vector3 Position, double Radius) { TrainManager.CarSound s; s = TrainManager.CarSound.Empty; s.Position = Position; s.Source = null; if (FileName != null) { if (System.IO.File.Exists(FileName)) { s.Buffer = Sounds.RegisterBuffer(FileName, Radius); } } return s; } private static TrainManager.CarSound[] TryLoadSoundArray(string Folder, string FileStart, string FileEnd, Vector3 Position, double Radius) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; TrainManager.CarSound[] Sounds = new TrainManager.CarSound[] { }; string[] Files = System.IO.Directory.GetFiles(Folder); for (int i = 0; i < Files.Length; i++) { string a = System.IO.Path.GetFileName(Files[i]); if (a.Length > FileStart.Length + FileEnd.Length) { if (a.StartsWith(FileStart, StringComparison.OrdinalIgnoreCase) & a.EndsWith(FileEnd, StringComparison.OrdinalIgnoreCase)) { string b = a.Substring(FileStart.Length, a.Length - FileEnd.Length - FileStart.Length); int n; if (int.TryParse(b, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0) { int m = Sounds.Length; if (n >= m) { Array.Resize(ref Sounds, n + 1); for (int j = m; j < n; j++) { Sounds[j] = TrainManager.CarSound.Empty; Sounds[j].Source = null; } } Sounds[n] = TryLoadSound(Files[i], Position, Radius); } } } } } return Sounds; } } }openbve-1.4.0.10/openBVE/OpenBve/OldParsers/TrainDatParser.cs000066400000000000000000001231201171674032100235160ustar00rootroot00000000000000using System; namespace OpenBve { internal static class TrainDatParser { // parse train data internal static void ParseTrainData(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; // load file string FileName = OpenBveApi.Path.CombineFile(TrainPath, "train.dat"); string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); 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(); } } bool ver1220000 = false; for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length > 0) { string t = Lines[i].ToLowerInvariant(); if (t == "bve1220000") { ver1220000 = true; } else if (t != "bve2000000" & t != "openbve") { Interface.AddMessage(Interface.MessageType.Error, false, "The train.dat format " + Lines[0].ToLowerInvariant() + " is not supported in " + OpenBveApi.Path.CombineFile(TrainPath, "train.dat")); } break; } } // initialize Train.Cars = new TrainManager.Car[] { }; Train.Couplers = null; double BrakeCylinderServiceMaximumPressure = 440000.0; double BrakeCylinderEmergencyMaximumPressure = 440000.0; double MainReservoirMinimumPressure = 690000.0; double MainReservoirMaximumPressure = 780000.0; double BrakePipePressure = 0.0; TrainManager.CarBrakeType BrakeType = TrainManager.CarBrakeType.ElectromagneticStraightAirBrake; TrainManager.EletropneumaticBrakeType ElectropneumaticType = TrainManager.EletropneumaticBrakeType.None; double BrakeControlSpeed = 0.0; double BrakeDeceleration = 0.277777777777778; double JerkPowerUp = 10.0; double JerkPowerDown = 10.0; double JerkBrakeUp = 10.0; double JerkBrakeDown = 10.0; double BrakeCylinderUp = 300000.0; double BrakeCylinderDown = 200000.0; double CoefficientOfStaticFriction = 0.35; double CoefficientOfRollingResistance = 0.0025; double AerodynamicDragCoefficient = 1.1; TrainManager.AccelerationCurve[] AccelerationCurves = new TrainManager.AccelerationCurve[] { }; double DriverX = 0.0, DriverY = 0.0, DriverZ = 0.0; int DriverCar = 0; double MotorCarMass = 1.0, TrailerCarMass = 1.0; int MotorCars = 0, TrailerCars = 0; double CarLength = 20.0; double CarWidth = 2.6; double CarHeight = 3.6; double CenterOfGravityHeight = 1.6; double CarExposedFrontalArea = 0.6 * CarWidth * CarHeight; double CarUnexposedFrontalArea = 0.2 * CarWidth * CarHeight; bool FrontCarIsMotorCar = true; int ReAdhesionDevice = 0; Train.Specs.PassAlarm = TrainManager.PassAlarmType.None; Train.Specs.DoorOpenMode = TrainManager.DoorMode.AutomaticManualOverride; Train.Specs.DoorCloseMode = TrainManager.DoorMode.AutomaticManualOverride; TrainManager.MotorSoundTable[] Tables = new TrainManager.MotorSoundTable[4]; for (int i = 0; i < 4; i++) { Tables[i].Entries = new TrainManager.MotorSoundTableEntry[16]; for (int j = 0; j < 16; j++) { Tables[i].Entries[j].SoundIndex = -1; Tables[i].Entries[j].Pitch = 1.0f; Tables[i].Entries[j].Gain = 1.0f; } } // parse configuration double invfac = Lines.Length == 0 ? Loading.TrainProgressCurrentWeight : Loading.TrainProgressCurrentWeight / (double)Lines.Length; for (int i = 0; i < Lines.Length; i++) { Loading.TrainProgress = Loading.TrainProgressCurrentSum + invfac * (double)i; if ((i & 7) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } int n = 0; switch (Lines[i].ToLowerInvariant()) { case "#acceleration": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { Array.Resize(ref AccelerationCurves, n + 1); string t = Lines[i] + ","; int m = 0; while (true) { int j = t.IndexOf(','); if (j == -1) break; string s = t.Substring(0, j).Trim(); t = t.Substring(j + 1); double a; if (Interface.TryParseDoubleVb6(s, out a)) { switch (m) { case 0: if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "a0 in section #ACCELERATION is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { AccelerationCurves[n].StageZeroAcceleration = a * 0.277777777777778; } break; case 1: if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "a1 in section #ACCELERATION is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { AccelerationCurves[n].StageOneAcceleration = a * 0.277777777777778; } break; case 2: if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "v1 in section #ACCELERATION is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { AccelerationCurves[n].StageOneSpeed = a * 0.277777777777778; } break; case 3: if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "v2 in section #ACCELERATION is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { AccelerationCurves[n].StageTwoSpeed = a * 0.277777777777778; if (AccelerationCurves[n].StageTwoSpeed < AccelerationCurves[n].StageOneSpeed) { Interface.AddMessage(Interface.MessageType.Error, false, "v2 in section #ACCELERATION is expected to be greater than or equal to v1 at line " + (i + 1).ToString(Culture) + " in file " + FileName); AccelerationCurves[n].StageTwoSpeed = AccelerationCurves[n].StageOneSpeed; } } break; case 4: { if (ver1220000) { if (a <= 0.0) { AccelerationCurves[n].StageTwoExponent = 1.0; Interface.AddMessage(Interface.MessageType.Error, false, "e in section #ACCELERATION is expected to be positive at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { const double c = 4.439346232277577; AccelerationCurves[n].StageTwoExponent = 1.0 - Math.Log(a) * AccelerationCurves[n].StageTwoSpeed * c; if (AccelerationCurves[n].StageTwoExponent <= 0.0) { AccelerationCurves[n].StageTwoExponent = 1.0; } else if (AccelerationCurves[n].StageTwoExponent > 4.0) { AccelerationCurves[n].StageTwoExponent = 4.0; } } } else { AccelerationCurves[n].StageTwoExponent = a; if (AccelerationCurves[n].StageTwoExponent <= 0.0) { AccelerationCurves[n].StageTwoExponent = 1.0; } } } break; } } m++; } i++; n++; } i--; break; case "#performance": case "#deceleration": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (Interface.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "BrakeDeceleration is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { BrakeDeceleration = a * 0.277777777777778; } break; case 1: if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "CoefficientOfStaticFriction is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { CoefficientOfStaticFriction = a; } break; case 3: if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "CoefficientOfRollingResistance is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { CoefficientOfRollingResistance = a; } break; case 4: if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "AerodynamicDragCoefficient is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { AerodynamicDragCoefficient = a; } break; } } i++; n++; } i--; break; case "#delay": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (Interface.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: Train.Specs.DelayPowerUp = a; break; case 1: Train.Specs.DelayPowerDown = a; break; case 2: Train.Specs.DelayBrakeUp = a; break; case 3: Train.Specs.DelayBrakeDown = a; break; } } i++; n++; } i--; break; case "#move": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (Interface.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: JerkPowerUp = 0.01 * a; break; case 1: JerkPowerDown = 0.01 * a; break; case 2: JerkBrakeUp = 0.01 * a; break; case 3: JerkBrakeDown = 0.01 * a; break; case 4: BrakeCylinderUp = 1000.0 * a; break; case 5: BrakeCylinderDown = 1000.0 * a; break; } } i++; n++; } i--; break; case "#brake": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (Interface.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: { int b = (int)Math.Round(a); if (b >= 0 & b <= 2) { BrakeType = (TrainManager.CarBrakeType)b; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The setting for BrakeType is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName); BrakeType = TrainManager.CarBrakeType.ElectromagneticStraightAirBrake; } } break; case 1: { int b = (int)Math.Round(a); if (b >= 0 & b <= 2) { ElectropneumaticType = (TrainManager.EletropneumaticBrakeType)b; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The setting for ElectropneumaticType is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName); ElectropneumaticType = TrainManager.EletropneumaticBrakeType.None; } } break; case 2: BrakeControlSpeed = a * 0.277777777777778; break; } } i++; n++; } i--; break; case "#pressure": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (Interface.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "BrakeCylinderServiceMaximumPressure is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { BrakeCylinderServiceMaximumPressure = a * 1000.0; } break; case 1: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "BrakeCylinderEmergencyMaximumPressure is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { BrakeCylinderEmergencyMaximumPressure = a * 1000.0; } break; case 2: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "MainReservoirMinimumPressure is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { MainReservoirMinimumPressure = a * 1000.0; } break; case 3: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "MainReservoirMaximumPressure is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { MainReservoirMaximumPressure = a * 1000.0; } break; case 4: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "BrakePipePressure is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { BrakePipePressure = a * 1000.0; } break; } } i++; n++; } i--; break; case "#handle": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { int a; if (Interface.TryParseIntVb6(Lines[i], out a)) { switch (n) { case 0: Train.Specs.SingleHandle = a == 1; break; case 1: Train.Specs.MaximumPowerNotch = a; break; case 2: Train.Specs.MaximumBrakeNotch = a; break; case 3: Train.Specs.PowerNotchReduceSteps = a; break; } } i++; n++; } i--; break; case "#cockpit": case "#cab": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (Interface.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: DriverX = 0.001 * a; break; case 1: DriverY = 0.001 * a; break; case 2: DriverZ = 0.001 * a; break; case 3: DriverCar = (int)Math.Round(a); break; } } i++; n++; } i--; break; case "#car": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (Interface.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "MotorCarMass is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { MotorCarMass = a * 1000.0; } break; case 1: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "NumberOfMotorCars is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { MotorCars = (int)Math.Round(a); } break; case 2: TrailerCarMass = a * 1000.0; break; case 3: if (a < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "NumberOfTrailerCars is expected to be non-negative at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { TrailerCars = (int)Math.Round(a); } break; case 4: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "LengthOfACar is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { CarLength = a; } break; case 5: FrontCarIsMotorCar = a == 1.0; break; case 6: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "WidthOfACar is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { CarWidth = a; CarExposedFrontalArea = 0.65 * CarWidth * CarHeight; CarUnexposedFrontalArea = 0.2 * CarWidth * CarHeight; } break; case 7: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "HeightOfACar is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { CarHeight = a; CarExposedFrontalArea = 0.65 * CarWidth * CarHeight; CarUnexposedFrontalArea = 0.2 * CarWidth * CarHeight; } break; case 8: CenterOfGravityHeight = a; break; case 9: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ExposedFrontalArea is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { CarExposedFrontalArea = a; CarUnexposedFrontalArea = 0.2 * CarWidth * CarHeight; break; } break; case 10: if (a <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "UnexposedFrontalArea is expected to be positive at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { CarExposedFrontalArea = a; } break; } } i++; n++; } i--; break; case "#device": i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { double a; if (Interface.TryParseDoubleVb6(Lines[i], out a)) { switch (n) { case 0: if (a == 0.0) { Train.Specs.DefaultSafetySystems |= TrainManager.DefaultSafetySystems.AtsSn; } else if (a == 1.0) { Train.Specs.DefaultSafetySystems |= TrainManager.DefaultSafetySystems.AtsSn; Train.Specs.DefaultSafetySystems |= TrainManager.DefaultSafetySystems.AtsP; } break; case 1: if (a == 1.0 | a == 2.0) { Train.Specs.DefaultSafetySystems |= TrainManager.DefaultSafetySystems.Atc; } break; case 2: if (a == 1.0) { Train.Specs.DefaultSafetySystems |= TrainManager.DefaultSafetySystems.Eb; } break; case 3: Train.Specs.HasConstSpeed = a == 1.0; break; case 4: Train.Specs.HasHoldBrake = a == 1.0; break; case 5: ReAdhesionDevice = (int)Math.Round(a); break; case 7: { int b = (int)Math.Round(a); if (b >= 0 & b <= 2) { Train.Specs.PassAlarm = (TrainManager.PassAlarmType)b; } else { Interface.AddMessage(Interface.MessageType.Error, false, "PassAlarm is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } case 8: { int b = (int)Math.Round(a); if (b >= 0 & b <= 2) { Train.Specs.DoorOpenMode = (TrainManager.DoorMode)b; } else { Interface.AddMessage(Interface.MessageType.Error, false, "DoorOpenMode is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } case 9: { int b = (int)Math.Round(a); if (b >= 0 & b <= 2) { Train.Specs.DoorCloseMode = (TrainManager.DoorMode)b; } else { Interface.AddMessage(Interface.MessageType.Error, false, "DoorCloseMode is invalid at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } } i++; n++; } i--; break; case "#motor_p1": case "#motor_p2": case "#motor_b1": case "#motor_b2": { int msi = 0; switch (Lines[i].ToLowerInvariant()) { case "#motor_p1": msi = TrainManager.MotorSound.MotorP1; break; case "#motor_p2": msi = TrainManager.MotorSound.MotorP2; break; case "#motor_b1": msi = TrainManager.MotorSound.MotorB1; break; case "#motor_b2": msi = TrainManager.MotorSound.MotorB2; break; } i++; while (i < Lines.Length && !Lines[i].StartsWith("#", StringComparison.Ordinal)) { int u = Tables[msi].Entries.Length; if (n >= u) { Array.Resize(ref Tables[msi].Entries, 2 * u); for (int j = u; j < 2 * u; j++) { Tables[msi].Entries[j].SoundIndex = -1; Tables[msi].Entries[j].Pitch = 1.0f; Tables[msi].Entries[j].Gain = 1.0f; } } string t = Lines[i] + ","; int m = 0; while (true) { int j = t.IndexOf(','); if (j == -1) break; string s = t.Substring(0, j).Trim(); t = t.Substring(j + 1); double a; if (Interface.TryParseDoubleVb6(s, out a)) { switch (m) { case 0: Tables[msi].Entries[n].SoundIndex = (int)Math.Round(a); break; case 1: if (a < 0.0) a = 0.0; Tables[msi].Entries[n].Pitch = (float)(0.01 * a); break; case 2: if (a < 0.0) a = 0.0; Tables[msi].Entries[n].Gain = (float)Math.Pow((0.0078125 * a), 0.25); break; } } m++; } i++; n++; } Array.Resize(ref Tables[msi].Entries, n); i--; } break; } } if (TrailerCars > 0 & TrailerCarMass <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "TrailerCarMass is expected to be positive in " + FileName); TrailerCarMass = 1.0; } if (Train.Specs.MaximumPowerNotch <= 0) Train.Specs.MaximumPowerNotch = 8; if (Train.Specs.MaximumBrakeNotch <= 0) Train.Specs.MaximumBrakeNotch = 8; // apply data Train.Specs.TotalMass = (double)MotorCars * MotorCarMass + (double)TrailerCars * TrailerCarMass; if (MotorCars < 1) MotorCars = 1; if (TrailerCars < 0) TrailerCars = 0; int Cars = MotorCars + TrailerCars; Train.Cars = new TrainManager.Car[Cars]; double DistanceBetweenTheCars = 0.3; if (DriverCar < 0 | DriverCar >= Cars) { Interface.AddMessage(Interface.MessageType.Error, false, "DriverCar must point to an existing car in " + FileName); DriverCar = 0; } // brake system double OperatingPressure; if (BrakePipePressure <= 0.0) { if (BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { OperatingPressure = BrakeCylinderEmergencyMaximumPressure + 0.75 * (MainReservoirMinimumPressure - BrakeCylinderEmergencyMaximumPressure); if (OperatingPressure > MainReservoirMinimumPressure) { OperatingPressure = MainReservoirMinimumPressure; } } else { if (BrakeCylinderEmergencyMaximumPressure < 480000.0 & MainReservoirMinimumPressure > 500000.0) { OperatingPressure = 490000.0; } else { OperatingPressure = BrakeCylinderEmergencyMaximumPressure + 0.75 * (MainReservoirMinimumPressure - BrakeCylinderEmergencyMaximumPressure); } } } else { OperatingPressure = BrakePipePressure; } // acceleration curves double MaximumAcceleration = 0.0; for (int i = 0; i < Math.Min(AccelerationCurves.Length, Train.Specs.MaximumPowerNotch); i++) { bool errors = false; if (AccelerationCurves[i].StageZeroAcceleration <= 0.0) { AccelerationCurves[i].StageZeroAcceleration = 1.0; errors = true; } if (AccelerationCurves[i].StageOneAcceleration <= 0.0) { AccelerationCurves[i].StageOneAcceleration = 1.0; errors = true; } if (AccelerationCurves[i].StageOneSpeed <= 0.0) { AccelerationCurves[i].StageOneSpeed = 1.0; errors = true; } if (AccelerationCurves[i].StageTwoSpeed <= 0.0) { AccelerationCurves[i].StageTwoSpeed = 1.0; errors = true; } if (AccelerationCurves[i].StageTwoExponent <= 0.0) { AccelerationCurves[i].StageTwoExponent = 1.0; errors = true; } if (AccelerationCurves[i].StageOneSpeed > AccelerationCurves[i].StageTwoSpeed) { double x = 0.5 * (AccelerationCurves[i].StageOneSpeed + AccelerationCurves[i].StageTwoSpeed); AccelerationCurves[i].StageOneSpeed = x; AccelerationCurves[i].StageTwoSpeed = x; errors = true; } if (errors) { Interface.AddMessage(Interface.MessageType.Error, false, "Entry " + (i + 1).ToString(Culture) + " in the #ACCELERATION section is missing or invalid in " + FileName); } if (AccelerationCurves[i].StageZeroAcceleration > MaximumAcceleration) { MaximumAcceleration = AccelerationCurves[i].StageZeroAcceleration; } if (AccelerationCurves[i].StageOneAcceleration > MaximumAcceleration) { MaximumAcceleration = AccelerationCurves[i].StageOneAcceleration; } } double MotorDeceleration = Math.Sqrt(MaximumAcceleration * BrakeDeceleration); // apply brake-specific attributes for all cars for (int i = 0; i < Cars; i++) { Train.Cars[i].Specs.BrakeType = BrakeType; Train.Cars[i].Specs.ElectropneumaticType = ElectropneumaticType; Train.Cars[i].Specs.BrakeControlSpeed = BrakeControlSpeed; Train.Cars[i].Specs.BrakeDecelerationAtServiceMaximumPressure = BrakeDeceleration; Train.Cars[i].Specs.MotorDeceleration = MotorDeceleration; Train.Cars[i].Specs.AirBrake.AirCompressorEnabled = false; Train.Cars[i].Specs.AirBrake.AirCompressorMinimumPressure = MainReservoirMinimumPressure; Train.Cars[i].Specs.AirBrake.AirCompressorMaximumPressure = MainReservoirMaximumPressure; Train.Cars[i].Specs.AirBrake.AirCompressorRate = 5000.0; Train.Cars[i].Specs.AirBrake.MainReservoirCurrentPressure = Train.Cars[i].Specs.AirBrake.AirCompressorMinimumPressure + (Train.Cars[i].Specs.AirBrake.AirCompressorMaximumPressure - Train.Cars[i].Specs.AirBrake.AirCompressorMinimumPressure) * Program.RandomNumberGenerator.NextDouble(); Train.Cars[i].Specs.AirBrake.MainReservoirEqualizingReservoirCoefficient = 0.01; Train.Cars[i].Specs.AirBrake.MainReservoirBrakePipeCoefficient = (BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake ? 0.25 : 0.075) / Cars; Train.Cars[i].Specs.AirBrake.EqualizingReservoirCurrentPressure = 0.0; Train.Cars[i].Specs.AirBrake.EqualizingReservoirNormalPressure = 1.005 * OperatingPressure; Train.Cars[i].Specs.AirBrake.EqualizingReservoirServiceRate = 50000.0; Train.Cars[i].Specs.AirBrake.EqualizingReservoirEmergencyRate = 250000.0; Train.Cars[i].Specs.AirBrake.EqualizingReservoirChargeRate = 200000.0; Train.Cars[i].Specs.AirBrake.BrakePipeNormalPressure = OperatingPressure; Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure = BrakeType == TrainManager.CarBrakeType.ElectricCommandBrake ? 0.0 : Train.Cars[i].Specs.AirBrake.BrakePipeNormalPressure; Train.Cars[i].Specs.AirBrake.BrakePipeFlowSpeed = 100000000.0; Train.Cars[i].Specs.AirBrake.BrakePipeChargeRate = 10000000.0; Train.Cars[i].Specs.AirBrake.BrakePipeServiceRate = 1500000.0; Train.Cars[i].Specs.AirBrake.BrakePipeEmergencyRate = 5000000.0; Train.Cars[i].Specs.AirBrake.AuxillaryReservoirMaximumPressure = 0.975 * OperatingPressure; Train.Cars[i].Specs.AirBrake.AuxillaryReservoirCurrentPressure = Train.Cars[i].Specs.AirBrake.AuxillaryReservoirMaximumPressure; Train.Cars[i].Specs.AirBrake.AuxillaryReservoirChargeRate = 200000.0; Train.Cars[i].Specs.AirBrake.AuxillaryReservoirBrakePipeCoefficient = 0.5; { double r = Train.Cars[i].Specs.AirBrake.AuxillaryReservoirMaximumPressure / BrakeCylinderEmergencyMaximumPressure - 1.0; if (r < 0.1) r = 0.1; if (r > 1.0) r = 1.0; Train.Cars[i].Specs.AirBrake.AuxillaryReservoirBrakeCylinderCoefficient = r; } Train.Cars[i].Specs.AirBrake.BrakeCylinderCurrentPressure = BrakeCylinderEmergencyMaximumPressure; Train.Cars[i].Specs.AirBrake.BrakeCylinderServiceMaximumPressure = BrakeCylinderServiceMaximumPressure; Train.Cars[i].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure = BrakeCylinderEmergencyMaximumPressure; if (BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { Train.Cars[i].Specs.AirBrake.BrakeCylinderServiceChargeRate = BrakeCylinderUp; Train.Cars[i].Specs.AirBrake.BrakeCylinderEmergencyChargeRate = BrakeCylinderUp; Train.Cars[i].Specs.AirBrake.BrakeCylinderReleaseRate = BrakeCylinderDown; } else { Train.Cars[i].Specs.AirBrake.BrakeCylinderServiceChargeRate = 0.3 * BrakeCylinderUp; Train.Cars[i].Specs.AirBrake.BrakeCylinderEmergencyChargeRate = BrakeCylinderUp; Train.Cars[i].Specs.AirBrake.BrakeCylinderReleaseRate = BrakeCylinderDown; } Train.Cars[i].Specs.AirBrake.BrakeCylinderSoundPlayedForPressure = BrakeCylinderEmergencyMaximumPressure; Train.Cars[i].Specs.AirBrake.StraightAirPipeCurrentPressure = 0.0; Train.Cars[i].Specs.AirBrake.StraightAirPipeReleaseRate = 200000.0; Train.Cars[i].Specs.AirBrake.StraightAirPipeServiceRate = 300000.0; Train.Cars[i].Specs.AirBrake.StraightAirPipeEmergencyRate = 400000.0; } if (Train.Specs.HasHoldBrake & Train.Specs.MaximumBrakeNotch > 1) { Train.Specs.MaximumBrakeNotch--; } // apply train attributes Train.Specs.CurrentReverser.Driver = 0; Train.Specs.CurrentReverser.Actual = 0; Train.Specs.CurrentPowerNotch.Driver = 0; Train.Specs.CurrentPowerNotch.Safety = 0; Train.Specs.CurrentPowerNotch.Actual = 0; Train.Specs.CurrentPowerNotch.DelayedChanges = new TrainManager.HandleChange[] { }; Train.Specs.CurrentBrakeNotch.Driver = 0; Train.Specs.CurrentBrakeNotch.Safety = 0; Train.Specs.CurrentBrakeNotch.Actual = 0; Train.Specs.CurrentBrakeNotch.DelayedChanges = new TrainManager.HandleChange[] { }; Train.Specs.CurrentEmergencyBrake.ApplicationTime = double.MaxValue; if (BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { Train.Specs.SingleHandle = false; Train.Specs.HasHoldBrake = false; } // starting mode if (Game.TrainStart == Game.TrainStartMode.ServiceBrakesAts) { for (int i = 0; i < Cars; i++) { Train.Cars[i].Specs.AirBrake.AuxillaryReservoirCurrentPressure = Train.Cars[i].Specs.AirBrake.AuxillaryReservoirMaximumPressure; Train.Cars[i].Specs.AirBrake.BrakeCylinderCurrentPressure = Train.Cars[i].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure = Train.Cars[i].Specs.AirBrake.BrakePipeNormalPressure; Train.Cars[i].Specs.AirBrake.StraightAirPipeCurrentPressure = Train.Cars[i].Specs.AirBrake.BrakeCylinderServiceMaximumPressure; Train.Cars[i].Specs.AirBrake.EqualizingReservoirCurrentPressure = Train.Cars[i].Specs.AirBrake.EqualizingReservoirNormalPressure; } Train.Specs.AirBrake.Handle.Driver = TrainManager.AirBrakeHandleState.Service; Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service; Train.Specs.AirBrake.Handle.Actual = TrainManager.AirBrakeHandleState.Service; int notch = (int)Math.Round(0.7 * Train.Specs.MaximumBrakeNotch); Train.Specs.CurrentBrakeNotch.Driver = notch; Train.Specs.CurrentBrakeNotch.Safety = notch; Train.Specs.CurrentBrakeNotch.Actual = notch; Train.Specs.CurrentEmergencyBrake.Driver = false; Train.Specs.CurrentEmergencyBrake.Safety = false; Train.Specs.CurrentEmergencyBrake.Actual = false; Train.Specs.CurrentReverser.Driver = 1; Train.Specs.CurrentReverser.Actual = 1; } else if (Game.TrainStart == Game.TrainStartMode.EmergencyBrakesAts) { for (int i = 0; i < Cars; i++) { Train.Cars[i].Specs.AirBrake.AuxillaryReservoirCurrentPressure = Train.Cars[i].Specs.AirBrake.AuxillaryReservoirMaximumPressure; Train.Cars[i].Specs.AirBrake.BrakeCylinderCurrentPressure = Train.Cars[i].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure = 0.0; Train.Cars[i].Specs.AirBrake.StraightAirPipeCurrentPressure = 0.0; Train.Cars[i].Specs.AirBrake.EqualizingReservoirCurrentPressure = 0.0; } Train.Specs.AirBrake.Handle.Driver = TrainManager.AirBrakeHandleState.Service; Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service; Train.Specs.AirBrake.Handle.Actual = TrainManager.AirBrakeHandleState.Service; Train.Specs.CurrentBrakeNotch.Driver = Train.Specs.MaximumBrakeNotch; Train.Specs.CurrentBrakeNotch.Safety = Train.Specs.MaximumBrakeNotch; Train.Specs.CurrentBrakeNotch.Actual = Train.Specs.MaximumBrakeNotch; Train.Specs.CurrentEmergencyBrake.Driver = true; Train.Specs.CurrentEmergencyBrake.Safety = true; Train.Specs.CurrentEmergencyBrake.Actual = true; } else { for (int i = 0; i < Cars; i++) { Train.Cars[i].Specs.AirBrake.AuxillaryReservoirCurrentPressure = Train.Cars[i].Specs.AirBrake.AuxillaryReservoirMaximumPressure; Train.Cars[i].Specs.AirBrake.BrakeCylinderCurrentPressure = Train.Cars[i].Specs.AirBrake.BrakeCylinderEmergencyMaximumPressure; Train.Cars[i].Specs.AirBrake.BrakePipeCurrentPressure = 0.0; Train.Cars[i].Specs.AirBrake.StraightAirPipeCurrentPressure = 0.0; Train.Cars[i].Specs.AirBrake.EqualizingReservoirCurrentPressure = 0.0; } Train.Specs.AirBrake.Handle.Driver = TrainManager.AirBrakeHandleState.Service; Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service; Train.Specs.AirBrake.Handle.Actual = TrainManager.AirBrakeHandleState.Service; Train.Specs.CurrentBrakeNotch.Driver = Train.Specs.MaximumBrakeNotch; Train.Specs.CurrentBrakeNotch.Safety = Train.Specs.MaximumBrakeNotch; Train.Specs.CurrentBrakeNotch.Actual = Train.Specs.MaximumBrakeNotch; Train.Specs.CurrentEmergencyBrake.Driver = true; Train.Specs.CurrentEmergencyBrake.Safety = true; Train.Specs.CurrentEmergencyBrake.Actual = true; } // apply other attributes for all cars double AxleDistance = 0.4 * CarLength; for (int i = 0; i < Cars; i++) { Train.Cars[i].CarSections = new TrainManager.CarSection[] { }; Train.Cars[i].CurrentCarSection = -1; TrainManager.ChangeCarSection(Train, i, -1); Train.Cars[i].FrontAxle.Follower.TriggerType = i == 0 ? TrackManager.EventTriggerType.FrontCarFrontAxle : TrackManager.EventTriggerType.OtherCarFrontAxle; Train.Cars[i].RearAxle.Follower.TriggerType = i == Cars - 1 ? TrackManager.EventTriggerType.RearCarRearAxle : TrackManager.EventTriggerType.OtherCarRearAxle; Train.Cars[i].BeaconReceiver.TriggerType = i == 0 ? TrackManager.EventTriggerType.TrainFront : TrackManager.EventTriggerType.None; Train.Cars[i].BeaconReceiverPosition = 0.5 * CarLength; Train.Cars[i].FrontAxle.Follower.CarIndex = i; Train.Cars[i].RearAxle.Follower.CarIndex = i; Train.Cars[i].FrontAxlePosition = AxleDistance; Train.Cars[i].RearAxlePosition = -AxleDistance; Train.Cars[i].Specs.IsMotorCar = false; Train.Cars[i].Specs.JerkPowerUp = JerkPowerUp; Train.Cars[i].Specs.JerkPowerDown = JerkPowerDown; Train.Cars[i].Specs.JerkBrakeUp = JerkBrakeUp; Train.Cars[i].Specs.JerkBrakeDown = JerkBrakeDown; Train.Cars[i].Specs.CoefficientOfStaticFriction = CoefficientOfStaticFriction; Train.Cars[i].Specs.CoefficientOfRollingResistance = CoefficientOfRollingResistance; Train.Cars[i].Specs.AerodynamicDragCoefficient = AerodynamicDragCoefficient; Train.Cars[i].Specs.ExposedFrontalArea = CarExposedFrontalArea; Train.Cars[i].Specs.UnexposedFrontalArea = CarUnexposedFrontalArea; Train.Cars[i].Specs.Doors = new TrainManager.Door[2]; Train.Cars[i].Specs.Doors[0].Direction = -1; Train.Cars[i].Specs.Doors[0].State = 0.0; Train.Cars[i].Specs.Doors[1].Direction = 1; Train.Cars[i].Specs.Doors[1].State = 0.0; Train.Cars[i].Specs.DoorOpenFrequency = 0.0; Train.Cars[i].Specs.DoorCloseFrequency = 0.0; Train.Cars[i].Specs.CenterOfGravityHeight = CenterOfGravityHeight; Train.Cars[i].Specs.HoldBrake.UpdateInterval = 0.5; Train.Cars[i].Specs.ConstSpeed.UpdateInterval = 0.5; Train.Cars[i].Specs.ReAdhesionDevice.MaximumAccelerationOutput = double.PositiveInfinity; Train.Cars[i].Width = CarWidth; Train.Cars[i].Height = CarHeight; Train.Cars[i].Length = CarLength; Train.Cars[i].Specs.CriticalTopplingAngle = 0.5 * Math.PI - Math.Atan(2 * Train.Cars[i].Specs.CenterOfGravityHeight / Train.Cars[i].Width); } // assign motor cars if (MotorCars == 1) { if (FrontCarIsMotorCar | TrailerCars == 0) { Train.Cars[0].Specs.IsMotorCar = true; } else { Train.Cars[Cars - 1].Specs.IsMotorCar = true; } } else if (MotorCars == 2) { if (FrontCarIsMotorCar | TrailerCars == 0) { Train.Cars[0].Specs.IsMotorCar = true; Train.Cars[Cars - 1].Specs.IsMotorCar = true; } else if (TrailerCars == 1) { Train.Cars[1].Specs.IsMotorCar = true; Train.Cars[2].Specs.IsMotorCar = true; } else { int i = (int)Math.Ceiling(0.25 * (double)(Cars - 1)); int j = (int)Math.Floor(0.75 * (double)(Cars - 1)); Train.Cars[i].Specs.IsMotorCar = true; Train.Cars[j].Specs.IsMotorCar = true; } } else if (MotorCars > 0) { if (FrontCarIsMotorCar) { Train.Cars[0].Specs.IsMotorCar = true; double t = 1.0 + (double)TrailerCars / (double)(MotorCars - 1); double r = 0.0; double x = 0.0; while (true) { double y = x + t - r; x = Math.Ceiling(y); r = x - y; int i = (int)x; if (i >= Cars) break; Train.Cars[i].Specs.IsMotorCar = true; } } else { Train.Cars[1].Specs.IsMotorCar = true; double t = 1.0 + (double)(TrailerCars - 1) / (double)(MotorCars - 1); double r = 0.0; double x = 1.0; while (true) { double y = x + t - r; x = Math.Ceiling(y); r = x - y; int i = (int)x; if (i >= Cars) break; Train.Cars[i].Specs.IsMotorCar = true; } } } // assign motor/trailer-specific settings for (int i = 0; i < Cars; i++) { if (Train.Cars[i].Specs.IsMotorCar) { // motor car Train.Cars[i].Specs.AirBrake.Type = TrainManager.AirBrakeType.Main; Train.Cars[i].Specs.MassEmpty = MotorCarMass; Train.Cars[i].Specs.MassCurrent = MotorCarMass; Train.Cars[i].Specs.AccelerationCurves = AccelerationCurves; Train.Cars[i].Specs.AccelerationCurvesMultiplier = 1.0 + TrailerCars * TrailerCarMass / (MotorCars * MotorCarMass); Train.Cars[i].Specs.AccelerationCurveMaximum = MaximumAcceleration; switch (ReAdhesionDevice) { case 0: // type a: Train.Cars[i].Specs.ReAdhesionDevice.UpdateInterval = 1.0; Train.Cars[i].Specs.ReAdhesionDevice.ApplicationFactor = 0.0; Train.Cars[i].Specs.ReAdhesionDevice.ReleaseInterval = 1.0; Train.Cars[i].Specs.ReAdhesionDevice.ReleaseFactor = 8.0; break; case 1: // type b: Train.Cars[i].Specs.ReAdhesionDevice.UpdateInterval = 0.1; Train.Cars[i].Specs.ReAdhesionDevice.ApplicationFactor = 0.9935; Train.Cars[i].Specs.ReAdhesionDevice.ReleaseInterval = 4.0; Train.Cars[i].Specs.ReAdhesionDevice.ReleaseFactor = 1.125; break; case 2: // type c: Train.Cars[i].Specs.ReAdhesionDevice.UpdateInterval = 0.1; Train.Cars[i].Specs.ReAdhesionDevice.ApplicationFactor = 0.965; Train.Cars[i].Specs.ReAdhesionDevice.ReleaseInterval = 2.0; Train.Cars[i].Specs.ReAdhesionDevice.ReleaseFactor = 1.5; break; case 3: // type d: Train.Cars[i].Specs.ReAdhesionDevice.UpdateInterval = 0.05; Train.Cars[i].Specs.ReAdhesionDevice.ApplicationFactor = 0.935; Train.Cars[i].Specs.ReAdhesionDevice.ReleaseInterval = 0.3; Train.Cars[i].Specs.ReAdhesionDevice.ReleaseFactor = 2.0; break; default: // no readhesion device Train.Cars[i].Specs.ReAdhesionDevice.UpdateInterval = 1.0; Train.Cars[i].Specs.ReAdhesionDevice.ApplicationFactor = 1.0; Train.Cars[i].Specs.ReAdhesionDevice.ReleaseInterval = 1.0; Train.Cars[i].Specs.ReAdhesionDevice.ReleaseFactor = 99.0; break; } // motor sound Train.Cars[i].Sounds.Motor.SpeedConversionFactor = 18.0; Train.Cars[i].Sounds.Motor.Tables = new TrainManager.MotorSoundTable[4]; for (int j = 0; j < 4; j++) { Train.Cars[i].Sounds.Motor.Tables[j].Entries = new TrainManager.MotorSoundTableEntry[Tables[j].Entries.Length]; for (int k = 0; k < Tables[j].Entries.Length; k++) { Train.Cars[i].Sounds.Motor.Tables[j].Entries[k] = Tables[j].Entries[k]; } } } else { // trailer car Train.Cars[i].Specs.AirBrake.Type = Train == TrainManager.PlayerTrain & i == Train.DriverCar | BrakeType == TrainManager.CarBrakeType.ElectricCommandBrake ? TrainManager.AirBrakeType.Main : TrainManager.AirBrakeType.Auxillary; Train.Cars[i].Specs.MassEmpty = TrailerCarMass; Train.Cars[i].Specs.MassCurrent = TrailerCarMass; Train.Cars[i].Specs.AccelerationCurves = new TrainManager.AccelerationCurve[] { }; Train.Cars[i].Specs.AccelerationCurvesMultiplier = 0.0; Train.Cars[i].Specs.AccelerationCurveMaximum = 0.0; Train.Cars[i].Specs.ReAdhesionDevice.ApplicationFactor = 0.0; Train.Cars[i].Sounds.Motor.SpeedConversionFactor = 18.0; Train.Cars[i].Sounds.Motor.Tables = new TrainManager.MotorSoundTable[4]; for (int j = 0; j < 4; j++) { Train.Cars[i].Sounds.Motor.Tables[j].Entries = new TrainManager.MotorSoundTableEntry[] { }; } } } // driver Train.DriverCar = DriverCar; Train.Cars[Train.DriverCar].DriverX = DriverX; Train.Cars[Train.DriverCar].DriverY = DriverY; Train.Cars[Train.DriverCar].DriverZ = 0.5 * CarLength + DriverZ; // couplers Train.Couplers = new TrainManager.Coupler[Cars - 1]; for (int i = 0; i < Train.Couplers.Length; i++) { Train.Couplers[i].MinimumDistanceBetweenCars = 0.9 * DistanceBetweenTheCars; Train.Couplers[i].MaximumDistanceBetweenCars = 1.1 * DistanceBetweenTheCars; } // finish Train.Station = -1; Train.RouteLimits = new double[] { double.PositiveInfinity }; Train.CurrentRouteLimit = double.PositiveInfinity; Train.CurrentSectionLimit = double.PositiveInfinity; } } }openbve-1.4.0.10/openBVE/OpenBve/OldParsers/WaveParser.cs000066400000000000000000000564401171674032100227240ustar00rootroot00000000000000#pragma warning disable 0660 // Defines == or != but does not override Object.Equals #pragma warning disable 0661 // Defines == or != but does not override Object.GetHashCode using System; using System.IO; namespace OpenBve { internal static class WaveParser { // --- structures and enumerations --- /// Represents the format of wave data. internal struct WaveFormat { // members /// The number of samples per second per channel. internal int SampleRate; /// The number of bits per sample. internal int BitsPerSample; /// The number of channels. internal int Channels; // constructors /// Creates a new instance of this structure. /// The number of samples per second per channel. /// The number of bits per sample. /// The number of channels. internal WaveFormat(int sampleRate, int bitsPerSample, int channels) { this.SampleRate = sampleRate; this.BitsPerSample = bitsPerSample; this.Channels = channels; } // operators public static bool operator ==(WaveFormat a, WaveFormat b) { if (a.SampleRate != b.SampleRate) return false; if (a.BitsPerSample != b.BitsPerSample) return false; if (a.Channels != b.Channels) return false; return true; } public static bool operator !=(WaveFormat a, WaveFormat b) { if (a.SampleRate != b.SampleRate) return true; if (a.BitsPerSample != b.BitsPerSample) return true; if (a.Channels != b.Channels) return true; return false; } } /// Represents wave data. internal class WaveData { // members /// The format of the wave data. internal WaveFormat Format; /// The wave data in little endian byte order. If the bits per sample are not a multiple of 8, each sample is padded into a multiple-of-8 byte. For bytes per sample higher than 1, the values are stored as signed integers, otherwise as unsigned integers. internal byte[] Bytes; // constructors /// Creates a new instance of this class. /// The format of the wave data. /// The wave data in little endian byte order. If the bits per sample are not a multiple of 8, each sample is padded into a multiple-of-8 byte. For bytes per sample higher than 1, the values are stored as signed integers, otherwise as unsigned integers. internal WaveData(WaveFormat format, byte[] bytes) { this.Format = format; this.Bytes = bytes; } } /// Represents the endianness of an integer. private enum Endianness { /// Represents little endian byte order, i.e. least-significant byte first. Little = 0, /// Represents big endian byte order, i.e. most-significant byte first. Big = 1 } // --- format-specific data --- /// Represents format-specific data. private abstract class FormatData { internal int BlockSize; } /// Represents PCM-specific data. private class PcmData : FormatData { } /// Represents Microsoft-ADPCM-specific data. private class MicrosoftAdPcmData : FormatData { // structures internal struct ChannelData { internal int bPredictor; internal short iDelta; internal short iSamp1; internal short iSamp2; internal int iCoef1; internal int iCoef2; } // members internal int SamplesPerBlock; internal short[][] Coefficients = null; // read-only fields internal static readonly short[] AdaptionTable = new short[] { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 }; } // --- functions --- /// Reads wave data from a RIFF/WAVE/PCM file. /// The file name of the RIFF/WAVE/PCM file. /// The wave data. /// Both RIFF and RIFX container formats are supported by this function. internal static WaveData LoadFromFile(string fileName) { string fileTitle = Path.GetFileName(fileName); byte[] fileBytes = File.ReadAllBytes(fileName); using (MemoryStream stream = new MemoryStream(fileBytes)) { using (BinaryReader reader = new BinaryReader(stream)) { // RIFF/RIFX chunk Endianness endianness; uint headerCkID = reader.ReadUInt32(); /* Chunk ID is character-based */ if (headerCkID == 0x46464952) { endianness = Endianness.Little; } else if (headerCkID == 0x58464952) { endianness = Endianness.Big; } else { throw new InvalidDataException("Invalid chunk ID in " + fileTitle); } uint headerCkSize = ReadUInt32(reader, endianness); uint formType = ReadUInt32(reader, endianness); if (formType != 0x45564157) { throw new InvalidDataException("Unsupported format in " + fileTitle); } // data chunks WaveFormat format = new WaveFormat(); FormatData data = null; byte[] dataBytes = null; while (stream.Position + 8 <= stream.Length) { uint ckID = reader.ReadUInt32(); /* Chunk ID is character-based */ uint ckSize = ReadUInt32(reader, endianness); if (ckID == 0x20746d66) { // "fmt " chunk if (ckSize < 14) { throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle); } ushort wFormatTag = ReadUInt16(reader, endianness); ushort wChannels = ReadUInt16(reader, endianness); uint dwSamplesPerSec = ReadUInt32(reader, endianness); if (dwSamplesPerSec >= 0x80000000) { throw new InvalidDataException("Unsupported dwSamplesPerSec in " + fileTitle); } uint dwAvgBytesPerSec = ReadUInt32(reader, endianness); ushort wBlockAlign = ReadUInt16(reader, endianness); if (wFormatTag == 1) { // PCM if (ckSize < 16) { throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle); } ushort wBitsPerSample = ReadUInt16(reader, endianness); stream.Position += ckSize - 16; if (wBitsPerSample < 1) { throw new InvalidDataException("Unsupported wBitsPerSample in " + fileTitle); } if (wBlockAlign != ((wBitsPerSample + 7) / 8) * wChannels) { throw new InvalidDataException("Unexpected wBlockAlign in " + fileTitle); } format.SampleRate = (int)dwSamplesPerSec; format.BitsPerSample = (int)wBitsPerSample; format.Channels = (int)wChannels; PcmData pcmData = new PcmData(); pcmData.BlockSize = (int)wBlockAlign; data = pcmData; } else if (wFormatTag == 2) { // Microsoft ADPCM if (ckSize < 22) { throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle); } ushort wBitsPerSample = ReadUInt16(reader, endianness); if (wBitsPerSample != 4) { throw new InvalidDataException("Unsupported wBitsPerSample in " + fileTitle); } ushort cbSize = ReadUInt16(reader, endianness); MicrosoftAdPcmData adpcmData = new MicrosoftAdPcmData(); adpcmData.SamplesPerBlock = ReadUInt16(reader, endianness); if (adpcmData.SamplesPerBlock == 0 | adpcmData.SamplesPerBlock > 2 * ((int)wBlockAlign - 6)) { throw new InvalidDataException("Unexpected nSamplesPerBlock in " + fileTitle); } ushort wNumCoef = ReadUInt16(reader, endianness); if (ckSize < 22 + 4 * wNumCoef) { throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle); } adpcmData.Coefficients = new short[wNumCoef][]; for (int i = 0; i < wNumCoef; i++) { unchecked { adpcmData.Coefficients[i] = new short[] { (short)ReadUInt16(reader, endianness), (short)ReadUInt16(reader, endianness) }; } } stream.Position += ckSize - (22 + 4 * wNumCoef); format.SampleRate = (int)dwSamplesPerSec; format.BitsPerSample = 16; format.Channels = (int)wChannels; adpcmData.BlockSize = wBlockAlign; data = adpcmData; } else { // unsupported format throw new InvalidDataException("Unsupported wFormatTag in " + fileTitle); } } else if (ckID == 0x61746164) { // "data" chunk if (ckSize >= 0x80000000) { throw new InvalidDataException("Unsupported data chunk size in " + fileTitle); } if (data is PcmData) { // PCM int bytesPerSample = (format.BitsPerSample + 7) / 8; int samples = (int)ckSize / (format.Channels * bytesPerSample); int dataSize = samples * format.Channels * bytesPerSample; dataBytes = reader.ReadBytes(dataSize); stream.Position += ckSize - dataSize; } else if (data is MicrosoftAdPcmData) { // Microsoft ADPCM MicrosoftAdPcmData adpcmData = (MicrosoftAdPcmData)data; int blocks = (int)ckSize / adpcmData.BlockSize; dataBytes = new byte[2 * blocks * format.Channels * adpcmData.SamplesPerBlock]; int position = 0; for (int i = 0; i < blocks; i++) { unchecked { MicrosoftAdPcmData.ChannelData[] channelData = new MicrosoftAdPcmData.ChannelData[format.Channels]; for (int j = 0; j < format.Channels; j++) { channelData[j].bPredictor = (int)reader.ReadByte(); if (channelData[j].bPredictor >= adpcmData.Coefficients.Length) { throw new InvalidDataException("Invalid bPredictor in " + fileTitle); } else { channelData[j].iCoef1 = (int)adpcmData.Coefficients[channelData[j].bPredictor][0]; channelData[j].iCoef2 = (int)adpcmData.Coefficients[channelData[j].bPredictor][1]; } } for (int j = 0; j < format.Channels; j++) { channelData[j].iDelta = (short)ReadUInt16(reader, endianness); } for (int j = 0; j < format.Channels; j++) { channelData[j].iSamp1 = (short)ReadUInt16(reader, endianness); } for (int j = 0; j < format.Channels; j++) { channelData[j].iSamp2 = (short)ReadUInt16(reader, endianness); } for (int j = 0; j < format.Channels; j++) { dataBytes[position] = (byte)(ushort)channelData[j].iSamp2; dataBytes[position + 1] = (byte)((ushort)channelData[j].iSamp2 >> 8); position += 2; } for (int j = 0; j < format.Channels; j++) { dataBytes[position] = (byte)(ushort)channelData[j].iSamp1; dataBytes[position + 1] = (byte)((ushort)channelData[j].iSamp1 >> 8); position += 2; } uint nibbleByte = 0; bool nibbleFirst = true; for (int j = 0; j < adpcmData.SamplesPerBlock - 2; j++) { for (int k = 0; k < format.Channels; k++) { int lPredSample = (int)channelData[k].iSamp1 * channelData[k].iCoef1 + (int)channelData[k].iSamp2 * channelData[k].iCoef2 >> 8; int iErrorDeltaUnsigned; if (nibbleFirst) { nibbleByte = (uint)reader.ReadByte(); iErrorDeltaUnsigned = (int)(nibbleByte >> 4); nibbleFirst = false; } else { iErrorDeltaUnsigned = (int)(nibbleByte & 15); nibbleFirst = true; } int iErrorDeltaSigned = iErrorDeltaUnsigned >= 8 ? iErrorDeltaUnsigned - 16 : iErrorDeltaUnsigned; int lNewSampInt = lPredSample + (int)channelData[k].iDelta * iErrorDeltaSigned; short lNewSamp = lNewSampInt <= -32768 ? (short)-32768 : lNewSampInt >= 32767 ? (short)32767 : (short)lNewSampInt; channelData[k].iDelta = (short)( (int)channelData[k].iDelta * (int)MicrosoftAdPcmData.AdaptionTable[iErrorDeltaUnsigned] >> 8 ); if (channelData[k].iDelta < 16) { channelData[k].iDelta = 16; } channelData[k].iSamp2 = channelData[k].iSamp1; channelData[k].iSamp1 = lNewSamp; dataBytes[position] = (byte)(ushort)lNewSamp; dataBytes[position + 1] = (byte)((ushort)lNewSamp >> 8); position += 2; } } } stream.Position += adpcmData.BlockSize - (format.Channels * (adpcmData.SamplesPerBlock - 2) + 1 >> 1) - 7 * format.Channels; } stream.Position += (int)ckSize - blocks * adpcmData.BlockSize; } else { // invalid throw new InvalidDataException("No fmt chunk before the data chunk in " + fileTitle); } } else { // unsupported chunk stream.Position += (long)ckSize; } // pad byte if ((ckSize & 1) == 1) { stream.Position++; } } // finalize if (dataBytes == null) { throw new InvalidDataException("No data chunk before the end of the file in " + fileTitle); } else { return new WaveData(format, dataBytes); } } } } /// Reads a System.UInt32 from a binary reader with the specified endianness. /// The binary reader. /// The endianness. /// The System.UInt32 read from the reader. /// The stream is closed. /// An I/O error occurs. /// The end of the stream is reached. private static uint ReadUInt32(BinaryReader reader, Endianness endianness) { uint value = reader.ReadUInt32(); if (endianness == Endianness.Big) { unchecked { return (value << 24) | (value & ((uint)0xFF00 << 8)) | ((value & (uint)0xFF0000) >> 8) | (value >> 24); } } else { return value; } } /// Reads a System.UInt16 from a binary reader with the specified endianness. /// The binary reader. /// The endianness. /// The System.UInt16 read from the reader. /// The stream is closed. /// An I/O error occurs. /// The end of the stream is reached. private static ushort ReadUInt16(BinaryReader reader, Endianness endianness) { ushort value = reader.ReadUInt16(); if (endianness == Endianness.Big) { unchecked { return (ushort)(((uint)value << 8) | ((uint)value >> 8)); } } else { return value; } } /// Converts the specified wave data to 8-bit or 16-bit mono. /// The original wave data. /// The wave data converted to 8-bit or 16-bit mono. /// If the bits per sample per channel are less than or equal to 8, the result will be 8-bit mono, otherwise 16-bit mono. internal static WaveData ConvertToMono8Or16(WaveData data) { if ((data.Format.BitsPerSample == 8 | data.Format.BitsPerSample == 16) & data.Format.Channels == 1) { // already in target format return data; } else if (data.Format.Channels != 1) { // convert to mono first return ConvertToMono(data); } else if (data.Format.BitsPerSample < 8) { // less than 8 bits per sample WaveFormat format = new WaveFormat(data.Format.SampleRate, 8, 1); return new WaveData(format, data.Bytes); } else if (data.Format.BitsPerSample < 16) { // between 9 and 15 bits per sample WaveFormat format = new WaveFormat(data.Format.SampleRate, 16, 1); return new WaveData(format, data.Bytes); } else { // more than 16 bits per sample int bytesPerSample = data.Format.BitsPerSample + 7 >> 3; int samples = data.Bytes.Length / bytesPerSample; byte[] bytes = new byte[samples << 1]; for (int i = 0; i < samples; i++) { int j = (i + 1) * bytesPerSample; bytes[2 * i] = data.Bytes[j - 2]; bytes[2 * i + 1] = data.Bytes[j - 1]; } WaveFormat format = new WaveFormat(data.Format.SampleRate, 16, 1); return new WaveData(format, bytes); } } /// Converts the specified wave data to mono. /// The original wave data. /// The wave data converted to mono. /// This function will try to mix the channels, but will revert to a single channel if silence, constructive or destructive interference is detected, or if the number of bits per channel exceeds 48. private static WaveData ConvertToMono(WaveData data) { if (data.Format.Channels == 1) { // is already mono return data; } else { // convert to mono int bytesPerSample = (data.Format.BitsPerSample + 7) / 8; int samples = data.Bytes.Length / (data.Format.Channels * bytesPerSample); /* * In order to detect for silence, constructive interference and * destructive interference, compute the sums of the absolute values * of the signed samples in each channel, considering the high byte only. * */ long[] channelSum = new long[data.Format.Channels]; int position = 0; for (int i = 0; i < samples; i++) { for (int j = 0; j < data.Format.Channels; j++) { int value; if (data.Format.BitsPerSample <= 8) { value = (int)data.Bytes[position + bytesPerSample - 1] - 128; } else { unchecked { value = (int)(sbyte)data.Bytes[position + bytesPerSample - 1]; } } channelSum[j] += Math.Abs(value); position += bytesPerSample; } } /* * Determine the highest of all channel sums. * */ long maximum = 0; for (int i = 0; i < data.Format.Channels; i++) { if (channelSum[i] > maximum) { maximum = channelSum[i]; } } /* * Isolate channels which are not silent. A channel is considered not silent * if its sum is more than 0.39% of the highest sum found in all channels. * */ long silenceThreshold = maximum >> 8; int[] nonSilentChannels = new int[data.Format.Channels]; int nonSilentChannelsCount = 0; for (int i = 0; i < data.Format.Channels; i++) { if (channelSum[i] >= silenceThreshold) { nonSilentChannels[nonSilentChannelsCount] = i; nonSilentChannelsCount++; } } /* * If there is only one non-silent channel, use that channel. * Otherwise, try to mix the non-silent channels. * */ if (nonSilentChannelsCount == 1 | bytesPerSample > 3) { /* Use the only non-silent channel. */ byte[] bytes = new byte[samples * bytesPerSample]; int channel = nonSilentChannels[0]; int to = 0; int from = channel * bytesPerSample; for (int i = 0; i < samples; i++) { for (int j = 0; j < bytesPerSample; j++) { bytes[to] = data.Bytes[from + j]; to++; } from += data.Format.Channels * bytesPerSample; } WaveFormat format = new WaveFormat(data.Format.SampleRate, data.Format.BitsPerSample, 1); return ConvertToMono8Or16(new WaveData(format, bytes)); } else { /* * Try mixing the non-silent channels. In order to detect for constructive * or destructive interference, compute the sum of the absolute values * of the signed samples in the mixed channel, considering the high * byte only. * */ long mixedSum = 0; byte[] bytes = new byte[samples * bytesPerSample]; int from = 0; int to = 0; long bytesFullRange = 1 << (8 * bytesPerSample); long bytesHalfRange = bytesFullRange >> 1; long bytesMinimum = -bytesHalfRange; long bytesMaximum = bytesHalfRange - 1; int mixedRightShift = 8 * (bytesPerSample - 1); for (int i = 0; i < samples; i++) { long mixed = 0; for (int j = 0; j < nonSilentChannelsCount; j++) { if (j == 0) { from += bytesPerSample * nonSilentChannels[0]; } else { from += bytesPerSample * (nonSilentChannels[j] - nonSilentChannels[j - 1]); } if (bytesPerSample == 1) { long sample = (long)data.Bytes[from] - 0x80; mixed += sample; } else { ulong sampleUnsigned = 0; for (int k = 0; k < bytesPerSample; k++) { sampleUnsigned |= (ulong)data.Bytes[from + k] << (k << 3); } unchecked { long sampleSigned = (long)sampleUnsigned; if (sampleSigned >= bytesHalfRange) { sampleSigned -= bytesFullRange; } mixed += sampleSigned; } } } if (bytesPerSample == 1) { mixedSum += Math.Abs(mixed); unchecked { bytes[to] = (byte)(mixed + 0x80); } } else { mixedSum += Math.Abs(mixed >> mixedRightShift); if (mixed < bytesMinimum) { mixed = bytesMinimum; } else if (mixed > bytesMaximum) { mixed = bytesMaximum; } ulong mixedUnsigned; unchecked { if (mixed < 0) { mixed += bytesFullRange; } mixedUnsigned = (ulong)mixed; for (int k = 0; k < bytesPerSample; k++) { bytes[to + k] = (byte)mixedUnsigned; mixedUnsigned >>= 8; } } } from += bytesPerSample * (data.Format.Channels - nonSilentChannels[nonSilentChannelsCount - 1]); to += bytesPerSample; } /* * Determine the lowest of all non-silent channel sums. * */ long minimum = long.MaxValue; for (int i = 0; i < nonSilentChannelsCount; i++) { if (channelSum[nonSilentChannels[i]] < minimum) { minimum = channelSum[nonSilentChannels[i]]; } } /* * Detect constructive and destructive interference. If an interference is * detected, use the first non-silent channel, otherwise the mixed channel. * */ if ( (double)mixedSum < 0.4 * (double)minimum || (double)mixedSum > 1.1 * (double)maximum ) { /* Interference detected. Use the first non-silent channel. */ int channel = nonSilentChannels[0]; from = channel * bytesPerSample; to = 0; for (int i = 0; i < samples; i++) { for (int j = 0; j < bytesPerSample; j++) { bytes[to] = data.Bytes[from + j]; to++; } from += data.Format.Channels * bytesPerSample; } WaveFormat format = new WaveFormat(data.Format.SampleRate, data.Format.BitsPerSample, 1); return ConvertToMono8Or16(new WaveData(format, bytes)); } else { /* No interference detected. Use the mixed channel. */ WaveFormat format = new WaveFormat(data.Format.SampleRate, data.Format.BitsPerSample, 1); return ConvertToMono8Or16(new WaveData(format, bytes)); } } } } } }openbve-1.4.0.10/openBVE/OpenBve/OldParsers/XObjectParser.cs000066400000000000000000002507541171674032100233640ustar00rootroot00000000000000using System; using System.IO; using System.IO.Compression; using OpenBveApi.Colors; using OpenBveApi.Math; namespace OpenBve { internal static class XObjectParser { // read object internal static ObjectManager.StaticObject ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { byte[] Data = System.IO.File.ReadAllBytes(FileName); if (Data.Length < 16 || Data[0] != 120 | Data[1] != 111 | Data[2] != 102 | Data[3] != 32) { // not an x object Interface.AddMessage(Interface.MessageType.Error, false, "Invalid X object file encountered in " + FileName); return null; } if (Data[4] != 48 | Data[5] != 51 | Data[6] != 48 | Data[7] != 50 & Data[7] != 51) { // unrecognized version System.Text.ASCIIEncoding Ascii = new System.Text.ASCIIEncoding(); string s = new string(Ascii.GetChars(Data, 4, 4)); Interface.AddMessage(Interface.MessageType.Error, false, "Unsupported X object file version " + s + " encountered in " + FileName); } // floating-point format int FloatingPointSize; if (Data[12] == 48 & Data[13] == 48 & Data[14] == 51 & Data[15] == 50) { FloatingPointSize = 32; } else if (Data[12] == 48 & Data[13] == 48 & Data[14] == 54 & Data[15] == 52) { FloatingPointSize = 64; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Unsupported floating point format encountered in X object file " + FileName); return null; } // supported floating point format if (Data[8] == 116 & Data[9] == 120 & Data[10] == 116 & Data[11] == 32) { // textual flavor return LoadTextualX(FileName, System.IO.File.ReadAllText(FileName), Encoding, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); } else if (Data[8] == 98 & Data[9] == 105 & Data[10] == 110 & Data[11] == 32) { // binary flavor return LoadBinaryX(FileName, Data, 16, Encoding, FloatingPointSize, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); } else if (Data[8] == 116 & Data[9] == 122 & Data[10] == 105 & Data[11] == 112) { // compressed textual flavor #if !DEBUG try { #endif byte[] Uncompressed = Decompress(Data); string Text = Encoding.GetString(Uncompressed); return LoadTextualX(FileName, Text, Encoding, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); #if !DEBUG } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "An unexpected error occured (" + ex.Message + ") while attempting to decompress the binary X object file encountered in " + FileName); return null; } #endif } else if (Data[8] == 98 & Data[9] == 122 & Data[10] == 105 & Data[11] == 112) { // compressed binary flavor #if !DEBUG try { #endif byte[] Uncompressed = Decompress(Data); return LoadBinaryX(FileName, Uncompressed, 0, Encoding, FloatingPointSize, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY); #if !DEBUG } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "An unexpected error occured (" + ex.Message + ") while attempting to decompress the binary X object file encountered in " + FileName); return null; } #endif } else { // unsupported flavor Interface.AddMessage(Interface.MessageType.Error, false, "Unsupported X object file encountered in " + FileName); return null; } } // ================================ // decompress private static byte[] Decompress(byte[] Data) { byte[] Target; using (MemoryStream InputStream = new MemoryStream(Data)) { InputStream.Position = 26; using (DeflateStream Deflate = new DeflateStream(InputStream, CompressionMode.Decompress, true)) { using (MemoryStream OutputStream = new MemoryStream()) { byte[] Buffer = new byte[4096]; while (true) { int Count = Deflate.Read(Buffer, 0, Buffer.Length); if (Count != 0) { OutputStream.Write(Buffer, 0, Count); } if (Count != Buffer.Length) { break; } } Target = new byte[OutputStream.Length]; OutputStream.Position = 0; OutputStream.Read(Target, 0, Target.Length); } } } return Target; } // ================================ // template private class Template { internal string Name; internal string[] Members; internal Template(string Name, string[] Members) { this.Name = Name; this.Members = Members; } } private static Template[] Templates = new Template[] { new Template("Mesh", new string[] { "DWORD", "Vector[0]", "DWORD", "MeshFace[2]", "[...]" }), new Template("Vector", new string[] { "float", "float", "float" }), new Template("MeshFace", new string[] { "DWORD", "DWORD[0]" }), new Template("MeshMaterialList", new string[] { "DWORD", "DWORD", "DWORD[1]", "[...]" }), new Template("Material", new string[] { "Color32", "float", "Color24", "Color24", "[...]" }), new Template("Color32", new string[] { "float", "float", "float", "float" }), new Template("Color24", new string[] { "float", "float", "float" }), new Template("TextureFilename", new string[] { "string" }), new Template("MeshTextureCoords", new string[] { "DWORD", "Coords2d[0]" }), new Template("Coords2d", new string[] { "float", "float" }), new Template("MeshNormals", new string[] { "DWORD", "Vector[0]", "DWORD", "MeshFace[2]" }) }; // data private class Structure { internal string Name; internal object[] Data; internal Structure(string Name, object[] Data) { this.Name = Name; this.Data = Data; } } // get template private static Template GetTemplate(string Name) { for (int i = 0; i < Templates.Length; i++) { if (Templates[i].Name == Name) { return Templates[i]; } } return new Template(Name, new string[] { "[???]" }); } // ================================ // load textual x private static ObjectManager.StaticObject LoadTextualX(string FileName, string Text, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { // load string[] Lines = Text.Replace("\u000D\u000A", "\u2028").Split(new char[] { '\u000A', '\u000C', '\u000D', '\u0085', '\u2028', '\u2029' }, StringSplitOptions.None); // strip away comments bool Quote = false; for (int i = 0; i < Lines.Length; i++) { for (int j = 0; j < Lines[i].Length; j++) { if (Lines[i][j] == '"') Quote = !Quote; if (!Quote) { if (Lines[i][j] == '#' || j < Lines[i].Length - 1 && Lines[i].Substring(j, 2) == "//") { Lines[i] = Lines[i].Substring(0, j); break; } } } } // strip away header if (Lines.Length == 0 || Lines[0].Length < 16) { Interface.AddMessage(Interface.MessageType.Error, false, "The textual X object file is invalid at line 1 in " + FileName); return null; } Lines[0] = Lines[0].Substring(16); // join lines System.Text.StringBuilder Builder = new System.Text.StringBuilder(); for (int i = 0; i < Lines.Length; i++) { Builder.Append(Lines[i]); } string Content = Builder.ToString(); // parse file int Position = 0; Structure Structure; if (!ReadTextualTemplate(FileName, Content, ref Position, new Template("", new string[] { "[...]" }), false, out Structure)) { return null; } // process structure ObjectManager.StaticObject Object; if (!ProcessStructure(FileName, Structure, out Object, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY)) { return null; } return Object; } // read textual template private static bool ReadTextualTemplate(string FileName, string Content, ref int Position, Template Template, bool Inline, out Structure Structure) { Structure = new Structure(Template.Name, new object[] { }); int i = Position; bool q = false; int m; for (m = 0; m < Template.Members.Length; m++) { if (Position >= Content.Length) break; if (Template.Members[m] == "[???]") { // unknown data accepted while (Position < Content.Length) { if (q) { if (Content[Position] == '"') q = false; } else { if (Content[Position] == '"') { q = true; } else if (Content[Position] == ',' | Content[Position] == ';') { i = Position + 1; } else if (Content[Position] == '{') { string s = Content.Substring(i, Position - i).Trim(); Structure o; Position++; if (!ReadTextualTemplate(FileName, Content, ref Position, GetTemplate(s), false, out o)) { return false; } Position--; i = Position + 1; } else if (Content[Position] == '}') { Position++; return true; } } Position++; } m--; } else if (Template.Members[m] == "[...]") { // any template accepted while (Position < Content.Length) { if (q) { if (Content[Position] == '"') q = false; } else { if (Content[Position] == '"') { q = true; } else if (Content[Position] == '{') { string s = Content.Substring(i, Position - i).Trim(); Structure o; Position++; if (!ReadTextualTemplate(FileName, Content, ref Position, GetTemplate(s), false, out o)) { return false; } Position--; Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = o; i = Position + 1; } else if (Content[Position] == '}') { if (Inline) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected closing brace encountered in inlined template " + Template.Name + " in textual X object file " + FileName); return false; } else { Position++; return true; } } else if (Content[Position] == ',') { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected comma encountered in template " + Template.Name + " in textual X object file " + FileName); return false; } else if (Content[Position] == ';') { if (Inline) { Position++; return true; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected semicolon encountered in template " + Template.Name + " in textual X object file " + FileName); return false; } } } Position++; } m--; } else if (Template.Members[m].EndsWith("]", StringComparison.Ordinal)) { // inlined array expected string r = Template.Members[m].Substring(0, Template.Members[m].Length - 1); int h = r.IndexOf('['); if (h >= 0) { string z = r.Substring(h + 1, r.Length - h - 1); r = r.Substring(0, h); if (!int.TryParse(z, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out h)) { Interface.AddMessage(Interface.MessageType.Error, false, "The internal format description for a template array is invalid in template " + Template.Name + " in textual X object file " + FileName); return false; } if (h < 0 || h >= Structure.Data.Length || !(Structure.Data[h] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "The internal format description for a template array is invalid in template " + Template.Name + " in textual X object file " + FileName); return false; } h = (int)Structure.Data[h]; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The internal format description for a template array is invalid in template " + Template.Name + " in textual X object file " + FileName); return false; } if (r == "DWORD") { // dword array int[] o = new int[h]; if (h == 0) { // empty array while (Position < Content.Length) { if (Content[Position] == ';') { Position++; break; } else if (!char.IsWhiteSpace(Content, Position)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing an array in template " + Template.Name + " in textual X object file " + FileName); return false; } else { Position++; } } } else { // non-empty array for (int k = 0; k < h; k++) { while (Position < Content.Length) { if (Content[Position] == '{' | Content[Position] == '}' | Content[Position] == '"') { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing a DWORD array in template " + Template.Name + " in textual X object file " + FileName); return false; } else if (Content[Position] == ',') { if (k == h - 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing a DWORD array in template " + Template.Name + " in textual X object file " + FileName); return false; } break; } else if (Content[Position] == ';') { if (k != h - 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing a DWORD array in template " + Template.Name + " in textual X object file " + FileName); return false; } break; } Position++; } if (Position == Content.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "DWORD array was not terminated at the end of the file in template " + Template.Name + " in textual X object file " + FileName); return false; } string s = Content.Substring(i, Position - i); Position++; i = Position; if (!int.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out o[k])) { Interface.AddMessage(Interface.MessageType.Error, false, "DWORD could not be parsed in array in template " + Template.Name + " in textual X object file " + FileName); } } } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = o; } else if (r == "float") { // float array double[] o = new double[h]; if (h == 0) { // empty array while (Position < Content.Length) { if (Content[Position] == ';') { Position++; break; } else if (!char.IsWhiteSpace(Content, Position)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing an array in template " + Template.Name + " in textual X object file " + FileName); return false; } else { Position++; } } } else { // non-empty array for (int k = 0; k < h; k++) { while (Position < Content.Length) { if (Content[Position] == '{' | Content[Position] == '}' | Content[Position] == '"') { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing a float array in template " + Template.Name + " in textual X object file " + FileName); return false; } else if (Content[Position] == ',') { if (k == h - 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing a float array in template " + Template.Name + " in textual X object file " + FileName); return false; } break; } else if (Content[Position] == ';') { if (k != h - 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing a float array in template " + Template.Name + " in textual X object file " + FileName); return false; } break; } Position++; } if (Position == Content.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "float array was not terminated at the end of the file in template " + Template.Name + " in textual X object file " + FileName); return false; } string s = Content.Substring(i, Position - i); Position++; i = Position; if (!double.TryParse(s, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out o[k])) { Interface.AddMessage(Interface.MessageType.Error, false, "float could not be parsed in array in template " + Template.Name + " in textual X object file " + FileName); } } } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = o; } else { // non-primitive array Template t = GetTemplate(r); Structure[] o = new Structure[h]; if (h == 0) { // empty array while (Position < Content.Length) { if (Content[Position] == ';') { Position++; break; } else if (!char.IsWhiteSpace(Content, Position)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing an array in template " + Template.Name + " in textual X object file " + FileName); return false; } else { Position++; } } } else { int k; for (k = 0; k < h; k++) { if (!ReadTextualTemplate(FileName, Content, ref Position, t, true, out o[k])) { return false; } if (k < h - 1) { // most elements while (Position < Content.Length) { if (Content[Position] == ',') { Position++; break; } else if (!char.IsWhiteSpace(Content, Position)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing an array in template " + Template.Name + " in textual X object file " + FileName); return false; } else { Position++; } } if (Position == Content.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "Array was not continued at the end of the file in template " + Template.Name + " in textual X object file " + FileName); return false; } } else { // last element while (Position < Content.Length) { if (Content[Position] == ';') { Position++; break; } else if (!char.IsWhiteSpace(Content, Position)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing an array in template " + Template.Name + " in textual X object file " + FileName); return false; } else { Position++; } } if (Position == Content.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "Array was not terminated at the end of the file in template " + Template.Name + " in textual X object file " + FileName); return false; } } } if (k < h) { return false; } } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = o; } i = Position; } else { // inlined template or primitive expected switch (Template.Members[m]) { case "DWORD": while (Position < Content.Length) { if (Content[Position] == '{' | Content[Position] == '}' | Content[Position] == ',' | Content[Position] == '"') { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing a DWORD in template " + Template.Name + " in textual X object file " + FileName); return false; } else if (Content[Position] == ';') { string s = Content.Substring(i, Position - i).Trim(); int a; if (!int.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "DWORD could not be parsed in template " + Template.Name + " in textual X object file " + FileName); return false; } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = a; Position++; i = Position; break; } Position++; } break; case "float": while (Position < Content.Length) { if (Content[Position] == '{' | Content[Position] == '}' | Content[Position] == ',' | Content[Position] == '"') { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing a DWORD in template " + Template.Name + " in textual X object file " + FileName); return false; } else if (Content[Position] == ';') { string s = Content.Substring(i, Position - i).Trim(); double a; if (!double.TryParse(s, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "float could not be parsed in template " + Template.Name + " in textual X object file " + FileName); return false; } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = a; Position++; i = Position; break; } Position++; } break; case "string": while (Position < Content.Length) { if (Content[Position] == '"') { Position++; break; } else if (!char.IsWhiteSpace(Content, Position)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing a string in template " + Template.Name + " in textual X object file " + FileName); return false; } else { Position++; } } if (Position >= Content.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected end of file encountered while processing a string in template " + Template.Name + " in textual X object file " + FileName); return false; } i = Position; while (Position < Content.Length) { if (Content[Position] == '"') { Position++; break; } else { Position++; } } if (Position >= Content.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected end of file encountered while processing a string in template " + Template.Name + " in textual X object file " + FileName); return false; } string t = Content.Substring(i, Position - i - 1); while (Position < Content.Length) { if (Content[Position] == ';') { Position++; break; } else if (!char.IsWhiteSpace(Content, Position)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing a string in template " + Template.Name + " in textual X object file " + FileName); return false; } else { Position++; } } if (Position >= Content.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected end of file encountered while processing a string in template " + Template.Name + " in textual X object file " + FileName); return false; } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = t; i = Position; break; default: { Structure o; if (!ReadTextualTemplate(FileName, Content, ref Position, GetTemplate(Template.Members[m]), true, out o)) { return false; } while (Position < Content.Length) { if (Content[Position] == ';') { Position++; break; } else if (!char.IsWhiteSpace(Content, Position)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered while processing an inlined template in template " + Template.Name + " in textual X object file " + FileName); return false; } else { Position++; } } if (Position >= Content.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected end of file encountered while processing an inlined template in template " + Template.Name + " in textual X object file " + FileName); return false; } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = o; i = Position; } break; } } } if (m >= Template.Members.Length) { if (Inline) { return true; } else { // closed non-inline template while (Position < Content.Length) { if (Content[Position] == '}') { Position++; break; } else if (!char.IsWhiteSpace(Content, Position)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid character encountered in template " + Template.Name + " in textual X object file " + FileName); return false; } else { Position++; } } if (Position >= Content.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected end of file encountered in template " + Template.Name + " in textual X object file " + FileName); return false; } return true; } } else { if (q) { Interface.AddMessage(Interface.MessageType.Error, false, "Quotation mark not closed at the end of the file in template " + Template.Name + " in textual X object file " + FileName); return false; } else if (Template.Name.Length != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected end of file encountered in template " + Template.Name + " in textual X object file " + FileName); return false; } else { return true; } } } // ================================ // load binary x private static ObjectManager.StaticObject LoadBinaryX(string FileName, byte[] Data, int StartingPosition, System.Text.Encoding Encoding, int FloatingPointSize, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { // parse file Structure Structure; try { bool Result; using (System.IO.MemoryStream Stream = new System.IO.MemoryStream(Data)) { using (System.IO.BinaryReader Reader = new System.IO.BinaryReader(Stream)) { Stream.Position = StartingPosition; BinaryCache Cache = new BinaryCache(); Cache.IntegersRemaining = 0; Cache.FloatsRemaining = 0; Result = ReadBinaryTemplate(FileName, Reader, FloatingPointSize, new Template("", new string[] { "[...]" }), false, ref Cache, out Structure); Reader.Close(); } Stream.Close(); } if (!Result) { return null; } } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "Unhandled error (" + ex.Message + ") encountered in binary X object file " + FileName); return null; } // process structure ObjectManager.StaticObject Object; if (!ProcessStructure(FileName, Structure, out Object, LoadMode, ForceTextureRepeatX, ForceTextureRepeatY)) { return null; } return Object; } // read binary template private struct BinaryCache { internal int[] Integers; internal int IntegersRemaining; internal double[] Floats; internal int FloatsRemaining; } private static bool ReadBinaryTemplate(string FileName, System.IO.BinaryReader Reader, int FloatingPointSize, Template Template, bool Inline, ref BinaryCache Cache, out Structure Structure) { const short TOKEN_NAME = 0x1; const short TOKEN_STRING = 0x2; const short TOKEN_INTEGER = 0x3; const short TOKEN_INTEGER_LIST = 0x6; const short TOKEN_FLOAT_LIST = 0x7; const short TOKEN_OBRACE = 0xA; const short TOKEN_CBRACE = 0xB; const short TOKEN_COMMA = 0x13; const short TOKEN_SEMICOLON = 0x14; Structure = new Structure(Template.Name, new object[] { }); System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; System.Text.ASCIIEncoding Ascii = new System.Text.ASCIIEncoding(); int m; for (m = 0; m < Template.Members.Length; m++) { if (Template.Members[m] == "[???]") { // unknown template int Level = 0; if (Cache.IntegersRemaining != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "An integer list was not depleted at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); } else if (Cache.FloatsRemaining != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "A float list was not depleted at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); } short Token = Reader.ReadInt16(); switch (Token) { case TOKEN_NAME: { Level++; int n = Reader.ReadInt32(); if (n < 1) { Interface.AddMessage(Interface.MessageType.Error, false, "count is invalid in TOKEN_NAME at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } Reader.BaseStream.Position += n; Token = Reader.ReadInt16(); if (Token != TOKEN_OBRACE) { Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_OBRACE expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } } break; case TOKEN_INTEGER: { Reader.BaseStream.Position += 4; } break; case TOKEN_INTEGER_LIST: { int n = Reader.ReadInt32(); if (n < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "count is invalid in TOKEN_INTEGER_LIST at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } Reader.BaseStream.Position += 4 * n; } break; case TOKEN_FLOAT_LIST: { int n = Reader.ReadInt32(); if (n < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "count is invalid in TOKEN_FLOAT_LIST at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } Reader.BaseStream.Position += (FloatingPointSize >> 3) * n; } break; case TOKEN_STRING: { int n = Reader.ReadInt32(); if (n < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "count is invalid in TOKEN_STRING at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } Reader.BaseStream.Position += n; Token = Reader.ReadInt16(); if (Token != TOKEN_COMMA & Token != TOKEN_SEMICOLON) { Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_COMMA or TOKEN_SEMICOLON expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } } break; case TOKEN_OBRACE: Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected token TOKEN_OBRACE encountered at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; case TOKEN_CBRACE: if (Level == 0) return true; Level--; break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Unknown token encountered at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } m--; } else if (Template.Members[m] == "[...]") { // any template if (Cache.IntegersRemaining != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "An integer list was not depleted at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); } else if (Cache.FloatsRemaining != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "A float list was not depleted at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); } if (Template.Name.Length == 0 && Reader.BaseStream.Position == Reader.BaseStream.Length) { // end of file return true; } short Token = Reader.ReadInt16(); switch (Token) { case TOKEN_NAME: int n = Reader.ReadInt32(); if (n < 1) { Interface.AddMessage(Interface.MessageType.Error, false, "count is invalid in TOKEN_NAME at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } string Name = new string(Ascii.GetChars(Reader.ReadBytes(n))); Token = Reader.ReadInt16(); if (Token != TOKEN_OBRACE) { Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_OBRACE expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } Structure o; if (!ReadBinaryTemplate(FileName, Reader, FloatingPointSize, GetTemplate(Name), false, ref Cache, out o)) { return false; } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = o; break; case TOKEN_CBRACE: if (Template.Name.Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected TOKEN_CBRACE encountered at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } m++; break; default: Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_NAME or TOKEN_CBRACE expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } m--; } else if (Template.Members[m].EndsWith("]", StringComparison.Ordinal)) { // inlined array expected string r = Template.Members[m].Substring(0, Template.Members[m].Length - 1); int h = r.IndexOf('['); if (h >= 0) { string z = r.Substring(h + 1, r.Length - h - 1); r = r.Substring(0, h); if (!int.TryParse(z, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out h)) { Interface.AddMessage(Interface.MessageType.Error, false, "The internal format description for a template array is invalid in template " + Template.Name + " in binary X object file " + FileName); return false; } if (h < 0 || h >= Structure.Data.Length || !(Structure.Data[h] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "The internal format description for a template array is invalid in template " + Template.Name + " in binary X object file " + FileName); return false; } h = (int)Structure.Data[h]; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The internal format description for a template array is invalid in template " + Template.Name + " in binary X object file " + FileName); return false; } if (r == "DWORD") { // dword array int[] o = new int[h]; for (int i = 0; i < h; i++) { if (Cache.IntegersRemaining != 0) { // use cached integer int a = Cache.Integers[Cache.IntegersRemaining - 1]; Cache.IntegersRemaining--; o[i] = a; } else if (Cache.FloatsRemaining != 0) { // cannot use cached float Interface.AddMessage(Interface.MessageType.Error, false, "A float list was not depleted at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } else { while (true) { short Token = Reader.ReadInt16(); if (Token == TOKEN_INTEGER) { int a = Reader.ReadInt32(); o[i] = a; break; } else if (Token == TOKEN_INTEGER_LIST) { int n = Reader.ReadInt32(); if (n < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "count is invalid in TOKEN_INTEGER_LIST at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } if (n != 0) { Cache.Integers = new int[n]; for (int j = 0; j < n; i++) { Cache.Integers[n - j - 1] = Reader.ReadInt32(); } Cache.IntegersRemaining = n - 1; int a = Cache.Integers[Cache.IntegersRemaining]; o[i] = a; break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_INTEGER or TOKEN_INTEGER_LIST expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } } } } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = o; } else if (r == "float") { // float array double[] o = new double[h]; for (int i = 0; i < h; i++) { if (Cache.IntegersRemaining != 0) { // cannot use cached integer Interface.AddMessage(Interface.MessageType.Error, false, "An integer list was not depleted at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } else if (Cache.FloatsRemaining != 0) { // use cached float double a = Cache.Floats[Cache.FloatsRemaining - 1]; Cache.FloatsRemaining--; o[i] = a; } else { while (true) { short Token = Reader.ReadInt16(); if (Token == TOKEN_FLOAT_LIST) { int n = Reader.ReadInt32(); if (n < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "count is invalid in TOKEN_FLOAT_LIST at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } if (n != 0) { Cache.Floats = new double[n]; for (int j = 0; j < n; i++) { if (FloatingPointSize == 32) { Cache.Floats[n - j - 1] = (double)Reader.ReadSingle(); } else if (FloatingPointSize == 64) { Cache.Floats[n - j - 1] = Reader.ReadDouble(); } } Cache.FloatsRemaining = n - 1; double a = Cache.Floats[Cache.FloatsRemaining]; o[i] = a; break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_FLOAT_LIST expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } } } } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = o; } else { // template array Structure[] o = new Structure[h]; for (int i = 0; i < h; i++) { ReadBinaryTemplate(FileName, Reader, FloatingPointSize, GetTemplate(r), true, ref Cache, out o[i]); } Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = o; } } else { // inlined template or primitive expected switch (Template.Members[m]) { case "DWORD": // dword expected if (Cache.IntegersRemaining != 0) { // use cached integer int a = Cache.Integers[Cache.IntegersRemaining - 1]; Cache.IntegersRemaining--; Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = a; } else if (Cache.FloatsRemaining != 0) { // cannot use cached float Interface.AddMessage(Interface.MessageType.Error, false, "A float list was not depleted at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } else { // read new data while (true) { short Token = Reader.ReadInt16(); if (Token == TOKEN_INTEGER) { int a = Reader.ReadInt32(); Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = a; break; } else if (Token == TOKEN_INTEGER_LIST) { int n = Reader.ReadInt32(); if (n < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "count is invalid in TOKEN_INTEGER_LIST at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } if (n != 0) { Cache.Integers = new int[n]; for (int i = 0; i < n; i++) { Cache.Integers[n - i - 1] = Reader.ReadInt32(); } Cache.IntegersRemaining = n - 1; int a = Cache.Integers[Cache.IntegersRemaining]; Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = a; break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_INTEGER or TOKEN_INTEGER_LIST expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } } } break; case "float": // float expected if (Cache.IntegersRemaining != 0) { // cannot use cached integer Interface.AddMessage(Interface.MessageType.Error, false, "An integer list was not depleted at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } else if (Cache.FloatsRemaining != 0) { // use cached float double a = Cache.Floats[Cache.FloatsRemaining - 1]; Cache.FloatsRemaining--; Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = a; } else { // read new data while (true) { short Token = Reader.ReadInt16(); if (Token == TOKEN_FLOAT_LIST) { int n = Reader.ReadInt32(); if (n < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "count is invalid in TOKEN_FLOAT_LIST at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } if (n != 0) { Cache.Floats = new double[n]; for (int i = 0; i < n; i++) { if (FloatingPointSize == 32) { Cache.Floats[n - i - 1] = (double)Reader.ReadSingle(); } else if (FloatingPointSize == 64) { Cache.Floats[n - i - 1] = Reader.ReadDouble(); } } Cache.FloatsRemaining = n - 1; double a = Cache.Floats[Cache.FloatsRemaining]; Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = a; break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_FLOAT_LIST expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } } } break; case "string": { // string expected if (Cache.IntegersRemaining != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "An integer list was not depleted at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); } else if (Cache.FloatsRemaining != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "A float list was not depleted at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); } short Token = Reader.ReadInt16(); if (Token == TOKEN_STRING) { int n = Reader.ReadInt32(); if (n < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "count is invalid in TOKEN_STRING at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } string s = new string(Ascii.GetChars(Reader.ReadBytes(n))); Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = s; Token = Reader.ReadInt16(); if (Token != TOKEN_SEMICOLON) { Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_SEMICOLON expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_STRING expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } } break; default: // inlined template expected Structure o; ReadBinaryTemplate(FileName, Reader, FloatingPointSize, GetTemplate(Template.Members[m]), true, ref Cache, out o); Array.Resize(ref Structure.Data, Structure.Data.Length + 1); Structure.Data[Structure.Data.Length - 1] = o; break; } } } if (Inline) { return true; } else { string s = Template.Members[Template.Members.Length - 1]; if (s != "[???]" & s != "[...]") { int Token = Reader.ReadInt16(); if (Token != TOKEN_CBRACE) { Interface.AddMessage(Interface.MessageType.Error, false, "TOKEN_CBRACE expected at position 0x" + Reader.BaseStream.Position.ToString("X", Culture) + " in binary X object file " + FileName); return false; } } return true; } } // ================================ // structures private struct Material { internal Color32 faceColor; internal Color24 specularColor; internal Color24 emissiveColor; internal string TextureFilename; } // process structure private static bool ProcessStructure(string FileName, Structure Structure, out ObjectManager.StaticObject Object, ObjectManager.ObjectLoadMode LoadMode, bool ForceTextureRepeatX, bool ForceTextureRepeatY) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; Object = new ObjectManager.StaticObject(); Object.Mesh.Faces = new World.MeshFace[] { }; Object.Mesh.Materials = new World.MeshMaterial[] { }; Object.Mesh.Vertices = new World.Vertex[] { }; // file for (int i = 0; i < Structure.Data.Length; i++) { Structure f = Structure.Data[i] as Structure; if (f == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Top-level inlined arguments are invalid in x object file " + FileName); return false; } switch (f.Name) { case "Mesh": { // mesh if (f.Data.Length < 4) { Interface.AddMessage(Interface.MessageType.Error, false, "Mesh is expected to have at least 4 arguments in x object file " + FileName); return false; } else if (!(f.Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nVertices is expected to be a DWORD in Mesh in x object file " + FileName); return false; } else if (!(f.Data[1] is Structure[])) { Interface.AddMessage(Interface.MessageType.Error, false, "vertices[nVertices] is expected to be a Vector array in Mesh in x object file " + FileName); return false; } else if (!(f.Data[2] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaces is expected to be a DWORD in Mesh in x object file " + FileName); return false; } else if (!(f.Data[3] is Structure[])) { Interface.AddMessage(Interface.MessageType.Error, false, "faces[nFaces] is expected to be a MeshFace array in Mesh in x object file " + FileName); return false; } int nVertices = (int)f.Data[0]; if (nVertices < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nVertices is expected to be non-negative in Mesh in x object file " + FileName); return false; } Structure[] vertices = (Structure[])f.Data[1]; if (nVertices != vertices.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nVertices does not match with the length of array vertices in Mesh in x object file " + FileName); return false; } int nFaces = (int)f.Data[2]; if (nFaces < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaces is expected to be non-negative in Mesh in x object file " + FileName); return false; } Structure[] faces = (Structure[])f.Data[3]; if (nFaces != faces.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaces does not match with the length of array faces in Mesh in x object file " + FileName); return false; } // collect vertices World.Vertex[] Vertices = new World.Vertex[nVertices]; for (int j = 0; j < nVertices; j++) { if (vertices[j].Name != "Vector") { Interface.AddMessage(Interface.MessageType.Error, false, "vertices[" + j.ToString(Culture) + "] is expected to be of template Vertex in Mesh in x object file " + FileName); return false; } else if (vertices[j].Data.Length != 3) { Interface.AddMessage(Interface.MessageType.Error, false, "vertices[" + j.ToString(Culture) + "] is expected to have 3 arguments in Mesh in x object file " + FileName); return false; } else if (!(vertices[j].Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "x is expected to be a float in vertices[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } else if (!(vertices[j].Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "y is expected to be a float in vertices[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } else if (!(vertices[j].Data[2] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "z is expected to be a float in vertices[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } double x = (double)vertices[j].Data[0]; double y = (double)vertices[j].Data[1]; double z = (double)vertices[j].Data[2]; Vertices[j].Coordinates = new Vector3(x, y, z); } // collect faces int[][] Faces = new int[nFaces][]; World.Vector3Df[][] FaceNormals = new World.Vector3Df[nFaces][]; int[] FaceMaterials = new int[nFaces]; for (int j = 0; j < nFaces; j++) { FaceMaterials[j] = -1; } for (int j = 0; j < nFaces; j++) { if (faces[j].Name != "MeshFace") { Interface.AddMessage(Interface.MessageType.Error, false, "faces[" + j.ToString(Culture) + "] is expected to be of template MeshFace in Mesh in x object file " + FileName); return false; } else if (faces[j].Data.Length != 2) { Interface.AddMessage(Interface.MessageType.Error, false, "face[" + j.ToString(Culture) + "] is expected to have 2 arguments in Mesh in x object file " + FileName); return false; } else if (!(faces[j].Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices is expected to be a DWORD in face[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } else if (!(faces[j].Data[1] is int[])) { Interface.AddMessage(Interface.MessageType.Error, false, "faceVertexIndices[nFaceVertexIndices] is expected to be a DWORD array in face[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } int nFaceVertexIndices = (int)faces[j].Data[0]; if (nFaceVertexIndices < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices is expected to be non-negative in MeshFace in Mesh in x object file " + FileName); return false; } int[] faceVertexIndices = (int[])faces[j].Data[1]; if (nFaceVertexIndices != faceVertexIndices.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices does not match with the length of array faceVertexIndices in face[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } Faces[j] = new int[nFaceVertexIndices]; FaceNormals[j] = new World.Vector3Df[nFaceVertexIndices]; for (int k = 0; k < nFaceVertexIndices; k++) { if (faceVertexIndices[k] < 0 | faceVertexIndices[k] >= nVertices) { Interface.AddMessage(Interface.MessageType.Error, false, "faceVertexIndices[" + k.ToString(Culture) + "] does not reference a valid vertex in face[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } Faces[j][k] = faceVertexIndices[k]; FaceNormals[j][k] = new World.Vector3Df(0.0f, 0.0f, 0.0f); } } // collect additional templates Material[] Materials = new Material[] { }; for (int j = 4; j < f.Data.Length; j++) { Structure g = f.Data[j] as Structure; if (g == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected inlined argument encountered in Mesh in x object file " + FileName); return false; } switch (g.Name) { case "MeshMaterialList": { // meshmateriallist if (g.Data.Length < 3) { Interface.AddMessage(Interface.MessageType.Error, false, "MeshMaterialList is expected to have at least 3 arguments in Mesh in x object file " + FileName); return false; } else if (!(g.Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nMaterials is expected to be a DWORD in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(g.Data[1] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceIndexes is expected to be a DWORD in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(g.Data[2] is int[])) { Interface.AddMessage(Interface.MessageType.Error, false, "faceIndexes[nFaceIndexes] is expected to be a DWORD array in MeshMaterialList in Mesh in x object file " + FileName); return false; } int nMaterials = (int)g.Data[0]; if (nMaterials < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nMaterials is expected to be non-negative in MeshMaterialList in Mesh in x object file " + FileName); return false; } int nFaceIndexes = (int)g.Data[1]; if (nFaceIndexes < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceIndexes is expected to be non-negative in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (nFaceIndexes > nFaces) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceIndexes does not reference valid faces in MeshMaterialList in Mesh in x object file " + FileName); return false; } int[] faceIndexes = (int[])g.Data[2]; if (nFaceIndexes != faceIndexes.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceIndexes does not match with the length of array faceIndexes in face[" + j.ToString(Culture) + "] in Mesh in x object file " + FileName); return false; } for (int k = 0; k < nFaceIndexes; k++) { if (faceIndexes[k] < 0 | faceIndexes[k] >= nMaterials) { Interface.AddMessage(Interface.MessageType.Error, false, "faceIndexes[" + k.ToString(Culture) + "] does not reference a valid Material template in MeshMaterialList in Mesh in x object file " + FileName); return false; } } // collect material templates int mn = Materials.Length; Array.Resize(ref Materials, mn + nMaterials); for (int k = 0; k < nMaterials; k++) { Materials[mn + k].faceColor = new Color32(255, 255, 255, 255); Materials[mn + k].specularColor = new Color24(0, 0, 0); Materials[mn + k].emissiveColor = new Color24(0, 0, 0); Materials[mn + k].TextureFilename = null; } int MaterialIndex = mn; for (int k = 3; k < g.Data.Length; k++) { Structure h = g.Data[k] as Structure; if (h == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected inlined argument encountered in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (h.Name != "Material") { Interface.AddMessage(Interface.MessageType.Error, false, "Material template expected in MeshMaterialList in Mesh in x object file " + FileName); return false; } else { // material if (h.Data.Length < 4) { Interface.AddMessage(Interface.MessageType.Error, false, "Material is expected to have at least 4 arguments in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(h.Data[0] is Structure)) { Interface.AddMessage(Interface.MessageType.Error, false, "faceColor is expected to be a Color32 in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(h.Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "power is expected to be a float in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(h.Data[2] is Structure)) { Interface.AddMessage(Interface.MessageType.Error, false, "specularColor is expected to be a Color32 in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(h.Data[3] is Structure)) { Interface.AddMessage(Interface.MessageType.Error, false, "emissiveColor is expected to be a Color32 in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } Structure faceColor = (Structure)h.Data[0]; Structure specularColor = (Structure)h.Data[2]; Structure emissiveColor = (Structure)h.Data[3]; double red, green, blue, alpha; // collect face color if (faceColor.Name != "Color32") { Interface.AddMessage(Interface.MessageType.Error, false, "faceColor is expected to be a Color32 in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (faceColor.Data.Length != 4) { Interface.AddMessage(Interface.MessageType.Error, false, "faceColor is expected to have 4 arguments in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(faceColor.Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be a float in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(faceColor.Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be a float in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(faceColor.Data[2] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be a float in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(faceColor.Data[3] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "alpha is expected to be a float in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } red = (double)faceColor.Data[0]; green = (double)faceColor.Data[1]; blue = (double)faceColor.Data[2]; alpha = (double)faceColor.Data[3]; if (red < 0.0 | red > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); red = red < 0.5 ? 0.0 : 1.0; } if (green < 0.0 | green > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); green = green < 0.5 ? 0.0 : 1.0; } if (blue < 0.0 | blue > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); blue = blue < 0.5 ? 0.0 : 1.0; } if (alpha < 0.0 | alpha > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "alpha is expected to be in the range from 0.0 to 1.0 in faceColor in Material in MeshMaterialList in Mesh in x object file " + FileName); alpha = alpha < 0.5 ? 0.0 : 1.0; } Materials[MaterialIndex].faceColor = new Color32((byte)Math.Round(255.0 * red), (byte)Math.Round(255.0 * green), (byte)Math.Round(255.0 * blue), (byte)Math.Round(255.0 * alpha)); // collect specular color if (specularColor.Name != "Color24") { Interface.AddMessage(Interface.MessageType.Error, false, "specularColor is expected to be a Color24 in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (specularColor.Data.Length != 3) { Interface.AddMessage(Interface.MessageType.Error, false, "specularColor is expected to have 3 arguments in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(specularColor.Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be a float in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(specularColor.Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be a float in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(specularColor.Data[2] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be a float in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } red = (double)specularColor.Data[0]; green = (double)specularColor.Data[1]; blue = (double)specularColor.Data[2]; if (red < 0.0 | red > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be in the range from 0.0 to 1.0 in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); red = red < 0.5 ? 0.0 : 1.0; } if (green < 0.0 | green > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be in the range from 0.0 to 1.0 in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); green = green < 0.5 ? 0.0 : 1.0; } if (blue < 0.0 | blue > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be in the range from 0.0 to 1.0 in specularColor in Material in MeshMaterialList in Mesh in x object file " + FileName); blue = blue < 0.5 ? 0.0 : 1.0; } Materials[MaterialIndex].specularColor = new Color24((byte)Math.Round(255.0 * red), (byte)Math.Round(255.0 * green), (byte)Math.Round(255.0 * blue)); // collect emissive color if (emissiveColor.Name != "Color24") { Interface.AddMessage(Interface.MessageType.Error, false, "emissiveColor is expected to be a Color32 in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (emissiveColor.Data.Length != 3) { Interface.AddMessage(Interface.MessageType.Error, false, "emissiveColor is expected to have 3 arguments in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(emissiveColor.Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be a float in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(emissiveColor.Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be a float in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(emissiveColor.Data[2] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be a float in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } red = (double)emissiveColor.Data[0]; green = (double)emissiveColor.Data[1]; blue = (double)emissiveColor.Data[2]; if (red < 0.0 | red > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "red is expected to be in the range from 0.0 to 1.0 in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); red = red < 0.5 ? 0.0 : 1.0; } if (green < 0.0 | green > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "green is expected to be in the range from 0.0 to 1.0 in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); green = green < 0.5 ? 0.0 : 1.0; } if (blue < 0.0 | blue > 1.0) { Interface.AddMessage(Interface.MessageType.Error, false, "blue is expected to be in the range from 0.0 to 1.0 in emissiveColor in Material in MeshMaterialList in Mesh in x object file " + FileName); blue = blue < 0.5 ? 0.0 : 1.0; } Materials[MaterialIndex].emissiveColor = new Color24((byte)Math.Round(255.0 * red), (byte)Math.Round(255.0 * green), (byte)Math.Round(255.0 * blue)); // collect additional templates for (int l = 4; l < h.Data.Length; l++) { Structure e = h.Data[l] as Structure; if (e == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Unexpected inlined argument encountered in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } switch (e.Name) { case "TextureFilename": { // texturefilename if (e.Data.Length != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "filename is expected to have 1 argument in TextureFilename in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } else if (!(e.Data[0] is string)) { Interface.AddMessage(Interface.MessageType.Error, false, "filename is expected to be a string in TextureFilename in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } string filename = (string)e.Data[0]; if (Interface.ContainsInvalidPathChars(filename)) { Interface.AddMessage(Interface.MessageType.Error, false, "filename contains illegal characters in TextureFilename in Material in MeshMaterialList in Mesh in x object file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(FileName), filename); if (System.IO.File.Exists(File)) { Materials[MaterialIndex].TextureFilename = File; } else { Interface.AddMessage(Interface.MessageType.Error, true, "The texture file " + File + " could not be found in TextureFilename in Material in MeshMaterialList in Mesh in x object file " + FileName); } } } break; default: // unknown Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported template " + e.Name + " encountered in MeshMaterialList in Mesh in x object file " + FileName); break; } } // finish MaterialIndex++; } } if (MaterialIndex != mn + nMaterials) { Interface.AddMessage(Interface.MessageType.Error, false, "nMaterials does not match the number of Material templates encountered in Material in MeshMaterialList in Mesh in x object file " + FileName); return false; } // assign materials for (int k = 0; k < nFaceIndexes; k++) { FaceMaterials[k] = faceIndexes[k]; } if (nMaterials != 0) { for (int k = 0; k < nFaces; k++) { if (FaceMaterials[k] == -1) { FaceMaterials[k] = 0; } } } } break; case "MeshTextureCoords": { // meshtexturecoords if (g.Data.Length != 2) { Interface.AddMessage(Interface.MessageType.Error, false, "MeshTextureCoords is expected to have 2 arguments in Mesh in x object file " + FileName); return false; } else if (!(g.Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nTextureCoords is expected to be a DWORD in MeshTextureCoords in Mesh in x object file " + FileName); return false; } else if (!(g.Data[1] is Structure[])) { Interface.AddMessage(Interface.MessageType.Error, false, "textureCoords[nTextureCoords] is expected to be a Coords2d array in MeshTextureCoords in Mesh in x object file " + FileName); return false; } int nTextureCoords = (int)g.Data[0]; Structure[] textureCoords = (Structure[])g.Data[1]; if (nTextureCoords < 0 | nTextureCoords > nVertices) { Interface.AddMessage(Interface.MessageType.Error, false, "nTextureCoords does not reference valid vertices in MeshTextureCoords in Mesh in x object file " + FileName); return false; } for (int k = 0; k < nTextureCoords; k++) { if (textureCoords[k].Name != "Coords2d") { Interface.AddMessage(Interface.MessageType.Error, false, "textureCoords[" + k.ToString(Culture) + "] is expected to be a Coords2d in MeshTextureCoords in Mesh in x object file " + FileName); return false; } else if (textureCoords[k].Data.Length != 2) { Interface.AddMessage(Interface.MessageType.Error, false, "textureCoords[" + k.ToString(Culture) + "] is expected to have 2 arguments in MeshTextureCoords in Mesh in x object file " + FileName); return false; } else if (!(textureCoords[k].Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "u is expected to be a float in textureCoords[" + k.ToString(Culture) + "] in MeshTextureCoords in Mesh in x object file " + FileName); return false; } else if (!(textureCoords[k].Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "v is expected to be a float in textureCoords[" + k.ToString(Culture) + "] in MeshTextureCoords in Mesh in x object file " + FileName); return false; } double u = (double)textureCoords[k].Data[0]; double v = (double)textureCoords[k].Data[1]; Vertices[k].TextureCoordinates = new World.Vector2Df((float)u, (float)v); } } break; case "MeshNormals": { // meshnormals if (g.Data.Length != 4) { Interface.AddMessage(Interface.MessageType.Error, false, "MeshNormals is expected to have 4 arguments in Mesh in x object file " + FileName); return false; } else if (!(g.Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nNormals is expected to be a DWORD in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(g.Data[1] is Structure[])) { Interface.AddMessage(Interface.MessageType.Error, false, "normals is expected to be a Vector array in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(g.Data[2] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceNormals is expected to be a DWORD in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(g.Data[3] is Structure[])) { Interface.AddMessage(Interface.MessageType.Error, false, "faceNormals is expected to be a MeshFace array in MeshNormals in Mesh in x object file " + FileName); return false; } int nNormals = (int)g.Data[0]; if (nNormals < 0) { Interface.AddMessage(Interface.MessageType.Error, false, "nNormals is expected to be non-negative in MeshNormals in Mesh in x object file " + FileName); return false; } Structure[] normals = (Structure[])g.Data[1]; if (nNormals != normals.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nNormals does not match with the length of array normals in MeshNormals in Mesh in x object file " + FileName); return false; } int nFaceNormals = (int)g.Data[2]; if (nFaceNormals < 0 | nFaceNormals > nFaces) { Interface.AddMessage(Interface.MessageType.Error, false, "nNormals does not reference valid vertices in MeshNormals in Mesh in x object file " + FileName); return false; } Structure[] faceNormals = (Structure[])g.Data[3]; if (nFaceNormals != faceNormals.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceNormals does not match with the length of array faceNormals in MeshNormals in Mesh in x object file " + FileName); return false; } // collect normals World.Vector3Df[] Normals = new World.Vector3Df[nNormals]; for (int k = 0; k < nNormals; k++) { if (normals[k].Name != "Vector") { Interface.AddMessage(Interface.MessageType.Error, false, "normals[" + k.ToString(Culture) + "] is expected to be of template Vertex in MeshNormals in Mesh in x object file " + FileName); return false; } else if (normals[k].Data.Length != 3) { Interface.AddMessage(Interface.MessageType.Error, false, "normals[" + k.ToString(Culture) + "] is expected to have 3 arguments in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(normals[k].Data[0] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "x is expected to be a float in normals[" + k.ToString(Culture) + "] in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(normals[k].Data[1] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "y is expected to be a float in normals[" + k.ToString(Culture) + " ]in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(normals[k].Data[2] is double)) { Interface.AddMessage(Interface.MessageType.Error, false, "z is expected to be a float in normals[" + k.ToString(Culture) + "] in MeshNormals in Mesh in x object file " + FileName); return false; } double x = (double)normals[k].Data[0]; double y = (double)normals[k].Data[1]; double z = (double)normals[k].Data[2]; World.Normalize(ref x, ref y, ref z); Normals[k] = new World.Vector3Df((float)x, (float)y, (float)z); } // collect faces for (int k = 0; k < nFaceNormals; k++) { if (faceNormals[k].Name != "MeshFace") { Interface.AddMessage(Interface.MessageType.Error, false, "faceNormals[" + k.ToString(Culture) + "] is expected to be of template MeshFace in MeshNormals in Mesh in x object file " + FileName); return false; } else if (faceNormals[k].Data.Length != 2) { Interface.AddMessage(Interface.MessageType.Error, false, "faceNormals[" + k.ToString(Culture) + "] is expected to have 2 arguments in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(faceNormals[k].Data[0] is int)) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices is expected to be a DWORD in faceNormals[" + k.ToString(Culture) + "] in MeshNormals in Mesh in x object file " + FileName); return false; } else if (!(faceNormals[k].Data[1] is int[])) { Interface.AddMessage(Interface.MessageType.Error, false, "faceVertexIndices[nFaceVertexIndices] is expected to be a DWORD array in faceNormals[" + k.ToString(Culture) + "] in MeshNormals in Mesh in x object file " + FileName); return false; } int nFaceVertexIndices = (int)faceNormals[k].Data[0]; if (nFaceVertexIndices < 0 | nFaceVertexIndices > Faces[k].Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices does not reference a valid vertex in MeshFace in MeshNormals in Mesh in x object file " + FileName); return false; } int[] faceVertexIndices = (int[])faceNormals[k].Data[1]; if (nFaceVertexIndices != faceVertexIndices.Length) { Interface.AddMessage(Interface.MessageType.Error, false, "nFaceVertexIndices does not match with the length of array faceVertexIndices in faceNormals[" + k.ToString(Culture) + "] in MeshFace in MeshNormals in Mesh in x object file " + FileName); return false; } for (int l = 0; l < nFaceVertexIndices; l++) { if (faceVertexIndices[l] < 0 | faceVertexIndices[l] >= nNormals) { Interface.AddMessage(Interface.MessageType.Error, false, "faceVertexIndices[" + l.ToString(Culture) + "] does not reference a valid normal in faceNormals[" + k.ToString(Culture) + "] in MeshFace in MeshNormals in Mesh in x object file " + FileName); return false; } FaceNormals[k][l] = Normals[faceVertexIndices[l]]; } } } break; default: // unknown Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported template " + g.Name + " encountered in Mesh in x object file " + FileName); break; } } // default material if (Materials.Length == 0) { Materials = new Material[1]; Materials[0].faceColor = new Color32(255, 255, 255, 255); Materials[0].emissiveColor = new Color24(0, 0, 0); Materials[0].specularColor = new Color24(0, 0, 0); Materials[0].TextureFilename = null; for (int j = 0; j < nFaces; j++) { FaceMaterials[j] = 0; } } // create mesh int mf = Object.Mesh.Faces.Length; int mm = Object.Mesh.Materials.Length; int mv = Object.Mesh.Vertices.Length; Array.Resize(ref Object.Mesh.Faces, mf + nFaces); Array.Resize(ref Object.Mesh.Materials, mm + Materials.Length); Array.Resize(ref Object.Mesh.Vertices, mv + Vertices.Length); for (int j = 0; j < Materials.Length; j++) { bool emissive = Materials[j].emissiveColor.R != 0 | Materials[j].emissiveColor.G != 0 | Materials[j].emissiveColor.B != 0; bool transparent; if (Materials[j].TextureFilename != null) { Textures.RegisterTexture(Materials[j].TextureFilename, new OpenBveApi.Textures.TextureParameters(null, Color24.Black), out Object.Mesh.Materials[mm + j].DaytimeTexture); transparent = true; } else { Object.Mesh.Materials[mm + j].DaytimeTexture = null; transparent = false; } Object.Mesh.Materials[mm + j].Flags = (byte)((transparent ? World.MeshMaterial.TransparentColorMask : 0) | (emissive ? World.MeshMaterial.EmissiveColorMask : 0)); Object.Mesh.Materials[mm + j].Color = Materials[j].faceColor; Object.Mesh.Materials[mm + j].TransparentColor = new Color24(0, 0, 0); Object.Mesh.Materials[mm + j].EmissiveColor = Materials[j].emissiveColor; Object.Mesh.Materials[mm + j].NighttimeTexture = null; Object.Mesh.Materials[mm + j].BlendMode = World.MeshMaterialBlendMode.Normal; Object.Mesh.Materials[mm + j].GlowAttenuationData = 0; } for (int j = 0; j < nFaces; j++) { Object.Mesh.Faces[mf + j].Material = (ushort)FaceMaterials[j]; Object.Mesh.Faces[mf + j].Vertices = new World.MeshFaceVertex[Faces[j].Length]; for (int k = 0; k < Faces[j].Length; k++) { Object.Mesh.Faces[mf + j].Vertices[mv + k] = new World.MeshFaceVertex(mv + Faces[j][k], FaceNormals[j][k]); } } for (int j = 0; j < Vertices.Length; j++) { Object.Mesh.Vertices[mv + j] = Vertices[j]; } break; } case "Header": break; default: // unknown Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported template " + f.Name + " encountered in x object file " + FileName); break; } } // return World.CreateNormals(ref Object.Mesh); return true; } } }openbve-1.4.0.10/openBVE/OpenBve/OpenBve.csproj000066400000000000000000000220551171674032100210130ustar00rootroot00000000000000 Debug AnyCPU 8.0.50727 2.0 {34743421-2EB8-4F68-9600-AEAE79AECFA5} WinExe Properties OpenBve OpenBve False false False Client False False OnBuildSuccess False False ..\icon.ico v4.0 true full false bin\Debug\ x86 TRACE;DEBUG prompt 4 None true x86 bin\Release\ TRACE prompt 4 false False False Auto 4194304 4096 False Project ..\Tao.OpenAl.dll True ..\Tao.OpenGl.dll True ..\Tao.Sdl.dll True Sounds.cs Sounds.cs Sounds.cs Sounds.cs Sounds.cs Renderer.cs Renderer.cs Renderer.cs Textures.cs Textures.cs Form formImage.cs formMain.cs Form formMain.cs formMain.cs formMain.cs formMain.cs formMain.cs PluginManager.cs PluginManager.cs ManagedContent.cs ManagedContent.cs ManagedContent.cs ManagedContent.cs Designer formImage.cs Designer formMain.cs {27134980-4415-4375-A564-40A9014DFA5F} OpenBveApi openbve-1.4.0.10/openBVE/OpenBve/OpenBve.sln000066400000000000000000000016061171674032100203060ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 # SharpDevelop 3.1.0.4977 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBve", "OpenBve.csproj", "{34743421-2EB8-4F68-9600-AEAE79AECFA5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Debug|Any CPU.Build.0 = Debug|Any CPU {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Release|Any CPU.Build.0 = Release|Any CPU {34743421-2EB8-4F68-9600-AEAE79AECFA5}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection EndGlobal openbve-1.4.0.10/openBVE/OpenBve/Properties/000077500000000000000000000000001171674032100203635ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBve/Properties/AssemblyInfo.cs000066400000000000000000000012431171674032100233050ustar00rootroot00000000000000using System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: AssemblyTitle("openBVE")] [assembly: AssemblyProduct("openBVE")] [assembly: AssemblyCopyright("(Public Domain) http://trainsimframework.org/")] [assembly: ComVisible(false)] [assembly: AssemblyVersion("1.4.0.10")] [assembly: AssemblyFileVersion("1.4.0.10")] [assembly: CLSCompliant(true)] namespace OpenBve { internal static partial class Program { /// Whether this is a development version. Affects the main menu design and the version checking. internal const bool IsDevelopmentVersion = false; } }openbve-1.4.0.10/openBVE/OpenBve/Properties/Resources.Designer.cs000066400000000000000000000054311171674032100244260ustar00rootroot00000000000000//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:2.0.50727.1433 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace OpenBve.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OpenBve.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } } } openbve-1.4.0.10/openBVE/OpenBve/Properties/Resources.resources000066400000000000000000000002641171674032100242730ustar00rootroot00000000000000lSystem.Resources.ResourceReader, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceSetPADPADPopenbve-1.4.0.10/openBVE/OpenBve/Properties/Resources.resx000066400000000000000000000135051171674032100232440ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 openbve-1.4.0.10/openBVE/OpenBve/Properties/Settings.Designer.cs000066400000000000000000000021021171674032100242440ustar00rootroot00000000000000//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:2.0.50727.1433 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace OpenBve.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); internal static Settings Default { get { return defaultInstance; } } } } openbve-1.4.0.10/openBVE/OpenBve/Properties/Settings.settings000066400000000000000000000003711171674032100237460ustar00rootroot00000000000000 openbve-1.4.0.10/openBVE/OpenBve/System/000077500000000000000000000000001171674032100175135ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBve/System/FileSystem.cs000066400000000000000000000163741171674032100221410ustar00rootroot00000000000000using System; using System.IO; using System.Reflection; using System.Text; namespace OpenBve { /// Represents the program's organization of files and folders. internal class FileSystem { // --- members --- /// The location of the application data, including, among others, Compatibility, Flags and Languages. internal string DataFolder; /// The locations of managed content. internal string[] ManagedContentFolders; /// The location where to save user settings, including settings.cfg and controls.cfg. internal string SettingsFolder; /// The initial location of the Railway/Route folder. internal string InitialRouteFolder; /// The initial location of the Train folder. internal string InitialTrainFolder; /// The location of the process to execute on restarting the program. internal string RestartProcess; /// The arguments to supply to the process on restarting the program. internal string RestartArguments; // --- constructors --- /// Creates a new instance of this class with default locations. internal FileSystem() { string assemblyFile = Assembly.GetExecutingAssembly().Location; string assemblyFolder = Path.GetDirectoryName(assemblyFile); string userDataFolder = OpenBveApi.Path.CombineDirectory(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "openBVE"); this.DataFolder = OpenBveApi.Path.CombineDirectory(assemblyFolder, "Data"); this.ManagedContentFolders = new string[] { OpenBveApi.Path.CombineDirectory(userDataFolder, "ManagedContent") }; this.SettingsFolder = OpenBveApi.Path.CombineDirectory(userDataFolder, "Settings"); this.InitialRouteFolder = OpenBveApi.Path.CombineDirectory(OpenBveApi.Path.CombineDirectory(OpenBveApi.Path.CombineDirectory(userDataFolder, "LegacyContent"), "Railway"), "Route"); this.InitialTrainFolder = OpenBveApi.Path.CombineDirectory(OpenBveApi.Path.CombineDirectory(userDataFolder, "LegacyContent"), "Train"); this.RestartProcess = assemblyFile; this.RestartArguments = string.Empty; } // --- internal functions --- /// Creates the file system information from the command line arguments. If no configuration file is specified in the command line arguments, the default lookup location is used. If no configuration file is found, default values are used. /// The command line arguments. /// The file system information. internal static FileSystem FromCommandLineArgs(string[] args) { foreach (string arg in args) { if (arg.StartsWith("/filesystem=", StringComparison.OrdinalIgnoreCase)) { return FromConfigurationFile(arg.Substring(12)); } } string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string configFile = OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(OpenBveApi.Path.CombineDirectory(assemblyFolder, "UserData"), "Settings"), "filesystem.cfg"); if (File.Exists(configFile)) { return FromConfigurationFile(configFile); } else { return new FileSystem(); } } /// Creates all folders in the file system that can later be written to. internal void CreateFileSystem() { try { Directory.CreateDirectory(this.SettingsFolder); } catch { } foreach (string folder in this.ManagedContentFolders) { try { Directory.CreateDirectory(folder); } catch { } } try { Directory.CreateDirectory(this.InitialRouteFolder); } catch { } try { Directory.CreateDirectory(this.InitialTrainFolder); } catch { } } /// Gets the data folder or any specified subfolder thereof. /// The subfolders. /// The data folder or a subfolder thereof. internal string GetDataFolder(params string[] subfolders) { string folder = this.DataFolder; foreach (string subfolder in subfolders) { folder = OpenBveApi.Path.CombineDirectory(folder, subfolder); } return folder; } // --- private functions --- /// Creates the file system information from the specified configuration file. /// The configuration file describing the file system. /// The file system. private static FileSystem FromConfigurationFile(string file) { FileSystem system = new FileSystem(); try { string[] lines = File.ReadAllLines(file, Encoding.UTF8); foreach (string line in lines) { int equals = line.IndexOf('='); if (equals >= 0) { string key = line.Substring(0, equals).Trim().ToLowerInvariant(); string value = line.Substring(equals + 1).Trim(); switch (key) { case "data": system.DataFolder = GetAbsolutePath(value, true); break; case "managedcontent": system.ManagedContentFolders = value.Split(','); for (int i = 0; i < system.ManagedContentFolders.Length; i++) { system.ManagedContentFolders[i] = GetAbsolutePath(system.ManagedContentFolders[i].Trim(), true); } break; case "settings": system.SettingsFolder = GetAbsolutePath(value, true); break; case "initialroute": system.InitialRouteFolder = GetAbsolutePath(value, true); break; case "initialtrain": system.InitialTrainFolder = GetAbsolutePath(value, true); break; case "restartprocess": system.RestartProcess = GetAbsolutePath(value, true); break; case "restartarguments": system.RestartArguments = GetAbsolutePath(value, false); break; } } } } catch { } return system; } /// Gets the absolute path from the specified folder. /// The folder which may contain special representations of system folders. /// Checks if the resulting path is an absolute path. /// The absolute path. private static string GetAbsolutePath(string folder, bool checkIfRooted) { string originalFolder = folder; if (checkIfRooted) { folder = folder.Replace('/', Path.DirectorySeparatorChar); folder = folder.Replace('\\', Path.DirectorySeparatorChar); } folder = folder.Replace("$[AssemblyFile]", Assembly.GetExecutingAssembly().Location); folder = folder.Replace("$[AssemblyFolder]", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); folder = folder.Replace("$[ApplicationData]", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); folder = folder.Replace("$[CommonApplicationData]", Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); folder = folder.Replace("$[Personal]", Environment.GetFolderPath(Environment.SpecialFolder.Personal)); if (checkIfRooted && !Path.IsPathRooted(folder)) { throw new InvalidDataException("The folder " + originalFolder + " does not produce an absolute path."); } return folder; } } }openbve-1.4.0.10/openBVE/OpenBve/System/Gzip.cs000066400000000000000000000033051171674032100207540ustar00rootroot00000000000000using System; using System.IO; using System.IO.Compression; namespace OpenBve { internal static class Gzip { /// Takes the argument and returns the gzip-compressed equivalent. /// The data to compress. /// The compressed data. internal static byte[] Compress(byte[] data) { byte[] target; using (MemoryStream outputStream = new MemoryStream()) { using (GZipStream gZipStream = new GZipStream(outputStream, CompressionMode.Compress, true)) { gZipStream.Write(data, 0, data.Length); } target = new byte[outputStream.Length]; outputStream.Position = 0; outputStream.Read(target, 0, target.Length); } return target; } /// Takes the gzip-compressed argument and returns the uncompressed equivalent. /// The data to decompress. /// The uncompressed data. internal static byte[] Decompress(byte[] data) { byte[] target; using (MemoryStream inputStream = new MemoryStream(data)) { using (GZipStream gZipStream = new GZipStream(inputStream, CompressionMode.Decompress, true)) { using (MemoryStream outputStream = new MemoryStream()) { byte[] buffer = new byte[4096]; while (true) { int count = gZipStream.Read(buffer, 0, buffer.Length); if (count != 0) { outputStream.Write(buffer, 0, count); } if (count != buffer.Length) { break; } } target = new byte[outputStream.Length]; outputStream.Position = 0; outputStream.Read(target, 0, target.Length); } } } return target; } } }openbve-1.4.0.10/openBVE/OpenBve/System/Host.cs000066400000000000000000000224001171674032100207550ustar00rootroot00000000000000using System; namespace OpenBve { /// Represents the host application. internal class Host : OpenBveApi.Hosts.HostInterface { // --- functions --- /// Reports a problem to the host application. /// The type of problem that is reported. /// The textual message that describes the problem. public override void ReportProblem(OpenBveApi.Hosts.ProblemType type, string text) { Interface.AddMessage(Interface.MessageType.Error, false, text); } // --- texture --- /// Queries the dimensions of a texture. /// The path to the file or folder that contains the texture. /// Receives the width of the texture. /// Receives the height of the texture. /// Whether querying the dimensions was successful. public override bool QueryTextureDimensions(string path, out int width, out int height) { if (System.IO.File.Exists(path) || System.IO.Directory.Exists(path)) { for (int i = 0; i < Plugins.LoadedPlugins.Length; i++) { if (Plugins.LoadedPlugins[i].Texture != null) { try { if (Plugins.LoadedPlugins[i].Texture.CanLoadTexture(path)) { try { if (Plugins.LoadedPlugins[i].Texture.QueryTextureDimensions(path, out width, out height)) { return true; } Interface.AddMessage(Interface.MessageType.Error, false, "Plugin " + Plugins.LoadedPlugins[i].Title + " returned unsuccessfully at QueryTextureDimensions" ); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "Plugin " + Plugins.LoadedPlugins[i].Title + " raised the following exception at QueryTextureDimensions:" + ex.Message ); } } } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "Plugin " + Plugins.LoadedPlugins[i].Title + " raised the following exception at CanLoadTexture:" + ex.Message ); } } } Interface.AddMessage(Interface.MessageType.Error, false, "No plugin found that is capable of loading texture " + path ); } else { ReportProblem(OpenBveApi.Hosts.ProblemType.PathNotFound, path.ToString()); } width = 0; height = 0; return false; } /// Loads a texture and returns the texture data. /// The path to the file or folder that contains the texture. /// The parameters that specify how to process the texture. /// Receives the texture. /// Whether loading the texture was successful. public override bool LoadTexture(string path, OpenBveApi.Textures.TextureParameters parameters, out OpenBveApi.Textures.Texture texture) { if (System.IO.File.Exists(path) || System.IO.Directory.Exists(path)) { for (int i = 0; i < Plugins.LoadedPlugins.Length; i++) { if (Plugins.LoadedPlugins[i].Texture != null) { try { if (Plugins.LoadedPlugins[i].Texture.CanLoadTexture(path)) { try { if (Plugins.LoadedPlugins[i].Texture.LoadTexture(path, out texture)) { texture = texture.ApplyParameters(parameters); return true; } Interface.AddMessage(Interface.MessageType.Error, false, "Plugin " + Plugins.LoadedPlugins[i].Title + " returned unsuccessfully at LoadTexture" ); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "Plugin " + Plugins.LoadedPlugins[i].Title + " raised the following exception at LoadTexture:" + ex.Message ); } } } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "Plugin " + Plugins.LoadedPlugins[i].Title + " raised the following exception at CanLoadTexture:" + ex.Message ); } } } Interface.AddMessage(Interface.MessageType.Error, false, "No plugin found that is capable of loading texture " + path.ToString() ); } else { ReportProblem(OpenBveApi.Hosts.ProblemType.PathNotFound, path.ToString()); } texture = null; return false; } /// Registers a texture and returns a handle to the texture. /// The path to the file or folder that contains the texture. /// The parameters that specify how to process the texture. /// Receives the handle to the texture. /// Whether loading the texture was successful. public override bool RegisterTexture(string path, OpenBveApi.Textures.TextureParameters parameters, out OpenBveApi.Textures.TextureHandle handle) { if (System.IO.File.Exists(path) || System.IO.Directory.Exists(path)) { Textures.Texture data; if (Textures.RegisterTexture(path, parameters, out data)) { handle = data; return true; } } else { ReportProblem(OpenBveApi.Hosts.ProblemType.PathNotFound, path.ToString()); } handle = null; return false; } /// Registers a texture and returns a handle to the texture. /// The texture data. /// The parameters that specify how to process the texture. /// Receives the handle to the texture. /// Whether loading the texture was successful. public override bool RegisterTexture(OpenBveApi.Textures.Texture texture, OpenBveApi.Textures.TextureParameters parameters, out OpenBveApi.Textures.TextureHandle handle) { texture = texture.ApplyParameters(parameters); handle = Textures.RegisterTexture(texture); return true; } // --- sound --- /// Loads a sound and returns the sound data. /// The path to the file or folder that contains the sound. /// Receives the sound. /// Whether loading the sound was successful. public override bool LoadSound(string path, out OpenBveApi.Sounds.Sound sound) { if (System.IO.File.Exists(path) || System.IO.Directory.Exists(path)) { for (int i = 0; i < Plugins.LoadedPlugins.Length; i++) { if (Plugins.LoadedPlugins[i].Sound != null) { try { if (Plugins.LoadedPlugins[i].Sound.CanLoadSound(path)) { try { if (Plugins.LoadedPlugins[i].Sound.LoadSound(path, out sound)) { return true; } Interface.AddMessage(Interface.MessageType.Error, false, "Plugin " + Plugins.LoadedPlugins[i].Title + " returned unsuccessfully at LoadSound" ); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "Plugin " + Plugins.LoadedPlugins[i].Title + " raised the following exception at LoadSound:" + ex.Message ); } } } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "Plugin " + Plugins.LoadedPlugins[i].Title + " raised the following exception at CanLoadSound:" + ex.Message ); } } } Interface.AddMessage(Interface.MessageType.Error, false, "No plugin found that is capable of loading sound " + path.ToString() ); } else { ReportProblem(OpenBveApi.Hosts.ProblemType.PathNotFound, path.ToString()); } sound = null; return false; } /// Registers a sound and returns a handle to the sound. /// The path to the file or folder that contains the sound. /// Receives a handle to the sound. /// Whether loading the sound was successful. public override bool RegisterSound(string path, out OpenBveApi.Sounds.SoundHandle handle) { if (System.IO.File.Exists(path) || System.IO.Directory.Exists(path)) { Sounds.SoundBuffer data; data = Sounds.RegisterBuffer(path, 0.0); // TODO } else { ReportProblem(OpenBveApi.Hosts.ProblemType.PathNotFound, path.ToString()); } handle = null; return false; } /// Registers a sound and returns a handle to the sound. /// The sound data. /// Receives a handle to the sound. /// Whether loading the sound was successful. public override bool RegisterSound(OpenBveApi.Sounds.Sound sound, out OpenBveApi.Sounds.SoundHandle handle) { handle = Sounds.RegisterBuffer(sound, 0.0); // TODO return true; } } }openbve-1.4.0.10/openBVE/OpenBve/System/Internet.cs000066400000000000000000000100561171674032100216340ustar00rootroot00000000000000using System; using System.IO; using System.Net; using System.Threading; namespace OpenBve { /// Provides methods for accessing the internet. internal static class Internet { /// Adds some user agent and referer to the web client headers. /// The web client. /// The URL to be accessed. private static void AddWebClientHeaders(WebClient client, string url) { const string agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0"; try { client.Headers.Add(HttpRequestHeader.UserAgent, agent); } catch { } if (url.StartsWith("http://")) { int index = url.IndexOf('/', 7); if (index >= 7) { string referer = url.Substring(0, index + 1); try { client.Headers.Add(HttpRequestHeader.Referer, referer); } catch { } } } } /// Downloads data from the specified URL. /// The URL. /// The data. internal static byte[] DownloadBytesFromUrl(string url) { byte[] bytes; using (WebClient client = new WebClient()) { AddWebClientHeaders(client, url); bytes = client.DownloadData(url); } return bytes; } /// Downloads data from the specified URL. /// The URL. /// Receives the data. /// Accumulates the size of the downloaded data in an interlocked operation. If the operation fails, all accumulated size is subtracted again. /// Whether the operation was successful. internal static bool TryDownloadBytesFromUrl(string url, out byte[] bytes, ref int size) { int count = 0; try { using (WebClient client = new WebClient()) { AddWebClientHeaders(client, url); using (Stream stream = client.OpenRead(url)) { const int chunkSize = 65536; bytes = new byte[chunkSize]; while (true) { if (count + chunkSize >= bytes.Length) { Array.Resize(ref bytes, bytes.Length << 1); } int read = stream.Read(bytes, count, chunkSize); if (read != 0) { count += read; Interlocked.Add(ref size, read); } else { break; } } } } Array.Resize(ref bytes, count); return true; } catch { Interlocked.Add(ref size, -count); bytes = null; return false; } } /// Downloads bytes from the specified URL and saves them to a file. /// The URL. /// The file name. /// If the file already exists and was modified during the last so and so days, the download will be bypassed. /// The function to execute once the data has been saved to the file, or a null reference. The argument in the callback function is of type System.String and contains the file name. internal static void DownloadAndSaveAsynchronous(string url, string file, double days, ParameterizedThreadStart callback) { bool download; if (File.Exists(file)) { try { DateTime lastWrite = File.GetLastWriteTime(file); TimeSpan span = DateTime.Now - lastWrite; download = span.TotalDays > days; } catch { download = true; } } else { download = true; } if (download) { ThreadStart start = new ThreadStart( () => { try { byte[] bytes = DownloadBytesFromUrl(url); string directory = Path.GetDirectoryName(file); try { Directory.CreateDirectory(directory); File.WriteAllBytes(file, bytes); } catch { } if (callback != null) { callback.Invoke(file); } } catch { } } ); Thread thread = new Thread(start); thread.IsBackground = true; thread.Start(); } else if (callback != null) { callback.Invoke(file); } } } }openbve-1.4.0.10/openBVE/OpenBve/System/Joysticks.cs000066400000000000000000000052571171674032100220350ustar00rootroot00000000000000using System; using Tao.Sdl; namespace OpenBve { /// Provides functions for dealing with joysticks. internal static class Joysticks { // --- structures --- /// Represents a joystick. internal struct Joystick { // --- members --- /// The textual representation of the joystick. internal string Name; /// The SDL handle to the joystick. internal IntPtr SdlHandle; // --- constructors --- /// Creates a new joystick. /// The textual representation of the joystick. /// The SDL handle to the joystick. internal Joystick(string name, IntPtr sdlHandle) { this.Name = name; this.SdlHandle = sdlHandle; } } // --- members --- /// Whether joysticks are initialized. private static bool Initialized = false; /// Holds all joysticks currently attached to the computer. internal static Joystick[] AttachedJoysticks = new Joystick[] { }; // --- functions --- /// Initializes joysticks. A call to SDL_Init must have been made before calling this function. A call to Deinitialize must be made when terminating the program. /// Whether initializing joysticks was successful. internal static bool Initialize() { if (!Initialized) { if (Sdl.SDL_Init(Sdl.SDL_INIT_JOYSTICK) != 0) { return false; } else { int count = Sdl.SDL_NumJoysticks(); AttachedJoysticks = new Joystick[count]; for (int i = 0; i < count; i++) { string name = Sdl.SDL_JoystickName(i); /* Due to an apparent bug in Tao or SDL, the joystick * name returned is actually ASCII packed in UTF-16. */ char[] characters = new char[2 * name.Length]; for (int j = 0; j < name.Length; j++) { int value = (int)name[j]; characters[2 * j + 0] = (char)(value & 0xFF); characters[2 * j + 1] = (char)(value >> 8); } AttachedJoysticks[i].Name = new string(characters); AttachedJoysticks[i].SdlHandle = Sdl.SDL_JoystickOpen(i); } Initialized = true; return true; } } else { return true; } } /// Deinitializes joysticks. internal static void Deinitialize() { if (Initialized) { for (int i = 0; i < AttachedJoysticks.Length; i++) { Sdl.SDL_JoystickClose(AttachedJoysticks[i].SdlHandle); } AttachedJoysticks = new Joystick[] { }; Sdl.SDL_QuitSubSystem(Sdl.SDL_INIT_JOYSTICK); Initialized = false; } } } }openbve-1.4.0.10/openBVE/OpenBve/System/ManagedContent.Functions.cs000066400000000000000000000075741171674032100247150ustar00rootroot00000000000000using System; using System.IO; using System.Text; namespace OpenBve { internal static partial class ManagedContent { // --- static functions --- /// Gets the directory of the specified package if installed. /// The package name. /// The directory of the specified package, or a null reference if not installed. /// If multiple instances of the package exists, this function returns the first one it encounters. internal static string GetInstalledPackageDirectory(string package) { for (int i = 0; i < Program.FileSystem.ManagedContentFolders.Length; i++) { string directory = OpenBveApi.Path.CombineDirectory(Program.FileSystem.ManagedContentFolders[i], package); if (Directory.Exists(directory)) { return directory; } } return null; } /// Gets the currently installed version of the specified package. /// The package name. /// The currently installed version of the specified package, or a null reference if the package is not installed. internal static string GetInstalledPackageVersion(string package) { foreach (string lookupDirectory in Program.FileSystem.ManagedContentFolders) { string packageDirectory = OpenBveApi.Path.CombineDirectory(lookupDirectory, package); if (Directory.Exists(packageDirectory)) { string file = OpenBveApi.Path.CombineFile(packageDirectory, "package.cfg"); if (File.Exists(file)) { string[] lines = File.ReadAllLines(file, Encoding.UTF8); for (int j = 0; j < lines.Length; j++) { lines[j] = lines[j].Trim(); int equals = lines[j].IndexOf('='); if (equals >= 0) { string key = lines[j].Substring(0, equals).TrimEnd(); if (key.Equals("version", StringComparison.OrdinalIgnoreCase)) { string value = lines[j].Substring(equals + 1).TrimStart(); return value; } } } } return "0.0"; } } return null; } /// Checks whether the specified package is installed and protected from modification or deletion. /// The package name. /// Whether the specified package is protected. internal static bool IsInstalledPackageProtected(string package) { foreach (string lookupDirectory in Program.FileSystem.ManagedContentFolders) { string packageDirectory = OpenBveApi.Path.CombineDirectory(lookupDirectory, package); if (Directory.Exists(packageDirectory)) { string file = OpenBveApi.Path.CombineFile(packageDirectory, "package.cfg"); if (File.Exists(file)) { string[] lines = File.ReadAllLines(file, Encoding.UTF8); for (int j = 0; j < lines.Length; j++) { lines[j] = lines[j].Trim(); int equals = lines[j].IndexOf('='); if (equals >= 0) { string key = lines[j].Substring(0, equals).TrimEnd(); if (key.Equals("protected", StringComparison.OrdinalIgnoreCase)) { string value = lines[j].Substring(equals + 1).TrimStart(); if (value.Equals("true", StringComparison.OrdinalIgnoreCase)) { return true; } } } } } } } return false; } /// Removes the specified directory if it exists. /// The path to the directory. private static void RemoveDirectory(string path) { if (Directory.Exists(path)) { Directory.Delete(path, true); } } /// Creates the specified directory if it does not already exist. /// The path to the directory. private static void CreateDirectory(string path) { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } } } }openbve-1.4.0.10/openBVE/OpenBve/System/ManagedContent.Install.cs000066400000000000000000000175311171674032100243450ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Text; using System.Threading; namespace OpenBve { internal static partial class ManagedContent { internal partial class Database { /// Gets all suggestions for the specified list of packages. /// The list of packages. /// The list of suggestions internal List GetSuggestions(List packages) { List results = new List(); foreach (Version package in packages) { foreach (Dependency suggestion in package.Suggestions) { if (!packages.Exists((Version item) => { return item.Name.Equals(suggestion.Name, StringComparison.OrdinalIgnoreCase); })) { Dependency find = results.Find((Dependency item) => { return item.Name.Equals(suggestion.Name, StringComparison.OrdinalIgnoreCase); }); if (find.Name != null) { if (CompareVersions(suggestion.Version, find.Version) > 0) { find.Version = suggestion.Version; } } else { string installedVersion = GetInstalledPackageVersion(suggestion.Name); if (installedVersion == null || CompareVersions(suggestion.Version, installedVersion) > 0) { results.Add(suggestion); } } } } } return results; } /// Takes a list of packages (which are to be installed) and adds their dependencies to the list. /// The list of packages. /// Returns false if not all dependencies were found. internal bool AddDependencies(List packages) { /* * Add dependencies. * */ for (int i = 0; i < packages.Count; i++) { foreach (Dependency dependency in packages[i].Dependencies) { Version find = packages.Find((Version item) => { return item.Name.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase); }); if (find != null) { if (CompareVersions(dependency.Version, find.Number) > 0) { Version version = Dereference(dependency.Name, dependency.Version); if (version != null) { packages[i] = version; i--; } else { return false; } } } else { Version version = Dereference(dependency.Name, dependency.Version); if (version != null) { packages.Add(version); } else { return false; } } } } /* * If the packages to be installed are already installed, * remove them from the list. * */ for (int i = 0; i < packages.Count; i++) { string version = GetInstalledPackageVersion(packages[i].Name); if (version != null) { if (CompareVersions(packages[i].Number, version) <= 0) { packages[i] = packages[packages.Count - 1]; packages.RemoveAt(packages.Count - 1); i--; } } } return true; } /// Installs the specified package, but not its dependencies. /// The specific version of the package. /// Accumulates the size of the downloaded data in an interlocked operation. If the operation fails, all accumulated size is subtracted again. /// Whether the package was successfully installed. internal bool InstallPackage(Version version, ref int size) { try { /* * Check whether the package is protected. * */ if (IsInstalledPackageProtected(version.Name)) { return false; } /* * Remove existing versions of this package. */ string packageDirectory = GetInstalledPackageDirectory(version.Name); if (packageDirectory != null) { RemoveDirectory(packageDirectory); } else { packageDirectory = Path.Combine(Program.FileSystem.ManagedContentFolders[0], version.Name); } /* * Install the package. * */ Source[] sources = version.Sources; Shuffle(sources); int attemps = sources.Length == 1 ? 3 : sources.Length == 2 ? 2 : 1; for (int a = 0; a < attemps; a++) { for (int i = 0; i < sources.Length; i++) { byte[] bytes; if (Internet.TryDownloadBytesFromUrl(sources[i].Url, out bytes, ref size)) { if (bytes.Length == sources[i].Size) { byte[] md5 = (new MD5CryptoServiceProvider()).ComputeHash(bytes); bool add = true; for (int k = 0; k < 16; k++) { if (md5[k] != sources[i].Md5[k]) { add = false; break; } } if (add) { if (bytes.Length >= 2 && bytes[0] == 0x1F && bytes[1] == 0x8B) { /* GZIP-compressed */ bytes = Gzip.Decompress(bytes); } if ((bytes.Length & 0x1FF) == 0) { /* TAR archive */ RemoveDirectory(packageDirectory); CreateDirectory(packageDirectory); Tar.Unpack(bytes, packageDirectory, version.Name); } else { add = false; } if (add) { /* * The installation was successful. Save some of * the package information for offline storage. * */ StringBuilder builder = new StringBuilder(); builder.AppendLine("name = " + version.Name); builder.AppendLine("version = " + version.Number); if (version.Sources.Length != 0) { builder.Append("sources = "); for (int j = 0; j < version.Sources.Length; j++) { if (j != 0) { builder.Append(',').Append(' '); } builder.Append(version.Sources[j].Url); } builder.AppendLine(); } if (version.Dependencies.Length != 0) { builder.Append("dependencies = "); for (int j = 0; j < version.Dependencies.Length; j++) { if (j != 0) { builder.Append(',').Append(' '); } builder.Append(version.Dependencies[j].Name); builder.Append(' ').Append('('); builder.Append(version.Dependencies[j].Version); builder.Append(')'); } builder.AppendLine(); } if (version.Suggestions.Length != 0) { builder.Append("suggestions = "); for (int j = 0; j < version.Suggestions.Length; j++) { if (j != 0) { builder.Append(',').Append(' '); } builder.Append(version.Suggestions[j].Name); builder.Append(' ').Append('('); builder.Append(version.Suggestions[j].Version); builder.Append(')'); } builder.AppendLine(); } builder.AppendLine(); foreach (KeyValuePair pair in version.Metadata) { if (!string.Equals(pair.Key, "protected", StringComparison.OrdinalIgnoreCase)) { builder.AppendLine(pair.ToString()); } } string file = Path.Combine(packageDirectory, "package.cfg"); File.WriteAllText(file, builder.ToString(), new UTF8Encoding(true)); return true; } } } Interlocked.Add(ref size, -bytes.Length); } } Thread.Sleep(1000); } return false; } catch { return false; } } internal static void Shuffle(T[] array) { Random random = Program.RandomNumberGenerator; for (int i = 0; i < array.Length - 1; i++) { int index = random.Next(i, array.Length); T temp = array[index]; array[index] = array[i]; array[i] = temp; } } } } }openbve-1.4.0.10/openBVE/OpenBve/System/ManagedContent.Remove.cs000066400000000000000000000133771171674032100242000ustar00rootroot00000000000000using System; using System.Collections.Generic; namespace OpenBve { internal static partial class ManagedContent { /// Gets all dependent and redundant items for the specified list of packages. /// The list of packages. /// The list of dependent and redundant items. /// Dependent items are packages that depend on any of the packages to be removed. /// Redundant items are libraries or shared libraries that are no longer used by any other package. internal static List GetDependentAndRedundantPackages(List packages) { List results = new List(); while (true) { bool again = false; /* * Add all dependent items. * Build a list of dependencies. * */ List dependencies = new List(); foreach (string lookupDirectory in Program.FileSystem.ManagedContentFolders) { if (System.IO.Directory.Exists(lookupDirectory)) { string[] packageDirectories = System.IO.Directory.GetDirectories(lookupDirectory); foreach (string packageDirectory in packageDirectories) { string package = System.IO.Path.GetFileName(packageDirectory); if ( !packages.Exists((string item) => { return item != null && item.Equals(package, StringComparison.OrdinalIgnoreCase); }) && !results.Exists((string item) => { return item != null && item.Equals(package, StringComparison.OrdinalIgnoreCase); }) ) { string file = OpenBveApi.Path.CombineFile(packageDirectory, "package.cfg"); if (System.IO.File.Exists(file)) { string[] lines = System.IO.File.ReadAllLines(file, System.Text.Encoding.UTF8); foreach (string line in lines) { int equals = line.IndexOf('='); if (equals >= 0) { string key = line.Substring(0, equals).Trim(); if (key.Equals("dependencies", StringComparison.OrdinalIgnoreCase)) { string[] values = line.Substring(equals + 1).Split(','); bool add = false; for (int j = 0; j < values.Length; j++) { values[j] = values[j].Trim(); if (values[j].Length != 0) { int bracket = Math.Max(values[j].IndexOf('('), values[j].IndexOf('[')); if (bracket >= 0 && (values[j][values[j].Length - 1] == ')' | values[j][values[j].Length - 1] == ']')) { values[j] = values[j].Substring(0, bracket).TrimEnd(); } if ( packages.Exists((string item) => { return item != null && item.Equals(values[j], StringComparison.OrdinalIgnoreCase); }) || results.Exists((string item) => { return item != null && item.Equals(values[j], StringComparison.OrdinalIgnoreCase); }) ) { add = true; } } } if (add) { results.Add(package); again = true; } else { dependencies.AddRange(values); } } } } } } } } } if (again) continue; /* * Add all redundant items. * */ foreach (string lookupDirectory in Program.FileSystem.ManagedContentFolders) { if (System.IO.Directory.Exists(lookupDirectory)) { string[] packageDirectories = System.IO.Directory.GetDirectories(lookupDirectory); foreach (string packageDirectory in packageDirectories) { string package = System.IO.Path.GetFileName(packageDirectory); if (!IsInstalledPackageProtected(package)) { if ( !packages.Exists((string item) => { return item != null && item.Equals(package, StringComparison.OrdinalIgnoreCase); }) && !results.Exists((string item) => { return item != null && item.Equals(package, StringComparison.OrdinalIgnoreCase); }) ) { string file = OpenBveApi.Path.CombineFile(packageDirectory, "package.cfg"); if (System.IO.File.Exists(file)) { bool add = false; string[] lines = System.IO.File.ReadAllLines(file, System.Text.Encoding.UTF8); foreach (string line in lines) { int equals = line.IndexOf('='); if (equals >= 0) { string key = line.Substring(0, equals).Trim(); if (key.Equals("type", StringComparison.OrdinalIgnoreCase)) { string value = line.Substring(equals + 1).Trim(); if (value.Equals("library", StringComparison.OrdinalIgnoreCase) | value.Equals("shared library", StringComparison.OrdinalIgnoreCase)) { add = !dependencies.Exists((string item) => { return item != null && item.Equals(package, StringComparison.OrdinalIgnoreCase); }); break; } } } } if (add) { results.Add(package); again = true; } } } } } } } if (!again) break; } return results; } /// Removes the specified package, but not dependent packages. /// The package. /// Whether removing the package was successful. internal static bool RemovePackage(string package) { try { /* * Check whether the package is protected. * */ if (IsInstalledPackageProtected(package)) { return false; } /* * Remove the package. * */ while (true) { string directory = GetInstalledPackageDirectory(package); if (directory != null) { RemoveDirectory(directory); } else { break; } } return true; } catch { return false; } } } }openbve-1.4.0.10/openBVE/OpenBve/System/ManagedContent.Versions.cs000066400000000000000000000050771171674032100245510ustar00rootroot00000000000000using System; using System.Globalization; namespace OpenBve { internal static partial class ManagedContent { /* * This module provides functions for comparing version numbers. * A version number is a string consisting of a series of decimal * digit groups, followed by an optional textual suffix. * * Examples: * 1.1 * 1.2.3 * 1.2.3alpha * 1.2.3beta * 1.2.4 * 1.3 * */ /// Compares two versions and returns the relationship between them. /// The first version. /// The second version. /// -1 if the first version is less than the second version, 0 if both versions are equal, and 1 if the first version is greater than the second version. internal static int CompareVersions(string a, string b) { if (a == b) return 0; int[] digitsA, digitsB; string suffixA, suffixB; SplitVersion(a, out digitsA, out suffixA); SplitVersion(b, out digitsB, out suffixB); int length = Math.Max(digitsA.Length, digitsB.Length); for (int i = 0; i < length; i++) { int digitA = i < digitsA.Length ? digitsA[i] : 0; int digitB = i < digitsB.Length ? digitsB[i] : 0; if (digitA < digitB) { return -1; } else if (digitA > digitB) { return 1; } } return Math.Sign(string.Compare(suffixA, suffixB, StringComparison.OrdinalIgnoreCase)); } /// Splits a version into its numeric digits and textual suffix. /// The version to split. /// Receives the numeric digits of the version. /// Receives the textual suffix of the version. private static void SplitVersion(string version, out int[] digits, out string suffix) { digits = new int[version.Length]; int digitCount = 0; int start = 0; for (int i = 0; i <= version.Length; i++) { if (i == version.Length || version[i] < 48 || version[i] > 57) { if (i == start) { break; } else { string digit = version.Substring(start, i - start); digits[digitCount] = int.Parse(digit, NumberStyles.Integer, CultureInfo.InvariantCulture); digitCount++; } if (i < version.Length && version[i] == '.') { start = i + 1; } else { start = i; break; } } } if (start < version.Length) { suffix = version.Substring(start); } else { suffix = string.Empty; } Array.Resize(ref digits, digitCount); } } }openbve-1.4.0.10/openBVE/OpenBve/System/ManagedContent.cs000066400000000000000000000330231171674032100227320ustar00rootroot00000000000000using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace OpenBve { /// Provides structures and functions for dealing with managed content. internal static partial class ManagedContent { /// Represents a source where a package can be downloaded from. internal struct Source { // --- members --- /// The size of the package. internal int Size; /// The MD5 of the package. internal byte[] Md5; /// The URL to the download. internal string Url; } /// Represents a reference to another package by name and version. internal struct Dependency { // --- members --- /// The package name. internal string Name; /// The package version. internal string Version; // --- constructors --- /// Creates a new dependency. /// The package name. /// The package version. internal Dependency(string name, string version) { this.Name = name; this.Version = version; } } /// Represents a key-value pair. internal struct KeyValuePair { // --- members --- /// The key. internal string Key; /// The language code, or a null reference. internal string Language; /// The value. internal string Value; // --- constructors --- /// Creates a new key-value pair. /// The key including the language code. /// The value internal KeyValuePair(string key, string value) { int index = key.IndexOf('['); if (index >= 0 && key.Length != 0 && key[key.Length - 1] == ']') { this.Key = key.Substring(0, index).TrimEnd(); this.Language = key.Substring(index + 1, key.Length - index - 2).Trim(); this.Value = value; } else { this.Key = key; this.Language = null; this.Value = value; } } // --- functions --- /// Gets the textual representation of this key-value pair. /// The textual representation of this key-value pair public override string ToString() { if (this.Language != null) { return this.Key + "[" + this.Language + "] = " + this.Value; } else { return this.Key + " = " + this.Value; } } } /// Represents a specific version of a package. internal class Version { // --- members --- /// The package name. internal string Name; /// The version number. internal string Number; /// The list of available sources. internal Source[] Sources; /// The list of dependencies. internal Dependency[] Dependencies; /// The list of suggestions. internal Dependency[] Suggestions; /// The list of metadata. internal KeyValuePair[] Metadata; // --- functions --- /// Gets the value associated to the specified key. /// The key without the language code. /// The preferred language code, or null if no language-specific value is expected. /// The default value in case the key is not found. /// The value. internal string GetMetadata(string key, string languageCode, string defaultValue) { return ManagedContent.GetMetadata(this.Metadata, key, languageCode, defaultValue); } /// Checks whether the specified keyword is contained in the metadata. /// The keyword. /// Whether the specified keyword is contained in the metadata. internal bool ContainsKeyword(string keyword) { for (int i = 0; i < this.Metadata.Length; i++) { if (this.Metadata[i].Value.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } } /// Represents a package. internal class Package { // --- members --- /// The package name. internal string Name; /// The list of available versions. internal Version[] Versions; } /// Represents a database of available packages. internal partial class Database { // --- members --- /// The list of available packages. internal Package[] Packages; // --- load --- /// Loads a database from a byte array. /// The byte array. /// The database. internal static Database Load(byte[] bytes) { /* * Parse the enclosing file format that holds the * compressed data and then decompress the data. * */ int version; byte[] compressed; byte[] md5; using (MemoryStream stream = new MemoryStream(bytes)) { using (BinaryReader reader = new BinaryReader(stream, Encoding.UTF8)) { if (reader.ReadUInt64() != 0x5453494C5F465354) { throw new InvalidDataException("The identifier in the header is invalid."); } version = reader.ReadInt32(); if (version != 2) { throw new InvalidDataException("The version number in the header is invalid."); } md5 = reader.ReadBytes(16); int length = reader.ReadInt32(); compressed = reader.ReadBytes(length); if (reader.ReadUInt32() != 0x444E455F) { throw new InvalidDataException("The identifier in the footer is invalid."); } } } byte[] check = (new MD5CryptoServiceProvider()).ComputeHash(compressed); for (int i = 0; i < 16; i++) { if (md5[i] != check[i]) { throw new InvalidDataException("The MD5 does not match."); } } byte[] decompressed = Gzip.Decompress(compressed); /* * Parse the raw file format and extract the database. * */ Database database = new Database(); using (MemoryStream stream = new MemoryStream(decompressed)) { using (BinaryReader reader = new BinaryReader(stream)) { if (reader.ReadUInt32() != 0x74727473) { throw new InvalidDataException("The uncompressed stream is invalid."); } database.Packages = new Package[reader.ReadInt32()]; for (int i = 0; i < database.Packages.Length; i++) { database.Packages[i] = new Package(); database.Packages[i].Name = reader.ReadString(); database.Packages[i].Versions = new Version[reader.ReadInt32()]; for (int j = 0; j < database.Packages[i].Versions.Length; j++) { database.Packages[i].Versions[j] = new Version(); database.Packages[i].Versions[j].Name = database.Packages[i].Name; database.Packages[i].Versions[j].Number = reader.ReadString(); database.Packages[i].Versions[j].Sources = new Source[reader.ReadInt32()]; for (int k = 0; k < database.Packages[i].Versions[j].Sources.Length; k++) { database.Packages[i].Versions[j].Sources[k].Size = reader.ReadInt32(); database.Packages[i].Versions[j].Sources[k].Md5 = reader.ReadBytes(16); database.Packages[i].Versions[j].Sources[k].Url = reader.ReadString(); } database.Packages[i].Versions[j].Dependencies = new Dependency[reader.ReadInt32()]; for (int k = 0; k < database.Packages[i].Versions[j].Dependencies.Length; k++) { database.Packages[i].Versions[j].Dependencies[k] = new Dependency(); database.Packages[i].Versions[j].Dependencies[k].Name = reader.ReadString(); database.Packages[i].Versions[j].Dependencies[k].Version = reader.ReadString(); } database.Packages[i].Versions[j].Suggestions = new Dependency[reader.ReadInt32()]; for (int k = 0; k < database.Packages[i].Versions[j].Suggestions.Length; k++) { database.Packages[i].Versions[j].Suggestions[k] = new Dependency(); database.Packages[i].Versions[j].Suggestions[k].Name = reader.ReadString(); database.Packages[i].Versions[j].Suggestions[k].Version = reader.ReadString(); } database.Packages[i].Versions[j].Metadata = new KeyValuePair[reader.ReadInt32()]; for (int k = 0; k < database.Packages[i].Versions[j].Metadata.Length; k++) { string key = reader.ReadString(); string value = reader.ReadString(); database.Packages[i].Versions[j].Metadata[k] = new KeyValuePair(key, value); } } } if (reader.ReadUInt32() != 0x646E655F) { throw new InvalidDataException("The uncompressed stream is invalid."); } } } return database; } // --- dereference --- /// Dereferences the specified package by name. /// The package name. /// The package, or a null reference if not found. internal Package Dereference(string name) { for (int i = 0; i < this.Packages.Length; i++) { if (string.Equals(this.Packages[i].Name, name, StringComparison.OrdinalIgnoreCase)) { return this.Packages[i]; } } return null; } /// Dereferences the specified package by name and version. /// The package name. /// The version number. /// The latest version of the package if the specified version number or higher is found - a null reference otherwise. internal Version Dereference(string name, string version) { for (int i = 0; i < this.Packages.Length; i++) { if (string.Equals(this.Packages[i].Name, name, StringComparison.OrdinalIgnoreCase)) { Version result = null; for (int j = 0; j < this.Packages[i].Versions.Length; j++) { if (result == null) { int delta = CompareVersions(this.Packages[i].Versions[j].Number, version); if (delta >= 0) { result = this.Packages[i].Versions[j]; } } else { int delta = CompareVersions(this.Packages[i].Versions[j].Number, result.Number); if (delta > 0) { result = this.Packages[i].Versions[j]; } } } return result; } } return null; } } // --- static functions --- /// Gets the value associated to the specified key. /// The list of key-value pairs. /// The key without the language code. /// The preferred language code, or null if no language-specific value is expected. /// The default value in case the key is not found. /// The value. internal static string GetMetadata(KeyValuePair[] pairs, string key, string preferredLanguage, string defaultValue) { if (preferredLanguage == null) { /* No language code is expected. */ for (int i = 0; i < pairs.Length; i++) { if (string.Equals(pairs[i].Key, key, StringComparison.OrdinalIgnoreCase)) { return pairs[i].Value; } } } else { /* A language code is expected. Let's first search for the exact language. */ for (int i = 0; i < pairs.Length; i++) { if (string.Equals(pairs[i].Key, key, StringComparison.OrdinalIgnoreCase)) { if (pairs[i].Language != null) { if (string.Equals(pairs[i].Language, preferredLanguage, StringComparison.OrdinalIgnoreCase)) { return pairs[i].Value; } } } } /* Let's search for the same language family. */ int index = preferredLanguage.IndexOf('-'); string preferredFamily = index >= 0 ? preferredLanguage.Substring(0, index) : preferredLanguage; for (int i = 0; i < pairs.Length; i++) { if (string.Equals(pairs[i].Key, key, StringComparison.OrdinalIgnoreCase)) { if (pairs[i].Language != null) { index = pairs[i].Language.IndexOf('-'); string family = index >= 0 ? pairs[i].Language.Substring(0, index) : pairs[i].Language; if (string.Equals(family, preferredFamily, StringComparison.OrdinalIgnoreCase)) { return pairs[i].Value; } } } } /* Let's search for the American English language. */ for (int i = 0; i < pairs.Length; i++) { if (string.Equals(pairs[i].Key, key, StringComparison.OrdinalIgnoreCase)) { if (pairs[i].Language != null) { if (string.Equals(pairs[i].Language, "en-US", StringComparison.OrdinalIgnoreCase)) { return pairs[i].Value; } } } } /* Let's search for any English language. */ for (int i = 0; i < pairs.Length; i++) { if (string.Equals(pairs[i].Key, key, StringComparison.OrdinalIgnoreCase)) { if (pairs[i].Language != null) { index = pairs[i].Language.IndexOf('-'); string family = index >= 0 ? pairs[i].Language.Substring(0, index) : pairs[i].Language; if (string.Equals(family, "en", StringComparison.OrdinalIgnoreCase)) { return pairs[i].Value; } } } } /* Let's return any language. */ for (int i = 0; i < pairs.Length; i++) { if (string.Equals(pairs[i].Key, key, StringComparison.OrdinalIgnoreCase)) { return pairs[i].Value; } } } return defaultValue; } } }openbve-1.4.0.10/openBVE/OpenBve/System/Plugins.cs000066400000000000000000000116101171674032100214620ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using System.Windows.Forms; namespace OpenBve { /// Represents plugins loaded by the program. internal static class Plugins { // --- classes --- /// Represents a plugin. internal class Plugin { // --- members --- /// The plugin file. internal string File; /// The plugin title. internal string Title; /// The interface to load textures as exposed by the plugin, or a null reference. internal OpenBveApi.Textures.TextureInterface Texture; /// The interface to load sounds as exposed by the plugin, or a null reference. internal OpenBveApi.Sounds.SoundInterface Sound; /// The interface to load objects as exposed by the plugin, or a null reference. internal OpenBveApi.Objects.ObjectInterface Object; // --- constructors --- /// Creates a new instance of this class. /// The plugin file. internal Plugin(string file) { this.File = file; this.Title = Path.GetFileName(file); this.Texture = null; this.Sound = null; this.Object = null; } // --- functions --- /// Loads all interfaces this plugin supports. internal void Load() { if (this.Texture != null) { this.Texture.Load(Program.CurrentHost); } if (this.Sound != null) { this.Sound.Load(Program.CurrentHost); } if (this.Object != null) { this.Object.Load(Program.CurrentHost); } } /// Unloads all interfaces this plugin supports. internal void Unload() { if (this.Texture != null) { this.Texture.Unload(); } if (this.Sound != null) { this.Sound.Unload(); } if (this.Object != null) { this.Object.Unload(); } } } // --- members --- /// A list of all non-runtime plugins that are currently loaded, or a null reference. internal static Plugin[] LoadedPlugins = null; // --- functions --- /// Loads all non-runtime plugins. /// Whether loading all plugins was successful. internal static bool LoadPlugins() { UnloadPlugins(); string folder = Program.FileSystem.GetDataFolder("Plugins"); string[] files = Directory.GetFiles(folder); List list = new List(); StringBuilder builder = new StringBuilder(); foreach (string file in files) { if (file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) { #if !DEBUG try { #endif Plugin plugin = new Plugin(file); Assembly assembly = Assembly.LoadFile(file); Type[] types = assembly.GetTypes(); foreach (Type type in types) { if (type.IsSubclassOf(typeof(OpenBveApi.Textures.TextureInterface))) { plugin.Texture = (OpenBveApi.Textures.TextureInterface)assembly.CreateInstance(type.FullName); } if (type.IsSubclassOf(typeof(OpenBveApi.Sounds.SoundInterface))) { plugin.Sound = (OpenBveApi.Sounds.SoundInterface)assembly.CreateInstance(type.FullName); } if (type.IsSubclassOf(typeof(OpenBveApi.Objects.ObjectInterface))) { plugin.Object = (OpenBveApi.Objects.ObjectInterface)assembly.CreateInstance(type.FullName); } } if (plugin.Texture != null | plugin.Sound != null | plugin.Object != null) { plugin.Load(); list.Add(plugin); } #if !DEBUG } catch (Exception ex) { builder.Append("Could not load plugin ").Append(Path.GetFileName(file)).AppendLine(":").AppendLine(ex.Message); builder.AppendLine(); } #endif } } LoadedPlugins = list.ToArray(); string message = builder.ToString().Trim(); if (message.Length != 0) { return MessageBox.Show(message, Application.ProductName, MessageBoxButtons.OKCancel, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button2) == DialogResult.OK; } else { return true; } } /// Unloads all non-runtime plugins. internal static void UnloadPlugins() { StringBuilder builder = new StringBuilder(); if (LoadedPlugins != null) { foreach (Plugin plugin in LoadedPlugins) { #if !DEBUG try { #endif plugin.Unload(); #if !DEBUG } catch (Exception ex) { builder.Append("Could not unload plugin ").Append(plugin.Title).AppendLine(":").AppendLine(ex.Message); builder.AppendLine(); } #endif } LoadedPlugins = null; } string message = builder.ToString().Trim(); if (message.Length != 0) { MessageBox.Show(message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); } } } }openbve-1.4.0.10/openBVE/OpenBve/System/Program.cs000066400000000000000000000262551171674032100214630ustar00rootroot00000000000000using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Windows.Forms; using Tao.Sdl; namespace OpenBve { /// Provides methods for starting the program, including the Main procedure. internal static partial class Program { // --- members --- /// Whether the program is currently running on Mono. This is of interest for the Windows Forms main menu which behaves differently on Mono than on Microsoft .NET. internal static bool CurrentlyRunningOnMono = false; /// Whether the program is currently running on Microsoft Windows or compatible. This is of interest for whether running Win32 plugins is possible. internal static bool CurrentlyRunningOnWindows = false; /// The host API used by this program. internal static Host CurrentHost = null; /// Information about the file system organization. internal static FileSystem FileSystem = null; /// The object that serves as an authentication for the SetPackageLookupDirectories call. private static object SetPackageLookupDirectoriesAuthentication = null; /// If the program is to be restarted, this contains the command-line arguments that should be passed to the process, or a null reference otherwise. internal static string RestartArguments = null; /// The random number generator used by this program. internal static Random RandomNumberGenerator = new Random(); // --- functions --- /// Is executed when the program starts. /// The command-line arguments. [STAThread] private static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // --- determine the running environment --- CurrentlyRunningOnMono = Type.GetType("Mono.Runtime") != null; CurrentlyRunningOnWindows = Environment.OSVersion.Platform == PlatformID.Win32S | Environment.OSVersion.Platform == PlatformID.Win32Windows | Environment.OSVersion.Platform == PlatformID.Win32NT; CurrentHost = new Host(); try { FileSystem = FileSystem.FromCommandLineArgs(args); FileSystem.CreateFileSystem(); } catch (Exception ex) { MessageBox.Show("The file system configuration could not be accessed or is invalid due to the following reason:\n\n" + ex.Message, "openBVE", MessageBoxButtons.OK, MessageBoxIcon.Hand); return; } SetPackageLookupDirectories(); // --- check the command-line arguments for route and train --- formMain.MainDialogResult result = new formMain.MainDialogResult(); for (int i = 0; i < args.Length; i++) { if (args[i].StartsWith("/route=", StringComparison.OrdinalIgnoreCase)) { result.RouteFile = args[i].Substring(7); result.RouteEncoding = System.Text.Encoding.UTF8; for (int j = 0; j < Interface.CurrentOptions.RouteEncodings.Length; j++) { if (string.Compare(Interface.CurrentOptions.RouteEncodings[j].Value, result.RouteFile, StringComparison.InvariantCultureIgnoreCase) == 0) { result.RouteEncoding = System.Text.Encoding.GetEncoding(Interface.CurrentOptions.RouteEncodings[j].Codepage); break; } } } else if (args[i].StartsWith("/train=", StringComparison.OrdinalIgnoreCase)) { result.TrainFolder = args[i].Substring(7); result.TrainEncoding = System.Text.Encoding.UTF8; for (int j = 0; j < Interface.CurrentOptions.TrainEncodings.Length; j++) { if (string.Compare(Interface.CurrentOptions.TrainEncodings[j].Value, result.TrainFolder, StringComparison.InvariantCultureIgnoreCase) == 0) { result.TrainEncoding = System.Text.Encoding.GetEncoding(Interface.CurrentOptions.TrainEncodings[j].Codepage); break; } } } } // --- check whether route and train exist --- if (result.RouteFile != null) { if (!System.IO.File.Exists(result.RouteFile)) { result.RouteFile = null; } } if (result.TrainFolder != null) { if (!System.IO.Directory.Exists(result.TrainFolder)) { result.TrainFolder = null; } } // --- if a route was provided but no train, try to use the route default --- if (result.RouteFile != null & result.TrainFolder == null) { bool isRW = string.Equals(System.IO.Path.GetExtension(result.RouteFile), ".rw", StringComparison.OrdinalIgnoreCase); CsvRwRouteParser.ParseRoute(result.RouteFile, isRW, result.RouteEncoding, null, null, null, true); if (Game.TrainName != null && Game.TrainName.Length != 0) { string folder = System.IO.Path.GetDirectoryName(result.RouteFile); while (true) { string trainFolder = OpenBveApi.Path.CombineDirectory(folder, "Train"); if (System.IO.Directory.Exists(trainFolder)) { folder = OpenBveApi.Path.CombineDirectory(trainFolder, Game.TrainName); if (System.IO.Directory.Exists(folder)) { string file = OpenBveApi.Path.CombineFile(folder, "train.dat"); if (System.IO.File.Exists(file)) { result.TrainFolder = folder; result.TrainEncoding = System.Text.Encoding.UTF8; for (int j = 0; j < Interface.CurrentOptions.TrainEncodings.Length; j++) { if (string.Compare(Interface.CurrentOptions.TrainEncodings[j].Value, result.TrainFolder, StringComparison.InvariantCultureIgnoreCase) == 0) { result.TrainEncoding = System.Text.Encoding.GetEncoding(Interface.CurrentOptions.TrainEncodings[j].Codepage); break; } } } } break; } else { System.IO.DirectoryInfo info = System.IO.Directory.GetParent(folder); if (info != null) { folder = info.FullName; } else { break; } } } } Game.Reset(false); } // --- load options and controls --- Interface.LoadOptions(); Interface.LoadControls(null, out Interface.CurrentControls); { string folder = Program.FileSystem.GetDataFolder("Controls"); string file = OpenBveApi.Path.CombineFile(folder, "Default keyboard assignment.controls"); Interface.Control[] controls; Interface.LoadControls(file, out controls); Interface.AddControls(ref Interface.CurrentControls, controls); } // --- load language --- { string folder = Program.FileSystem.GetDataFolder("Languages"); string file = OpenBveApi.Path.CombineFile(folder, Interface.CurrentOptions.LanguageCode + ".cfg"); if (!System.IO.File.Exists(file)) { file = OpenBveApi.Path.CombineFile(folder, "en-US.cfg"); } Interface.LoadLanguage(file); } // --- show the main menu if necessary --- if (result.RouteFile == null | result.TrainFolder == null) { // begin HACK // if (!Joysticks.Initialize()) { MessageBox.Show("SDL failed to initialize the joystick subsystem.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); return; } // end HACK // result = formMain.ShowMainDialog(); } // --- start the actual program --- Program.SetPackageLookupDirectories(); if (result.Start) { if (Initialize()) { MainLoop.StartLoopEx(result); } Deinitialize(); } // --- restart the program if necessary --- if (RestartArguments != null) { string arguments; if (FileSystem.RestartArguments.Length != 0 & RestartArguments.Length != 0) { arguments = FileSystem.RestartArguments + " " + RestartArguments; } else { arguments = FileSystem.RestartArguments + RestartArguments; } try { System.Diagnostics.Process.Start(FileSystem.RestartProcess, arguments); } catch (Exception ex) { MessageBox.Show(ex.Message + "\n\nProcess = " + FileSystem.RestartProcess + "\nArguments = " + arguments, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } } /// Initializes the program. A matching call to deinitialize must be made when the program is terminated. /// Whether the initialization was successful. private static bool Initialize() { if (!Plugins.LoadPlugins()) { return false; } if (!Screen.Initialize()) { MessageBox.Show("SDL failed to initialize the video subsystem.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); return false; } if (!Joysticks.Initialize()) { MessageBox.Show("SDL failed to initialize the joystick subsystem.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); return false; } Sounds.Initialize(); // begin HACK // const double degrees = 0.0174532925199433; World.VerticalViewingAngle = 45.0 * degrees; World.HorizontalViewingAngle = 2.0 * Math.Atan(Math.Tan(0.5 * World.VerticalViewingAngle) * World.AspectRatio); World.OriginalVerticalViewingAngle = World.VerticalViewingAngle; World.ExtraViewingDistance = 50.0; World.ForwardViewingDistance = (double)Interface.CurrentOptions.ViewingDistance; World.BackwardViewingDistance = 0.0; World.BackgroundImageDistance = (double)Interface.CurrentOptions.ViewingDistance; // end HACK // ClearLogFile(); return true; } /// Deinitializes the program. private static void Deinitialize() { Plugins.UnloadPlugins(); Sounds.Deinitialize(); Joysticks.Deinitialize(); Screen.Deinitialize(); Sdl.SDL_Quit(); } /// Provides the API with lookup directories for all installed packages. internal static void SetPackageLookupDirectories() { int size = 16; string[] names = new string[size]; string[] directories = new string[size]; int count = 0; foreach (string lookupDirectory in FileSystem.ManagedContentFolders) { string[] packageDirectories = System.IO.Directory.GetDirectories(lookupDirectory); foreach (string packageDirectory in packageDirectories) { string package = System.IO.Path.GetFileName(packageDirectory); if (count == size) { size <<= 1; Array.Resize(ref names, size); Array.Resize(ref directories, size); } names[count] = package; directories[count] = packageDirectory; count++; } } Array.Resize(ref names, count); Array.Resize(ref directories, count); SetPackageLookupDirectoriesAuthentication = OpenBveApi.Path.SetPackageLookupDirectories(names, directories, SetPackageLookupDirectoriesAuthentication); } /// Clears the log file. internal static void ClearLogFile() { try { string file = System.IO.Path.Combine(Program.FileSystem.SettingsFolder, "log.txt"); System.IO.File.WriteAllText(file, string.Empty, new System.Text.UTF8Encoding(true)); } catch { } } /// Appends the specified text to the log file. /// The text. internal static void AppendToLogFile(string text) { try { string file = System.IO.Path.Combine(Program.FileSystem.SettingsFolder, "log.txt"); System.IO.File.AppendAllText(file, text + "\n", new System.Text.UTF8Encoding(false)); } catch { } } } } openbve-1.4.0.10/openBVE/OpenBve/System/Tar.cs000066400000000000000000000104101171674032100205640ustar00rootroot00000000000000using System; using System.IO; using System.Text; namespace OpenBve { internal static class Tar { /// Extracts the tar data into a specified directory. /// The tar data to extract. /// The directory to extract the content to. /// The directory prefix that is trimmed off all the paths encountered in this archive, or a null reference. internal static void Unpack(byte[] bytes, string directory, string prefix) { System.Text.ASCIIEncoding ascii = new ASCIIEncoding(); System.Text.UTF8Encoding utf8 = new UTF8Encoding(); int position = 0; string longLink = null; while (position < bytes.Length) { string name; if (longLink != null) { name = longLink; longLink = null; } else { name = utf8.GetString(bytes, position, 100).TrimEnd('\0'); } if (name.Length == 0) { /* * The name is empty. This marks the end of the file. * */ break; } else { /* * Read the header and advance the position. * */ string sizeString = ascii.GetString(bytes, position + 124, 12).Trim('\0', ' '); int size = Convert.ToInt32(sizeString, 8); int mode; if (name[name.Length - 1] == '/') { mode = 53; } else { mode = (int)bytes[position + 156]; } if (bytes[position + 257] == 0x75 && bytes[position + 258] == 0x73 && bytes[position + 259] == 0x74 && bytes[position + 260] == 0x61 && bytes[position + 261] == 0x72 && bytes[position + 262] == 0x00) { /* * This is a POSIX ustar archive. * */ string namePrefix = utf8.GetString(bytes, position + 345, 155).TrimEnd(' '); if (namePrefix.Length != 0) { if (namePrefix[namePrefix.Length - 1] != '/' && name[0] != '/') { name = namePrefix + '/' + name; } else { name = namePrefix + name; } } } else if (bytes[position + 257] == 0x75 && bytes[position + 258] == 0x73 && bytes[position + 259] == 0x74 && bytes[position + 260] == 0x61 && bytes[position + 261] == 0x72 && bytes[position + 262] == 0x20) { /* * This is a GNU tar archive. * TODO: Implement support for GNU tar archives here. * */ } position += 512; /* * Process the data depending on the mode. * */ if (mode == 53) { /* * This is a directory. * */ if (name[name.Length - 1] == '/') { name = name.Substring(0, name.Length - 1); } name = name.Replace('/', Path.DirectorySeparatorChar); if (prefix != null) { if (name.StartsWith(prefix + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) { name = name.Substring(prefix.Length + 1); } else if (string.Equals(name, prefix, StringComparison.OrdinalIgnoreCase)) { name = string.Empty; } } try { Directory.CreateDirectory(Path.Combine(directory, name)); } catch { } } else if (mode < 49 | mode > 54) { /* * This is a normal file. * */ if (name != "././@LongLink") { name = name.Replace('/', Path.DirectorySeparatorChar); if (prefix != null && name.StartsWith(prefix + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) { name = name.Substring(prefix.Length + 1); } } int blocks = size + 511 >> 9; byte[] buffer = new byte[size]; Array.Copy(bytes, position, buffer, 0, size); position += blocks << 9; if (name == "././@LongLink") { longLink = utf8.GetString(buffer); if (longLink.Length != 0 && longLink[longLink.Length - 1] == '\0') { longLink = longLink.Substring(0, longLink.Length - 1); } } else { string file = Path.Combine(directory, name); try { Directory.CreateDirectory(Path.GetDirectoryName(file)); } catch { } if (File.Exists(file)) { try { File.SetAttributes(file, FileAttributes.Normal); } catch { } } File.WriteAllBytes(Path.Combine(directory, name), buffer); } } else { /* * Unsupported mode. * */ } } } } } }openbve-1.4.0.10/openBVE/OpenBve/icon.ico000066400000000000000000000124661171674032100176640ustar00rootroot00000000000000 h&  (  @4}}}ZZZYYYVVVTTTQQQOOOMMMKKKIIIGGGFFFGGGxxx6|||666444---&&&wwwlllPPPLLLEEE888>>>iiiYYY  CCCuuubbb\\\PPPpppFFFKKK}}}uuujjj{{{OOO???666>>>FFFPPP턄툈```___YYYRRRGGG444+++VVV파훛wwwppplllccc[\\QOOZTQYYY+++\\\퓓}}}eeefff[[[WWWc`_Rbizus;;;ccc횚vvv|||qqqgggfdcgrx5|ywNNNkkkVVV{vwwcccrrr,,,~~~wwwzzzDDDGGGnnnvvvwww~~~]]]\\\@@@RRRVVVUUUzzz|||OOOOOO 1[[[555쁁CCC4( @ uφ녅텅톆톆톆톆톆퇇퇇퇇퇇퇇퇇퇇퇇퇇툈툈툈툈툈툈틋볳yWWW...(((%%%""" 555ڵ p^^^777===:::777444000---***'''$$$!!! 111xHHHIIIEEEBBB???;;;888444111...+++(((%%%  ՃPPPQQQMMMJJJGGGCCC???<<<999555...%%%$$$,,,111---  킂ZZZYYYVVVRRROOOKKKGGGDDD>>>444KKK~~~eee$$$ 큁cccbbb^^^ZZZWWWSSSOOOGGGMMMmmm 퀀mmmjjjfffccc___[[[RRRdddooo______sss$$$vvvrrroookkkggg```mmmOOO@@@>>><<<888222---DDD%%% ~~~퀀|||xxxtttooonnnlllQQQTTTRRRNNNKKKGGGDDD@@@888---rrr &&&$$$!!!}}}튊|||vvvttt```bbb^^^ZZZWWWSSSOOOLLLHHHEEEAAA000zzz\\\&&&+++'''$$$|||퓓nnnnnnjjjfffccc___[[[XXXTTTPPPMMMIIIEEE999...111...+++{{{휜zzzwwwtttoookkkgggddd```\\\XXXUUUQQQNNNCCCcccSSS444666222%%%zzzwwwzzzwwwqqqpppkkkgggbbb]]][[[VVUVMIRHDzzz888===999---yyy~~~yyydddnnn___WWW```www\\\[[[dddbbbfffTSS_^^4~$}@@@EEEAAA666xxxtttooonnnwwwGGGzzzuuuGGGRRR```NNNdddrrrNGCOyeJJJLLLIII>>>wwwlllddd{{{uuufffwwwpppZZZYYY[[[CCCQQQaaaZUSd{ t¾SSSUUUQQQGGGvvvdddWWW~~~~~~}}}wwwnnniii{{{{wuiy]v\\\]]]YYYPPPuuu]]]LLLwww{y~yv~~~gggfffbbbZZZuuuVVVEEETTTxxxqqqnnnjjjccctttNNN===>>>www~~~{{{wwwsssmmmsssHHH333666;;;|||vvvrrrAAA,,,OOO///999ooouuurrr;;;ttt###---bbbkkk^^^fffqqq555,,,000QQQnnn}}}|||mmmYYYJJJKKKcccppp/// ggg333""""""&&&+++111<<Represents the interface for loading archives. Plugins must implement this interface if they wish to expose archives. public abstract class ArchiveInterface { /// Called when the plugin is loaded. /// The host that loaded the plugin. public virtual void Load(Hosts.HostInterface host) { } /// Called when the plugin is unloaded. public virtual void Unload() { } /// Checks whether the plugin can load the specified element. /// The path to the file or folder that contains the archive. /// The path to the element. /// Whether the plugin can load the specified element. public abstract bool CanLoadElement(string path, string element); /// Loads an item from the specified archive. /// The path to the file or folder that contains the archive. /// The path to the element. /// Receives the element data. /// Whether loading the element was successful. public abstract bool LoadElement(string path, string element, out byte[] data); } }openbve-1.4.0.10/openBVE/OpenBveApi/BasicMaterial.cs000066400000000000000000000014551171674032100217150ustar00rootroot00000000000000using OpenBveApi.Colors; using OpenBveApi.Textures; namespace OpenBveApi.Objects { /// Represents a material with basic properties such as colors and textures. public class BasicMaterial : AbstractMaterial { // --- members --- /// The reflective color. public Color32 ReflectiveColor; /// The emissive color. public Color24 EmissiveColor; /// The daytime texture, or a null reference. public TextureHandle DaytimeTexture; /// The nighttime texture, or a null reference. public TextureHandle NighttimeTexture; /// The blend mode. public BlendModes BlendMode; /// The glow, or a null reference. public AbstractGlow Glow; } }openbve-1.4.0.10/openBVE/OpenBveApi/Colors.cs000066400000000000000000000326241171674032100204600ustar00rootroot00000000000000#pragma warning disable 0660, 0661 using System; namespace OpenBveApi.Colors { /* ---------------------------------------- * TODO: This part of the API is unstable. * Modifications can be made at will. * ---------------------------------------- */ // --- color 24 --- /// Represents a 24-bit color with red, green and blue channels at 8 bits each. public struct Color24 { // --- members --- /// The red component. public byte R; /// The green component. public byte G; /// The blue component. public byte B; // --- constructors --- /// Creates a new color. /// The red component. /// The green component. /// The blue component. public Color24(byte r, byte g, byte b) { this.R = r; this.G = g; this.B = b; } // --- operators --- /// Checks whether two colors are equal. /// The first color. /// The second color. /// Whether the two colors are equal. public static bool operator ==(Color24 a, Color24 b) { return a.R == b.R & a.G == b.G & a.B == b.B; } /// Checks whether two colors are unequal. /// The first color. /// The second color. /// Whether the two colors are unequal. public static bool operator !=(Color24 a, Color24 b) { return a.R != b.R | a.G != b.G | a.B != b.B; } // --- read-only fields --- /// Represents a black color. public static readonly Color24 Black = new Color24(0, 0, 0); /// Represents a red color. public static readonly Color24 Red = new Color24(255, 0, 0); /// Represents a green color. public static readonly Color24 Green = new Color24(0, 255, 0); /// Represents a blue color. public static readonly Color24 Blue = new Color24(0, 0, 255); /// Represents a cyan color. public static readonly Color24 Cyan = new Color24(0, 255, 255); /// Represents a magenta color. public static readonly Color24 Magenta = new Color24(255, 0, 255); /// Represents a yellow color. public static readonly Color24 Yellow = new Color24(255, 255, 0); /// Represents a white color. public static readonly Color24 White = new Color24(255, 255, 255); } // --- color 32 --- /// Represents a 32-bit color with red, green, blue and alpha channels at 8 bits each. public struct Color32 { // --- members --- /// The red component. public byte R; /// The green component. public byte G; /// The blue component. public byte B; /// The alpha component. public byte A; // --- constructors --- /// Creates a new color. /// The red component. /// The green component. /// The blue component. /// The alpha component. public Color32(byte r, byte g, byte b, byte a) { this.R = r; this.G = g; this.B = b; this.A = a; } /// Creates a new color. /// The red component. /// The green component. /// The blue component. /// The alpha component is set to full opacity. public Color32(byte r, byte g, byte b) { this.R = r; this.G = g; this.B = b; this.A = 255; } /// Creates a new color. /// The solid color. /// The alpha component. public Color32(Color24 color, byte a) { this.R = color.R; this.G = color.G; this.B = color.B; this.A = a; } /// Creates a new color. /// The solid color. /// The alpha component is set to full opacity. public Color32(Color24 color) { this.R = color.R; this.G = color.G; this.B = color.B; this.A = 255; } // --- operators --- /// Checks whether two colors are equal. /// The first color. /// The second color. /// Whether the two colors are equal. public static bool operator ==(Color32 a, Color32 b) { return a.R == b.R & a.G == b.G & a.B == b.B & a.A == b.A; } /// Checks whether two colors are unequal. /// The first color. /// The second color. /// Whether the two colors are unequal. public static bool operator !=(Color32 a, Color32 b) { return a.R != b.R | a.G != b.G | a.B != b.B | a.A != b.A; } // --- read-only fields --- /// Represents a black color. public static readonly Color32 Black = new Color32(0, 0, 0); /// Represents a red color. public static readonly Color32 Red = new Color32(255, 0, 0); /// Represents a green color. public static readonly Color32 Green = new Color32(0, 255, 0); /// Represents a blue color. public static readonly Color32 Blue = new Color32(0, 0, 255); /// Represents a cyan color. public static readonly Color32 Cyan = new Color32(0, 255, 255); /// Represents a magenta color. public static readonly Color32 Magenta = new Color32(255, 0, 255); /// Represents a yellow color. public static readonly Color32 Yellow = new Color32(255, 255, 0); /// Represents a white color. public static readonly Color32 White = new Color32(255, 255, 255); /// Represents a transparent black color. public static readonly Color32 Transparent = new Color32(0, 0, 0, 0); // --- conversions --- /// Performs a widening conversion from Color24 to Color32. /// The Color24 value. /// The Color32 value. public static implicit operator Color32(Color24 value) { return new Color32(value.R, value.G, value.B); } /// Performs a narrowing conversion from Color32 to Color24. /// The Color32 value. /// The Color24 value. public static explicit operator Color24(Color32 value) { return new Color24(value.R, value.G, value.B); } } // --- color 96 --- /// Represents a 96-bit color with red, green and blue channels at 32 bits each. public struct Color96 { // --- members --- /// The red component. public float R; /// The green component. public float G; /// The blue component. public float B; // --- constructors --- /// Creates a new color. /// The red component. /// The green component. /// The blue component. public Color96(float r, float g, float b) { this.R = r; this.G = g; this.B = b; } // --- operators --- /// Checks whether two colors are equal. /// The first color. /// The second color. /// Whether the two colors are equal. public static bool operator ==(Color96 a, Color96 b) { return a.R == b.R & a.G == b.G & a.B == b.B; } /// Checks whether two colors are unequal. /// The first color. /// The second color. /// Whether the two colors are unequal. public static bool operator !=(Color96 a, Color96 b) { return a.R != b.R | a.G != b.G | a.B != b.B; } // --- read-only fields --- /// Represents a black color. public static readonly Color96 Black = new Color96(0.0f, 0.0f, 0.0f); /// Represents a red color. public static readonly Color96 Red = new Color96(1.0f, 0.0f, 0.0f); /// Represents a green color. public static readonly Color96 Green = new Color96(0.0f, 1.0f, 0.0f); /// Represents a blue color. public static readonly Color96 Blue = new Color96(0.0f, 0.0f, 1.0f); /// Represents a cyan color. public static readonly Color96 Cyan = new Color96(0.0f, 1.0f, 1.0f); /// Represents a magenta color. public static readonly Color96 Magenta = new Color96(1.0f, 0.0f, 1.0f); /// Represents a yellow color. public static readonly Color96 Yellow = new Color96(1.0f, 1.0f, 0.0f); /// Represents a white color. public static readonly Color96 White = new Color96(1.0f, 1.0f, 1.0f); } // --- color 128 --- /// Represents a 128-bit color with red, green, blue and alpha channels at 32 bits each. public struct Color128 { // --- members --- /// The red component. public float R; /// The green component. public float G; /// The blue component. public float B; /// The alpha component. public float A; // --- constructors --- /// Creates a new color. /// The red component. /// The green component. /// The blue component. /// The alpha component. public Color128(float r, float g, float b, float a) { this.R = r; this.G = g; this.B = b; this.A = a; } /// Creates a new color. /// The red component. /// The green component. /// The blue component. /// The alpha component is set to full opacity. public Color128(float r, float g, float b) { this.R = r; this.G = g; this.B = b; this.A = 1.0f; } /// Creates a new color. /// The solid color. /// The alpha component. public Color128(Color24 color, float a) { this.R = color.R; this.G = color.G; this.B = color.B; this.A = a; } /// Creates a new color. /// The solid color. /// The alpha component is set to full opacity. public Color128(Color24 color) { this.R = color.R; this.G = color.G; this.B = color.B; this.A = 1.0f; } // --- operators --- /// Checks whether two colors are equal. /// The first color. /// The second color. /// Whether the two colors are equal. public static bool operator ==(Color128 a, Color128 b) { return a.R == b.R & a.G == b.G & a.B == b.B & a.A == b.A; } /// Checks whether two colors are unequal. /// The first color. /// The second color. /// Whether the two colors are unequal. public static bool operator !=(Color128 a, Color128 b) { return a.R != b.R | a.G != b.G | a.B != b.B | a.A != b.A; } // --- read-only fields --- /// Represents a black color. public static readonly Color128 Black = new Color128(0.0f, 0.0f, 0.0f); /// Represents a red color. public static readonly Color128 Red = new Color128(1.0f, 0.0f, 0.0f); /// Represents a green color. public static readonly Color128 Green = new Color128(0.0f, 1.0f, 0.0f); /// Represents a blue color. public static readonly Color128 Blue = new Color128(0.0f, 0.0f, 1.0f); /// Represents a cyan color. public static readonly Color128 Cyan = new Color128(0.0f, 1.0f, 1.0f); /// Represents a magenta color. public static readonly Color128 Magenta = new Color128(1.0f, 0.0f, 1.0f); /// Represents a yellow color. public static readonly Color128 Yellow = new Color128(1.0f, 1.0f, 0.0f); /// Represents a white color. public static readonly Color128 White = new Color128(1.0f, 1.0f, 1.0f); /// Represents a transparent black color. public static readonly Color128 Transparent = new Color128(0.0f, 0.0f, 0.0f, 0.0f); // --- conversions --- /// Performs a widening conversion from Color96 to Color128. /// The Color96 value. /// The Color128 value. public static implicit operator Color128(Color24 value) { return new Color128(value.R, value.G, value.B); } /// Performs a narrowing conversion from Color128 to Color96. /// The Color128 value. /// The Color96 value. public static explicit operator Color96(Color128 value) { return new Color96(value.R, value.G, value.B); } } }openbve-1.4.0.10/openBVE/OpenBveApi/Geometry.cs000066400000000000000000000000441171674032100210010ustar00rootroot00000000000000namespace OpenBveApi.Geometry { }openbve-1.4.0.10/openBVE/OpenBveApi/Hosts.cs000066400000000000000000000121421171674032100203100ustar00rootroot00000000000000using System; using OpenBveApi.Objects; using OpenBveApi.Sounds; using OpenBveApi.Textures; namespace OpenBveApi.Hosts { /* ---------------------------------------- * TODO: This part of the API is unstable. * Modifications can be made at will. * ---------------------------------------- */ /// Represents the type of problem that is reported to the host. public enum ProblemType { /// Indicates that a file could not be found. FileNotFound = 1, /// Indicates that a directory could not be found. DirectoryNotFound = 2, /// Indicates that a file or directory could not be found. PathNotFound = 3, /// Indicates invalid data in a file or directory. InvalidData = 4, /// Indicates an invalid operation. InvalidOperation = 5, /// Indicates an unexpected exception. UnexpectedException = 6 } /// Represents the host application and functionality it exposes. public abstract class HostInterface { /// Reports a problem to the host application. /// The type of problem that is reported. /// The textual message that describes the problem. public virtual void ReportProblem(ProblemType type, string text) { } /// Queries the dimensions of a texture. /// The path to the file or folder that contains the texture. /// Receives the width of the texture. /// Receives the height of the texture. /// Whether querying the dimensions was successful. public virtual bool QueryTextureDimensions(string path, out int width, out int height) { width = 0; height = 0; return false; } /// Loads a texture and returns the texture data. /// The path to the file or folder that contains the texture. /// The parameters that specify how to process the texture. /// Receives the texture. /// Whether loading the texture was successful. public virtual bool LoadTexture(string path, TextureParameters parameters, out Texture texture) { texture = null; return false; } /// Registers a texture and returns a handle to the texture. /// The path to the file or folder that contains the texture. /// The parameters that specify how to process the texture. /// Receives the handle to the texture. /// Whether loading the texture was successful. public virtual bool RegisterTexture(string path, TextureParameters parameters, out TextureHandle handle) { handle = null; return false; } /// Registers a texture and returns a handle to the texture. /// The texture data. /// The parameters that specify how to process the texture. /// Receives the handle to the texture. /// Whether loading the texture was successful. public virtual bool RegisterTexture(Textures.Texture texture, TextureParameters parameters, out TextureHandle handle) { handle = null; return false; } /// Loads a sound and returns the sound data. /// The path to the file or folder that contains the sound. /// Receives the sound. /// Whether loading the sound was successful. public virtual bool LoadSound(string path, out Sound sound) { sound = null; return false; } /// Registers a sound and returns a handle to the sound. /// The path to the file or folder that contains the sound. /// Receives a handle to the sound. /// Whether loading the sound was successful. public virtual bool RegisterSound(string path, out SoundHandle handle) { handle = null; return false; } /// Registers a sound and returns a handle to the sound. /// The sound data. /// Receives a handle to the sound. /// Whether loading the sound was successful. public virtual bool RegisterSound(Sounds.Sound sound, out SoundHandle handle) { handle = null; return false; } /// Loads an object and returns the object data. /// The path to the file or folder that contains the object. /// Receives the object. /// Whether loading the object was successful. public virtual bool LoadObject(string path, out AbstractObject obj) { obj = null; return false; } } }openbve-1.4.0.10/openBVE/OpenBveApi/Math.cs000066400000000000000000000003401171674032100200760ustar00rootroot00000000000000namespace OpenBveApi.Math { /* ---------------------------------------- * TODO: This part of the API is unstable. * Modifications can be made at will. * ---------------------------------------- */ }openbve-1.4.0.10/openBVE/OpenBveApi/Objects.cs000066400000000000000000000152361171674032100206100ustar00rootroot00000000000000using System; using OpenBveApi.Math; namespace OpenBveApi.Objects { /* ---------------------------------------- * TODO: This part of the API is unstable. * Modifications can be made at will. * ---------------------------------------- */ // --- objects --- /// Represents an abstract object. This is the base class from which all objects must inherit. public abstract class AbstractObject { /// Translates the object by the specified offset. /// The offset by which to translate. public abstract void Translate(Vector3 offset); /// Translates the object by the specified offset that is measured in the specified orientation. /// The orientation along which to translate. /// The offset measured in the specified orientation. public abstract void Translate(Orientation3 orientation, Vector3 offset); /// Rotates the object around the specified axis. /// The axis along which to rotate. /// The cosine of the angle by which to rotate. /// The sine of the angle by which to rotate. public abstract void Rotate(Vector3 direction, double cosineOfAngle, double sineOfAngle); /// Rotates the object from the default orientation into the specified orientation. /// The target orientation. /// The default orientation is X = {1, 0, 0), Y = {0, 1, 0} and Z = {0, 0, 1}. public abstract void Rotate(Orientation3 orientation); /// Scales the object by the specified factor. /// The factor by which to scale. public abstract void Scale(Vector3 factor); } /// Represents an abstract static object. This is the base class from which all static objects must inherit. public abstract class StaticObject : AbstractObject { } /// Represents an abstract animated object. This is the base class from which all animated objects must inherit. public abstract class AnimatedObject : AbstractObject { } // --- materials --- /// Represents an abstract material. This is the base class from which all materials must inherit. public abstract class AbstractMaterial { } // --- blend modes --- /// Represents blend modes. public enum BlendModes { /// Represents normal blend mode. Normal = 0, /// Represents additive blend mode. Additive = 1 } // --- glow --- /// Represents an abstract glow. This is the base class from which all glows must inherit. public abstract class AbstractGlow { } /// Represents an abstract orientational glow. This is the base class from which all orientational glows must inherit. /// This type of glow computes the intensity as a function of the camera and object's position and orientation. public abstract class OrientationalGlow : AbstractGlow { /// Gets the intensity of the glow. /// The position of the camera. /// The orientation of the camera. /// The position of the object. /// The orientation of the object. /// The intensity of the glow expressed as a value between 0 and 1. public abstract double GetIntensity(Vector3 cameraPosition, Orientation3 cameraOrientation, Vector3 objectPosition, Vector3 objectOrientation); } /// Represents a glow where the intensity is inversely proportional to the distance between the object and the camera. public class DistanceGlow : OrientationalGlow { // --- members --- /// The square of the distance at which the intensity is exactly 50%. private double HalfDistanceSquared; // --- constructors --- /// Creates a new distance glow. /// The distance at which the intensity is exactly 50%. public DistanceGlow(double halfDistance) { this.HalfDistanceSquared = halfDistance * halfDistance; } // --- functions --- /// Gets the intensity of the glow. /// The position of the camera. /// The orientation of the camera. /// The position of the object. /// The orientation of the object. /// The intensity of the glow expressed as a value between 0 and 1. public override double GetIntensity(Vector3 cameraPosition, Orientation3 cameraOrientation, Vector3 objectPosition, Vector3 objectOrientation) { /* The underlying formula for the intensity is * i = d^2 / (d^2 + h^2) * where * i = intensity * d = distance between object and camera * h = distance at which intensity is 50% */ double distanceSquared = (objectPosition - cameraPosition).NormSquared(); return distanceSquared / (distanceSquared + this.HalfDistanceSquared); } } // --- handles --- /// Represents a handle to an object. public abstract class ObjectHandle { } // --- interfaces --- /// Represents the interface for loading objects. Plugins must implement this interface if they wish to expose objects. public abstract class ObjectInterface { /// Called when the plugin is loaded. /// The host that loaded the plugin. public virtual void Load(Hosts.HostInterface host) { } /// Called when the plugin is unloaded. public virtual void Unload() { } /// Checks whether the plugin can load the specified object. /// The path to the file or folder that contains the object. /// Whether the plugin can load the specified object. public abstract bool CanLoadObject(string path); /// Loads the specified object. /// The path to the file or folder that contains the object. /// Receives the object. /// Whether loading the object was successful. public abstract bool LoadObject(string path, out Object obj); } }openbve-1.4.0.10/openBVE/OpenBveApi/OpenBveApi.csproj000066400000000000000000000064601171674032100221010ustar00rootroot00000000000000 {27134980-4415-4375-A564-40A9014DFA5F} Debug x86 Library OpenBveApi OpenBveApi v4.0 Properties C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis False False 4 false bin\Release\OpenBveApi.xml Client AnyCPU False Auto 4194304 4096 bin\Debug\ true Full False True DEBUG;TRACE bin\Release\ false None True False TRACE Objects.cs Math.cs Objects.cs Textures.cs Math.cs Math.cs openbve-1.4.0.10/openBVE/OpenBveApi/OpenBveApi.sln000066400000000000000000000015151171674032100213710ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 # SharpDevelop 3.2.0.5777 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBveApi", "OpenBveApi.csproj", "{27134980-4415-4375-A564-40A9014DFA5F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {27134980-4415-4375-A564-40A9014DFA5F}.Debug|x86.Build.0 = Debug|x86 {27134980-4415-4375-A564-40A9014DFA5F}.Debug|x86.ActiveCfg = Debug|x86 {27134980-4415-4375-A564-40A9014DFA5F}.Release|x86.Build.0 = Release|x86 {27134980-4415-4375-A564-40A9014DFA5F}.Release|x86.ActiveCfg = Release|x86 EndGlobalSection EndGlobal openbve-1.4.0.10/openBVE/OpenBveApi/Orientation3.cs000066400000000000000000000023611171674032100215700ustar00rootroot00000000000000using System; namespace OpenBveApi.Math { /// Represents an orientation in three-dimensional space. public struct Orientation3 { // --- members --- /// The vector pointing right. public Vector3 X; /// The vector pointing up. public Vector3 Y; /// The vector pointing forward. public Vector3 Z; // --- constructors --- /// Creates a new orientation in three-dimensional space. /// The vector pointing right. /// The vector pointing up. /// The vector pointing forward. public Orientation3(Vector3 x, Vector3 y, Vector3 z) { this.X = x; this.Y = y; this.Z = z; } // --- read-only fields --- /// Represents a null orientation. public static readonly Orientation3 Null = new Orientation3(Vector3.Null, Vector3.Null, Vector3.Null); /// Represents the default orientation with X = {1, 0, 0}, Y = {0, 1, 0} and Z = {0, 0, 1}. public static readonly Orientation3 Default = new Orientation3(Vector3.Right, Vector3.Up, Vector3.Forward); } }openbve-1.4.0.10/openBVE/OpenBveApi/Path.cs000066400000000000000000000237421171674032100201140ustar00rootroot00000000000000#pragma warning disable 0659, 0661 using System; using System.Text; namespace OpenBveApi { /* ---------------------------------------- * TODO: This part of the API is unstable. * Modifications can be made at will. * ---------------------------------------- */ /// Provides path-related functions for accessing files and directories in a cross-platform manner. public static class Path { // --- read-only fields --- /// The list of characters that are invalid in platform-independent relative paths. private static readonly char[] InvalidPathChars = new char[] { ':', '*', '?', '"', '<', '>', '|' }; /// The list of characters at which relative paths are separated into parts. private static readonly char[] PathSeparationChars = new char[] { '/', '\\' }; // --- managed content --- /// The list of package names. private static string[] PackageNames = null; /// The list of package directories. private static string[] PackageDirectories = null; /// The object that serves as an authentication for the SetPackageLookupDirectories call. private static object SetPackageLookupDirectoriesAuthentication = null; // --- public functions --- /// Provides a list of package names and associated directories. /// The list of names. /// The list of fully qualified directories. /// A null reference on the first process-wide call to this function, otherwise the object returned by this function in the previous call. /// Raised when the authentication failed. public static object SetPackageLookupDirectories(string[] names, string[] directories, object authentication) { if (authentication == SetPackageLookupDirectoriesAuthentication) { PackageNames = (string[])names.Clone(); PackageDirectories = (string[])directories.Clone(); Array.Sort(PackageNames, PackageDirectories); SetPackageLookupDirectoriesAuthentication = new object(); return SetPackageLookupDirectoriesAuthentication; } else { throw new System.Security.SecurityException(); } } /// Combines a platform-specific absolute path with a platform-independent relative path that points to a directory. /// The platform-specific absolute path. /// The platform-independent relative path. /// A platform-specific absolute path to the specified directory. /// Raised when combining the paths failed, for example due to malformed paths or due to unauthorized access. public static string CombineDirectory(string absolute, string relative) { int index = relative.IndexOf("??"); if (index >= 0) { string directory = CombineDirectory(absolute, relative.Substring(0, index).TrimEnd()); if (System.IO.Directory.Exists(directory)) { return directory; } else { return CombineDirectory(absolute, relative.Substring(index + 2).TrimStart()); } } if (relative.IndexOfAny(InvalidPathChars) >= 0) { throw new ArgumentException("The relative path contains invalid characters."); } ResolvePackageReference(ref absolute, ref relative); string[] parts = relative.Split(PathSeparationChars, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < parts.Length; i++) { if (parts[i].Length != 0) { /* * Consider only non-empty parts. * */ if (IsAllPeriods(parts[i])) { /* * A string of periods is a reference to an * upper directory. A single period is the * current directory. For each additional * period, jump one directory up. * */ for (int j = 1; j < parts[i].Length; j++) { absolute = System.IO.Path.GetDirectoryName(absolute); } } else { /* * This part references a directory. * */ string directory = System.IO.Path.Combine(absolute, parts[i]); if (System.IO.Directory.Exists(directory)) { absolute = directory; } else { /* * Try to find the directory case-insensitively. * */ bool found = false; if (System.IO.Directory.Exists(absolute)) { string[] directories = System.IO.Directory.GetDirectories(absolute); for (int j = 0; j < directories.Length; j++) { string name = System.IO.Path.GetFileName(directories[j]); if (name.Equals(parts[i], StringComparison.OrdinalIgnoreCase)) { absolute = directories[j]; found = true; break; } } } if (!found) { absolute = directory; } } } } } return absolute; } /// Combines a platform-specific absolute path with a platform-independent relative path that points to a file. /// The platform-specific absolute path. /// The platform-independent relative path. /// Whether the operation succeeded and the specified file was found. /// Raised when combining the paths failed, for example due to malformed paths or due to unauthorized access. public static string CombineFile(string absolute, string relative) { int index = relative.IndexOf("??"); if (index >= 0) { string file = CombineFile(absolute, relative.Substring(0, index).TrimEnd()); if (System.IO.File.Exists(file)) { return file; } else { return CombineFile(absolute, relative.Substring(index + 2).TrimStart()); } } if (relative.IndexOfAny(InvalidPathChars) >= 0) { throw new ArgumentException("The relative path contains invalid characters."); } ResolvePackageReference(ref absolute, ref relative); string[] parts = relative.Split(PathSeparationChars, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < parts.Length; i++) { if (parts[i].Length != 0) { /* * Consider only non-empty parts. * */ if (IsAllPeriods(parts[i])) { if (i == parts.Length - 1) { /* * The last part must not be all periods because * it would reference a directory then, not a file. * */ throw new ArgumentException("The relative path is malformed."); } else { /* * A string of periods is a reference to an * upper directory. A single period is the * current directory. For each additional * period, jump one directory up. * */ for (int j = 1; j < parts[i].Length; j++) { absolute = System.IO.Path.GetDirectoryName(absolute); } } } else if (i == parts.Length - 1) { /* * The last part references a file. * */ string file = System.IO.Path.Combine(absolute, parts[i]); if (System.IO.File.Exists(file)) { return file; } else { /* * Try to find the file case-insensitively. * */ if (System.IO.Directory.Exists(absolute)) { string[] files = System.IO.Directory.GetFiles(absolute); for (int j = 0; j < files.Length; j++) { string name = System.IO.Path.GetFileName(files[j]); if (name.Equals(parts[i], StringComparison.OrdinalIgnoreCase)) { return files[j]; } } } return file; } } else { /* * This part references a directory. * */ string directory = System.IO.Path.Combine(absolute, parts[i]); if (System.IO.Directory.Exists(directory)) { absolute = directory; } else { /* * Try to find the directory case-insensitively. * */ bool found = false; if (System.IO.Directory.Exists(absolute)) { string[] directories = System.IO.Directory.GetDirectories(absolute); for (int j = 0; j < directories.Length; j++) { string name = System.IO.Path.GetFileName(directories[j]); if (name.Equals(parts[i], StringComparison.OrdinalIgnoreCase)) { absolute = directories[j]; found = true; break; } } } if (!found) { absolute = directory; } } } } } throw new ArgumentException("The reference to the file is malformed."); } // --- private functions --- /// Checks whether the specified string consists only of periods. /// The string to check. /// Whether the string consists only of periods. private static bool IsAllPeriods(string text) { for (int i = 0; i < text.Length; i++) { if (text[i] != '.') { return false; } } return true; } /// Resolves a package reference in the relative path and adjusts the absolute path if found. /// The absolute path. /// The relative path. private static void ResolvePackageReference(ref string absolute, ref string relative) { if (relative.Length != 0 && relative[0] == '$') { int index = relative.IndexOfAny(new char[] { '/', '\\' }); if (index >= 0) { string package = relative.Substring(1, index - 1); relative = relative.Substring(index + 1); if (PackageNames != null) { index = Array.BinarySearch(PackageNames, package); if (index >= 0 & index < PackageNames.Length) { absolute = PackageDirectories[index]; return; } } throw new System.IO.DirectoryNotFoundException("The package " + package + " could not be found."); } } } } }openbve-1.4.0.10/openBVE/OpenBveApi/Properties/000077500000000000000000000000001171674032100210155ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBveApi/Properties/AssemblyInfo.cs000066400000000000000000000005541171674032100237430ustar00rootroot00000000000000using System; using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle("openBVE API Class Library")] [assembly: AssemblyProduct("openBVE")] [assembly: AssemblyCopyright("(Public Domain) http://trainsimframework.org/")] [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] [assembly: AssemblyVersion("1.3.2.0")]openbve-1.4.0.10/openBVE/OpenBveApi/Runtime.cs000066400000000000000000000737201171674032100206440ustar00rootroot00000000000000using System; namespace OpenBveApi.Runtime { /* ---------------------------------------------------- * This part of the API is stable as of openBVE 1.2.10. * Any modification must retain backward compatibility. * ---------------------------------------------------- */ // --- load --- /// Represents the handle to a sound. public class SoundHandle { // --- members --- /// Whether the handle to the sound is valid. protected bool MyValid; /// The volume. A value of 1.0 represents nominal volume. protected double MyVolume; /// The pitch. A value of 1.0 represents nominal pitch. protected double MyPitch; // --- properties --- /// Gets whether the sound is still playing. Once this returns false, the sound handle is invalid. public bool Playing { get { return this.MyValid; } } /// Gets whether the sound has stopped. Once this returns true, the sound handle is invalid. public bool Stopped { get { return !this.MyValid; } } /// Gets or sets the volume. A value of 1.0 represents nominal volume. public double Volume { get { return this.MyVolume; } set { this.MyVolume = value; } } /// Gets or sets the pitch. A value of 1.0 represents nominal pitch. public double Pitch { get { return this.MyPitch; } set { this.MyPitch = value; } } // functions /// Stops the sound and invalidates the handle. public void Stop() { this.MyValid = false; } } /// Plays a sound. /// The index to the sound to be played. /// The initial volume of the sound. A value of 1.0 represents nominal volume. /// The initial pitch of the sound. A value of 1.0 represents nominal pitch. /// Whether the sound should be played in an indefinate loop. /// The handle to the sound, or a null reference if the sound could not be played. /// Raised when the host application does not allow the function to be called. public delegate SoundHandle PlaySoundDelegate(int index, double volume, double pitch, bool looped); /// Represents to which extent the plugin supports the AI. public enum AISupport { /// The plugin does not support the AI. Calls to PerformAI will not be made. Non-player trains will not use the plugin. None = 0, /// The plugin complements the built-in AI by performing only functions specific to the plugin. Basic = 1 } /// Represents properties supplied to the plugin on loading. public class LoadProperties { // --- members --- /// The absolute path to the plugin folder. private string MyPluginFolder; /// The absolute path to the train folder. private string MyTrainFolder; /// The array of panel variables. private int[] MyPanel; /// The callback function for playing sounds. /// Raised when the host application does not allow the function to be called. private PlaySoundDelegate MyPlaySound; /// The extent to which the plugin supports the AI. private AISupport MyAISupport; /// The reason why the plugin failed loading. private string MyFailureReason; // --- properties --- /// Gets the absolute path to the plugin folder. public string PluginFolder { get { return this.MyPluginFolder; } } /// Gets the absolute path to the train folder. public string TrainFolder { get { return this.MyTrainFolder; } } /// Gets or sets the array of panel variables. public int[] Panel { get { return this.MyPanel; } set { this.MyPanel = value; } } /// Gets the callback function for playing sounds. public PlaySoundDelegate PlaySound { get { return this.MyPlaySound; } } /// Gets or sets the extent to which the plugin supports the AI. public AISupport AISupport { get { return this.MyAISupport; } set { this.MyAISupport = value; } } /// Gets or sets the reason why the plugin failed loading. public string FailureReason { get { return this.MyFailureReason; } set { this.MyFailureReason = value; } } // --- constructors --- /// Creates a new instance of this class. /// The absolute path to the plugin folder. /// The absolute path to the train folder. /// The callback function for playing sounds. public LoadProperties(string pluginFolder, string trainFolder, PlaySoundDelegate playSound) { this.MyPluginFolder = pluginFolder; this.MyTrainFolder = trainFolder; this.MyPlaySound = playSound; this.MyFailureReason = null; } } // --- set vehicle specs --- /// Represents the type of brake the train uses. public enum BrakeTypes { /// The train uses the electromagnetic straight air brake. The numerical value of this constant is 0. ElectromagneticStraightAirBrake = 0, /// The train uses the analog/digital electro-pneumatic air brake without a brake pipe (electric command brake). The numerical value of this constant is 1. ElectricCommandBrake = 1, /// The train uses the automatic air brake with partial release. The numerical value of this constant is 2. AutomaticAirBrake = 2 } /// Represents the specification of the train. public class VehicleSpecs { // --- members --- /// The number of power notches the train has. private int MyPowerNotches; /// The type of brake the train uses. private BrakeTypes MyBrakeType; /// Whether the train has a hold brake. private bool MyHasHoldBrake; /// The number of brake notches the train has, including the hold brake, but excluding the emergency brake. private int MyBrakeNotches; /// The number of cars the train has. private int MyCars; // --- properties --- /// Gets the number of power notches the train has. public int PowerNotches { get { return this.MyPowerNotches; } } /// Gets the type of brake the train uses. public BrakeTypes BrakeType { get { return this.MyBrakeType; } } /// Gets the number of brake notches the train has, including the hold brake, but excluding the emergency brake. public int BrakeNotches { get { return this.MyBrakeNotches; } } /// Gets whether the train has a hold brake. public bool HasHoldBrake { get { return this.MyHasHoldBrake; } } /// Gets the index of the brake notch that corresponds to B1 or LAP. /// For trains without a hold brake, this returns 1. For trains with a hold brake, this returns 2. public int AtsNotch { get { if (this.MyHasHoldBrake) { return 2; } else { return 1; } } } /// Gets the index of the brake notch that corresponds to 70% of the available brake notches. public int B67Notch { get { return (int)System.Math.Round(0.7 * this.MyBrakeNotches); } } /// Gets the number of cars the train has. public int Cars { get { return this.MyCars; } } // --- constructors --- /// Creates a new instance of this class. /// The number of power notches the train has. /// The type of brake the train uses. /// The number of brake notches the train has, including the hold brake, but excluding the emergency brake. /// Whether the train has a hold brake. /// The number of cars the train has. public VehicleSpecs(int powerNotches, BrakeTypes brakeType, int brakeNotches, bool hasHoldBrake, int cars) { this.MyPowerNotches = powerNotches; this.MyBrakeType = brakeType; this.MyBrakeNotches = brakeNotches; this.MyHasHoldBrake = hasHoldBrake; this.MyCars = cars; } } // --- initialize --- /// Represents the mode in which the plugin should initialize. public enum InitializationModes { /// The safety system should be enabled. The train has its service brakes applied. The numerical value of this constant is -1. OnService = -1, /// The safety system should be enabled. The train has its emergency brakes applied. The numerical value of this constant is 0. OnEmergency = 0, /// The safety system should be disabled. The train has its emergency brakes applied. The numerical value of this constant is 1. OffEmergency = 1 } // --- elapse --- /// Represents a speed. public class Speed { // --- members --- /// The speed in meters per second. private double MyValue; // --- properties --- /// Gets the speed in meters per second. public double MetersPerSecond { get { return this.MyValue; } } /// Gets the speed in kilometes per hour. public double KilometersPerHour { get { return 3.6 * this.MyValue; } } /// Gets the speed in miles per hour. public double MilesPerHour { get { return 2.236936 * this.MyValue; } } // --- constructors --- /// Creates a new instance of this class. /// The speed in meters per second. public Speed(double value) { this.MyValue = value; } } /// Represents a time. public class Time { // --- members --- /// The time in seconds. private double MyValue; // --- properties --- /// Gets the time in seconds. public double Seconds { get { return this.MyValue; } } /// Gets the time in milliseconds. public double Milliseconds { get { return 1000.0 * this.MyValue; } } // --- constructors --- /// Creates a new instance of this class. /// The time in seconds. public Time(double value) { this.MyValue = value; } } /// Represents the current state of the train. public class VehicleState { // --- members --- /// The location of the front of the train, in meters. private double MyLocation; /// The speed of the train. private Speed MySpeed; /// The pressure in the brake cylinder, in pascal. private double MyBcPressure; /// The pressure in the main reservoir, in pascal. private double MyMrPressure; /// The pressure in the emergency reservoir, in pascal. private double MyErPressure; /// The pressure in the brake pipe, in pascal. private double MyBpPressure; /// The pressure in the straight air pipe, in pascal. private double MySapPressure; // --- properties --- /// Gets the location of the front of the train, in meters. public double Location { get { return this.MyLocation; } } /// Gets the speed of the train. public Speed Speed { get { return this.MySpeed; } } /// Gets the pressure in the brake cylinder, in pascal. public double BcPressure { get { return this.MyBcPressure; } } /// Gets the pressure in the main reservoir, in pascal. public double MrPressure { get { return this.MyMrPressure; } } /// Gets the pressure in the emergency reservoir, in pascal. public double ErPressure { get { return this.MyErPressure; } } /// Gets the pressure in the brake pipe, in pascal. public double BpPressure { get { return this.MyBpPressure; } } /// Gets the pressure in the straight air pipe, in pascal. public double SapPressure { get { return this.MySapPressure; } } // --- constructors --- /// Creates a new instance of this class. /// The location of the front of the train, in meters. /// The speed of the train. /// The pressure in the brake cylinder, in pascal. /// The pressure in the main reservoir, in pascal. /// The pressure in the emergency reservoir, in pascal. /// The pressure in the brake pipe, in pascal. /// The pressure in the straight air pipe, in pascal. public VehicleState(double location, Speed speed, double bcPressure, double mrPressure, double erPressure, double bpPressure, double sapPressure) { this.MyLocation = location; this.MySpeed = speed; this.MyBcPressure = bcPressure; this.MyMrPressure = mrPressure; this.MyErPressure = erPressure; this.MyBpPressure = bpPressure; this.MySapPressure = sapPressure; } } /// Represents the current state of the preceding train. public class PrecedingVehicleState { // --- members --- /// The location of the back of the preceding train, in meters. private double MyLocation; /// The distance from the front of the current train to the back of the preceding train, in meters. private double MyDistance; /// The current speed of the preceding train. private Speed MySpeed; // --- properties --- /// Gets the location of the back of the preceding train, in meters. public double Location { get { return this.MyLocation; } } /// Gets the distance from the front of the current train to the back of the preceding train, in meters. public double Distance { get { return this.MyDistance; } } /// Gets the speed of the preceding train. public Speed Speed { get { return this.MySpeed; } } // --- constructors --- /// Creates a new instance of this class. /// Gets the location of the back of the preceding train, in meters. /// The distance from the front of the current train to the back of the preceding train, in meters. /// Gets the speed of the preceding train. public PrecedingVehicleState(double location, double distance, Speed speed) { this.MyLocation = location; this.MyDistance = distance; this.MySpeed = speed; } } /// Represents the handles of the cab. public class Handles { // --- members --- /// The reverser position. private int MyReverser; /// The power notch. private int MyPowerNotch; /// The brake notch. private int MyBrakeNotch; /// Whether the const speed system is enabled. private bool MyConstSpeed; // --- properties --- /// Gets or sets the reverser position. public int Reverser { get { return this.MyReverser; } set { this.MyReverser = value; } } /// Gets or sets the power notch. public int PowerNotch { get { return this.MyPowerNotch; } set { this.MyPowerNotch = value; } } /// Gets or sets the brake notch. public int BrakeNotch { get { return this.MyBrakeNotch; } set { this.MyBrakeNotch = value; } } /// Gets or sets whether the const speed system is enabled. public bool ConstSpeed { get { return this.MyConstSpeed; } set { this.MyConstSpeed = value; } } // --- constructors --- /// Creates a new instance of this class. /// The current reverser position. /// The current power notch. /// The current brake notch. /// Whether the const speed system is enabled. public Handles(int reverser, int powerNotch, int brakeNotch, bool constSpeed) { this.MyReverser = reverser; this.MyPowerNotch = powerNotch; this.MyBrakeNotch = brakeNotch; this.MyConstSpeed = constSpeed; } } /// Represents data given to the plugin in the Elapse call. public class ElapseData { // --- members --- /// The state of the train. private VehicleState MyVehicle; /// The state of the preceding train, or a null reference if there is no preceding train. private PrecedingVehicleState MyPrecedingVehicle; /// The virtual handles. private Handles MyHandles; /// The current absolute time. private Time MyTotalTime; /// The elapsed time since the last call to Elapse. private Time MyElapsedTime; /// The debug message the plugin wants the host application to display. private string MyDebugMessage; // --- constructors --- /// Creates a new instance of this class. /// The state of the train. /// The state of the preceding train, or a null reference if there is no preceding train. /// The virtual handles. /// The current absolute time. /// The elapsed time since the last call to Elapse. public ElapseData(VehicleState vehicle, PrecedingVehicleState precedingVehicle, Handles handles, Time totalTime, Time elapsedTime) { this.MyVehicle = vehicle; this.MyPrecedingVehicle = precedingVehicle; this.MyHandles = handles; this.MyTotalTime = totalTime; this.MyElapsedTime = elapsedTime; this.MyDebugMessage = null; } // --- properties --- /// Gets the state of the train. public VehicleState Vehicle { get { return this.MyVehicle; } } /// Gets the state of the preceding train, or a null reference if there is no preceding train. public PrecedingVehicleState PrecedingVehicle { get { return this.MyPrecedingVehicle; } } /// Gets or sets the virtual handles. public Handles Handles { get { return this.MyHandles; } set { this.MyHandles = value; } } /// Gets the absolute in-game time. public Time TotalTime { get { return this.MyTotalTime; } } /// Gets the time that elapsed since the last call to Elapse. public Time ElapsedTime { get { return this.MyElapsedTime; } } /// Gets or sets the debug message the plugin wants the host application to display. public string DebugMessage { get { return this.MyDebugMessage; } set { this.MyDebugMessage = value; } } } // --- key down / key up --- /// Represents a virtual key. public enum VirtualKeys { /// The virtual S key. The default assignment is [Space]. The numerical value of this constant is 0. S = 0, /// The virtual A1 key. The default assignment is [Insert]. The numerical value of this constant is 1. A1 = 1, /// The virtual A2 key. The default assignment is [Delete]. The numerical value of this constant is 2. A2 = 2, /// The virtual B1 key. The default assignment is [Home]. The numerical value of this constant is 3. B1 = 3, /// The virtual B2 key. The default assignment is [End]. The numerical value of this constant is 4. B2 = 4, /// The virtual C1 key. The default assignment is [PageUp]. The numerical value of this constant is 5. C1 = 5, /// The virtual C2 key. The default assignment is [PageDown]. The numerical value of this constant is 6. C2 = 6, /// The virtual D key. The default assignment is [2]. The numerical value of this constant is 7. D = 7, /// The virtual E key. The default assignment is [3]. The numerical value of this constant is 8. E = 8, /// The virtual F key. The default assignment is [4]. The numerical value of this constant is 9. F = 9, /// The virtual G key. The default assignment is [5]. The numerical value of this constant is 10. G = 10, /// The virtual H key. The default assignment is [6]. The numerical value of this constant is 11. H = 11, /// The virtual I key. The default assignment is [7]. The numerical value of this constant is 12. I = 12, /// The virtual J key. The default assignment is [8]. The numerical value of this constant is 13. J = 13, /// The virtual K key. The default assignment is [9]. The numerical value of this constant is 14. K = 14, /// The virtual L key. The default assignment is [0]. The numerical value of this constant is 15. L = 15 } // --- horn blow --- /// Represents the type of horn. public enum HornTypes { /// The primary horn. The numerical value of this constant is 0. Primary = 1, /// The secondary horn. The numerical value of this constant is 1. Secondary = 2, /// The music horn. The numerical value of this constant is 2. Music = 3 } // --- door change --- /// Represents the state of the doors. public enum DoorStates { /// No door is open. None = 0, /// The left doors are open. Left = 1, /// The right doors are open. Right = 2, /// All doors are open. Both = 3 } // --- set signal --- /// Represents information about a signal or section. public class SignalData { // --- members --- /// The aspect of the signal or section. private int MyAspect; /// The underlying section. Possible values are 0 for the current section, 1 for the upcoming section, or higher values for sections further ahead. private double MyDistance; // --- properties --- /// Gets the aspect of the signal or section. public int Aspect { get { return this.MyAspect; } } /// Gets the distance to the signal or section. public double Distance { get { return this.MyDistance; } } // --- constructors --- /// Creates a new instance of this class. /// The aspect of the signal or section. /// The distance to the signal or section. public SignalData(int aspect, double distance) { this.MyAspect = aspect; this.MyDistance = distance; } } // --- set beacon --- /// Represents data trasmitted by a beacon. public class BeaconData { // --- members --- /// The type of beacon. private int MyType; /// Optional data the beacon transmits. private int MyOptional; /// The section the beacon is attached to. private SignalData MySignal; // --- properties --- /// Gets the type of beacon. public int Type { get { return this.MyType; } } /// Gets optional data the beacon transmits. public int Optional { get { return this.MyOptional; } } /// Gets the section the beacon is attached to. public SignalData Signal { get { return this.MySignal; } } // --- constructors --- /// Creates a new instance of this class. /// The type of beacon. /// Optional data the beacon transmits. /// The section the beacon is attached to. public BeaconData(int type, int optional, SignalData signal) { this.MyType = type; this.MyOptional = optional; this.MySignal = signal; } } // --- perform AI --- /// Represents responses by the AI. public enum AIResponse { /// No action was performed by the plugin. None = 0, /// The action performed took a short time. Short = 1, /// The action performed took an average amount of time. Medium = 2, /// The action performed took a long time. Long = 3 } /// Represents AI data. public class AIData { // --- members --- /// The driver handles. private Handles MyHandles; /// The AI response. private AIResponse MyResponse; // --- constructors --- /// Creates a new instance of this class. /// The driver handles. public AIData(Handles handles) { this.MyHandles = handles; this.MyResponse = AIResponse.None; } // --- properties --- /// Gets or sets the driver handles. public Handles Handles { get { return this.MyHandles; } set { this.MyHandles = value; } } /// Gets or sets the AI response. public AIResponse Response { get { return this.MyResponse; } set { this.MyResponse = value; } } } // --- interfaces --- /// Represents the interface for performing runtime train services. public interface IRuntime { /// Is called when the plugin is loaded. /// The properties supplied to the plugin on loading. /// Whether the plugin was loaded successfully. /// If the plugin was not loaded successfully, the plugin should set the Reason property to supply the reason of failure. bool Load(LoadProperties properties); /// Is called when the plugin is unloaded. void Unload(); /// Is called after loading to inform the plugin about the specifications of the train. /// The specifications of the train. void SetVehicleSpecs(VehicleSpecs specs); /// Is called when the plugin should initialize or reinitialize. /// The mode of initialization. void Initialize(InitializationModes mode); /// Is called every frame. /// The data passed to the plugin. void Elapse(ElapseData data); /// Is called when the driver changes the reverser. /// The new reverser position. void SetReverser(int reverser); /// Is called when the driver changes the power notch. /// The new power notch. void SetPower(int powerNotch); /// Is called when the driver changes the brake notch. /// The new brake notch. void SetBrake(int brakeNotch); /// Is called when a virtual key is pressed. /// The virtual key that was pressed. void KeyDown(VirtualKeys key); /// Is called when a virtual key is released. /// The virtual key that was released. void KeyUp(VirtualKeys key); /// Is called when a horn is played or when the music horn is stopped. /// The type of horn. void HornBlow(HornTypes type); /// Is called when the state of the doors changes. /// The old state of the doors. /// The new state of the doors. void DoorChange(DoorStates oldState, DoorStates newState); /// Is called when the aspect in the current or in any of the upcoming sections changes, or when passing section boundaries. /// Signal information per section. In the array, index 0 is the current section, index 1 the upcoming section, and so on. /// The signal array is guaranteed to have at least one element. When accessing elements other than index 0, you must check the bounds of the array first. void SetSignal(SignalData[] data); /// Is called when the train passes a beacon. /// The beacon data. void SetBeacon(BeaconData data); /// Is called when the plugin should perform the AI. /// The AI data. void PerformAI(AIData data); } }openbve-1.4.0.10/openBVE/OpenBveApi/SharedMesh.cs000066400000000000000000000240031171674032100212320ustar00rootroot00000000000000using System; using OpenBveApi.Math; namespace OpenBveApi.Objects { /// Represents a vertex used in a shared mesh. public struct SharedVertex { // --- members --- /// A reference to the list of spatial coordinates in the underlying shared mesh. public int SpatialCoordinates; /// A reference to the list of texture coordinates in the underlying shared mesh. public int TextureCoordinates; /// A reference to the list of normals in the underlying shared mesh. public int Normal; } /// Represents a face used in a shared mesh. public struct SharedFace { // --- members --- /// The vertices of this face. public SharedVertex[] Vertices; /// The material used by this face. public AbstractMaterial Material; // --- functions --- /// Flips the face. public void Flip() { Array.Reverse(this.Vertices); } } /// Represents a mesh with coordinates shared between faces. public class SharedMesh : StaticObject { // --- members --- /// The list of unique spatial coordinates. public Vector3[] SpatialCoordinates; /// The list of unique texture coordinates. public Vector2[] TextureCoordinates; /// The list of unique normals. public Vector3[] Normals; /// The faces stored in this mesh. public SharedFace[] Faces; // --- functions --- /// Translates the object by the specified offset. /// The offset by which to translate. public override void Translate(Vector3 offset) { for (int i = 0; i < this.SpatialCoordinates.Length; i++) { this.SpatialCoordinates[i].Translate(offset); } } /// Translates the object by the specified offset that is measured in the specified orientation. /// The orientation along which to translate. /// The offset measured in the specified orientation. public override void Translate(Orientation3 orientation, Vector3 offset) { for (int i = 0; i < this.SpatialCoordinates.Length; i++) { this.SpatialCoordinates[i].Translate(orientation, offset); } } /// Rotates the object around the specified axis. /// The axis along which to rotate. /// The cosine of the angle by which to rotate. /// The sine of the angle by which to rotate. public override void Rotate(Vector3 direction, double cosineOfAngle, double sineOfAngle) { for (int i = 0; i < this.SpatialCoordinates.Length; i++) { this.SpatialCoordinates[i].Rotate(direction, cosineOfAngle, sineOfAngle); } for (int i = 0; i < this.Normals.Length; i++) { this.Normals[i].Rotate(direction, cosineOfAngle, sineOfAngle); } } /// Rotates the object from the default orientation into the specified orientation. /// The target orientation. /// The default orientation is X = {1, 0, 0), Y = {0, 1, 0} and Z = {0, 0, 1}. public override void Rotate(Orientation3 orientation) { for (int i = 0; i < this.SpatialCoordinates.Length; i++) { this.SpatialCoordinates[i].Rotate(orientation); } for (int i = 0; i < this.Normals.Length; i++) { this.Normals[i].Rotate(orientation); } } /// Scales the object by the specified factor. /// The factor by which to scale. /// Raised when any component in the factor is zero. public override void Scale(Vector3 factor) { if (factor.X == 0.0 | factor.Y == 0.0 | factor.Z == 0.0) { throw new ArgumentException("The factor contains components that are zero."); } for (int i = 0; i < this.SpatialCoordinates.Length; i++) { this.SpatialCoordinates[i].Scale(factor); } double inverseFactorX = 1.0 / factor.X; double inverseFactorY = 1.0 / factor.Y; double inverseFactorZ = 1.0 / factor.Z; double inverseFactorSquaredX = inverseFactorX * inverseFactorX; double inverseFactorSquaredY = inverseFactorY * inverseFactorY; double inverseFactorSquaredZ = inverseFactorZ * inverseFactorZ; for (int i = 0; i < this.Normals.Length; i++) { double normalSquaredX = this.Normals[i].X * this.Normals[i].X; double normalSquaredY = this.Normals[i].Y * this.Normals[i].Y; double normalSquaredZ = this.Normals[i].Z * this.Normals[i].Z; double norm = normalSquaredX * inverseFactorSquaredX + normalSquaredY * inverseFactorSquaredY + normalSquaredZ * inverseFactorSquaredZ; if (norm != 0.0) { double scalar = System.Math.Sqrt((normalSquaredX + normalSquaredY + normalSquaredZ) / norm); this.Normals[i].X *= inverseFactorX * scalar; this.Normals[i].Y *= inverseFactorY * scalar; this.Normals[i].Z *= inverseFactorZ * scalar; } } if (factor.X * factor.Y * factor.Z < 0.0) { for (int i = 0; i < this.Faces.Length; i++) { this.Faces[i].Flip(); } } } // --- optimization --- /// Removes all duplicate and unused coordinates. public void Optimize() { OptimizeSpatialCoordinates(); OptimizeTextureCoordinates(); OptimizeNormals(); } /// Removes all duplicate and unused spatial coordinates. private void OptimizeSpatialCoordinates() { /* Eliminate duplicate references */ for (int i = 0; i < this.SpatialCoordinates.Length; i++) { for (int j = 0; j < i; j++) { if (this.SpatialCoordinates[i] == this.SpatialCoordinates[j]) { for (int k = 0; k < this.Faces.Length; k++) { for (int l = 0; l < this.Faces[k].Vertices.Length; l++) { if (this.Faces[k].Vertices[l].SpatialCoordinates == j) { this.Faces[k].Vertices[l].SpatialCoordinates = i; } } } break; } } } /* Eliminate unused elements */ int length = this.SpatialCoordinates.Length; for (int i = 0; i < length; i++) { bool remove = true; for (int k = 0; k < this.Faces.Length; k++) { for (int l = 0; l < this.Faces[k].Vertices.Length; l++) { if (this.Faces[k].Vertices[l].SpatialCoordinates == i) { remove = false; break; } } if (!remove) break; } if (remove) { this.SpatialCoordinates[i] = this.SpatialCoordinates[length - 1]; for (int k = 0; k < this.Faces.Length; k++) { for (int l = 0; l < this.Faces[k].Vertices.Length; l++) { if (this.Faces[k].Vertices[l].SpatialCoordinates == length - 1) { this.Faces[k].Vertices[l].SpatialCoordinates = i; } } } length--; i--; } } if (length != this.SpatialCoordinates.Length) { Array.Resize(ref this.SpatialCoordinates, length); } } /// Removes all duplicate and unused texture coordinates. private void OptimizeTextureCoordinates() { /* Eliminate duplicate references */ for (int i = 0; i < this.TextureCoordinates.Length; i++) { for (int j = 0; j < i; j++) { if (this.TextureCoordinates[i] == this.TextureCoordinates[j]) { for (int k = 0; k < this.Faces.Length; k++) { for (int l = 0; l < this.Faces[k].Vertices.Length; l++) { if (this.Faces[k].Vertices[l].TextureCoordinates == j) { this.Faces[k].Vertices[l].TextureCoordinates = i; } } } break; } } } /* Eliminate unused elements */ int length = this.TextureCoordinates.Length; for (int i = 0; i < length; i++) { bool remove = true; for (int k = 0; k < this.Faces.Length; k++) { for (int l = 0; l < this.Faces[k].Vertices.Length; l++) { if (this.Faces[k].Vertices[l].TextureCoordinates == i) { remove = false; break; } } if (!remove) break; } if (remove) { this.TextureCoordinates[i] = this.TextureCoordinates[length - 1]; for (int k = 0; k < this.Faces.Length; k++) { for (int l = 0; l < this.Faces[k].Vertices.Length; l++) { if (this.Faces[k].Vertices[l].TextureCoordinates == length - 1) { this.Faces[k].Vertices[l].TextureCoordinates = i; } } } length--; i--; } } if (length != this.TextureCoordinates.Length) { Array.Resize(ref this.TextureCoordinates, length); } } /// Removes all duplicate and unused normals. private void OptimizeNormals() { /* Eliminate duplicate references */ for (int i = 0; i < this.Normals.Length; i++) { for (int j = 0; j < i; j++) { if (this.Normals[i] == this.Normals[j]) { for (int k = 0; k < this.Faces.Length; k++) { for (int l = 0; l < this.Faces[k].Vertices.Length; l++) { if (this.Faces[k].Vertices[l].Normal == j) { this.Faces[k].Vertices[l].Normal = i; } } } break; } } } /* Eliminate unused elements */ int length = this.Normals.Length; for (int i = 0; i < length; i++) { bool remove = true; for (int k = 0; k < this.Faces.Length; k++) { for (int l = 0; l < this.Faces[k].Vertices.Length; l++) { if (this.Faces[k].Vertices[l].Normal == i) { remove = false; break; } } if (!remove) break; } if (remove) { this.Normals[i] = this.Normals[length - 1]; for (int k = 0; k < this.Faces.Length; k++) { for (int l = 0; l < this.Faces[k].Vertices.Length; l++) { if (this.Faces[k].Vertices[l].Normal == length - 1) { this.Faces[k].Vertices[l].Normal = i; } } } length--; i--; } } if (length != this.Normals.Length) { Array.Resize(ref this.Normals, length); } } } }openbve-1.4.0.10/openBVE/OpenBveApi/Sound.cs000066400000000000000000000100431171674032100202760ustar00rootroot00000000000000using System; namespace OpenBveApi.Sound { // --- structures --- /// Represents a sound. public class Sound { // --- members --- /// The number of samples per second. private int MySampleRate; /// The number of bits per sample. Allowed values are 8 or 16. private int MyBitsPerSample; /// The PCM sound data per channel. For 8 bits per sample, samples are unsigned from 0 to 255. For 16 bits per sample, samples are signed from -32768 to 32767 and in little endian byte order. private byte[][] MyBytes; // --- constructors --- /// Creates a new instance of this class. /// The number of samples per second. /// The number of bits per sample. Allowed values are 8 or 16. /// The PCM sound data per channel. For 8 bits per sample, samples are unsigned from 0 to 255. For 16 bits per sample, samples are signed from -32768 to 32767 and in little endian byte order. /// Raised when the bytes array or any of its subarrays is a null reference. /// Raised when the bytes' subarrays are of unequal length. /// Raised when the number of bits per samples is neither 8 nor 16. public Sound(int sampleRate, int bitsPerSample, byte[][] bytes) { if (bytes == null) { throw new ArgumentNullException("The data bytes are a null reference."); } for (int i = 0; i < bytes.Length; i++) { if (bytes[i] == null) { throw new ArgumentNullException("The data bytes of a particular channel is a null reference."); } } for (int i = 1; i < bytes.Length; i++) { if (bytes[i].Length != bytes[0].Length) { throw new ArgumentException("The data bytes of the channels are of unequal length."); } } if (bitsPerSample != 8 & bitsPerSample != 16) { throw new ArgumentException("The number of bits per sample is neither 8 nor 16."); } else { this.MySampleRate = sampleRate; this.MyBitsPerSample = bitsPerSample; this.MyBytes = bytes; } } // --- properties --- /// Gets the number of samples per second. public int SampleRate { get { return this.MySampleRate; } } /// Gets the number of bits per sample. Allowed values are 8 or 16. public int BitsPerSample { get { return this.MyBitsPerSample; } } /// Gets the PCM sound data per channel. For 8 bits per sample, samples are unsigned from 0 to 255. For 16 bits per sample, samples are signed from -32768 to 32767 and in little endian byte order. public byte[][] Bytes { get { return this.MyBytes; } } } // --- interfaces --- /// Represents the interface for loading sounds. public interface ISound { /// Is called to check whether the plugin can load the specified sound. /// The file to the sound. /// Additional information that describes how to process the file, or a null reference. /// Whether the plugin can load the specified sound. /// The plugin should only inspect file extensions, identifiers or headers. It should not perform a full file validation. bool CanLoadSound(string file, string optional); /// Is called to let the plugin load the specified sound. /// The file to the sound. /// Additional information that describes how to process the file, or a null reference. /// Receives the sound on success. /// Whether the plugin succeeded in loading the sound. bool LoadSound(string file, string optional, out Sound sound); } }openbve-1.4.0.10/openBVE/OpenBveApi/Sounds.cs000066400000000000000000000173041171674032100204700ustar00rootroot00000000000000#pragma warning disable 0659, 0661 using System; namespace OpenBveApi.Sounds { /* ---------------------------------------- * TODO: This part of the API is unstable. * Modifications can be made at will. * ---------------------------------------- */ // --- structures --- /// Represents a sound. public class Sound { // --- members --- /// The number of samples per second. private int MySampleRate; /// The number of bits per sample. Allowed values are 8 or 16. private int MyBitsPerSample; /// The PCM sound data per channel. For 8 bits per sample, samples are unsigned from 0 to 255. For 16 bits per sample, samples are signed from -32768 to 32767 and in little endian byte order. private byte[][] MyBytes; // --- constructors --- /// Creates a new instance of this class. /// The number of samples per second. /// The number of bits per sample. Allowed values are 8 or 16. /// The PCM sound data per channel. For 8 bits per sample, samples are unsigned from 0 to 255. For 16 bits per sample, samples are signed from -32768 to 32767 and in little endian byte order. /// Raised when the number of samples per second is not positive. /// Raised when the number of bits per samples is neither 8 nor 16. /// Raised when the bytes array or any of its subarrays is a null reference. /// Raised when the bytes array does not contain any elements. /// Raised when the bytes' subarrays are of unequal length. public Sound(int sampleRate, int bitsPerSample, byte[][] bytes) { if (sampleRate <= 0) { throw new ArgumentException("The sample rate must be positive."); } if (bitsPerSample != 8 & bitsPerSample != 16) { throw new ArgumentException("The number of bits per sample is neither 8 nor 16."); } if (bytes == null) { throw new ArgumentNullException("The data bytes are a null reference."); } if (bytes.Length == 0) { throw new ArgumentException("There must be at least one channel."); } for (int i = 0; i < bytes.Length; i++) { if (bytes[i] == null) { throw new ArgumentNullException("The data bytes channel " + i.ToString() + " are a null reference."); } } for (int i = 1; i < bytes.Length; i++) { if (bytes[i].Length != bytes[0].Length) { throw new ArgumentException("The data bytes of the channels are of unequal length."); } } this.MySampleRate = sampleRate; this.MyBitsPerSample = bitsPerSample; this.MyBytes = bytes; } // --- properties --- /// Gets the number of samples per second. public int SampleRate { get { return this.MySampleRate; } } /// Gets the number of bits per sample. Allowed values are 8 or 16. public int BitsPerSample { get { return this.MyBitsPerSample; } } /// Gets the PCM sound data per channel. For 8 bits per sample, samples are unsigned from 0 to 255. For 16 bits per sample, samples are signed from -32768 to 32767 and in little endian byte order. public byte[][] Bytes { get { return this.MyBytes; } } /// Gets the duration of the sound in seconds. public double Duration { get { return (double)(8 * this.MyBytes[0].Length / this.MyBitsPerSample) / (double)this.MySampleRate; } } // --- operators --- /// Checks whether two sound are equal. /// The first sound. /// The second sound. /// Whether the two sounds are equal. public static bool operator ==(Sound a, Sound b) { if (object.ReferenceEquals(a, b)) return true; if (object.ReferenceEquals(a, null)) return false; if (object.ReferenceEquals(b, null)) return false; if (a.MySampleRate != b.MySampleRate) return false; if (a.MyBitsPerSample != b.MyBitsPerSample) return false; if (a.MyBytes.Length != b.MyBytes.Length) return false; for (int i = 0; i < a.MyBytes.Length; i++) { if (a.MyBytes[i].Length != b.MyBytes[i].Length) return false; for (int j = 0; j < a.MyBytes[i].Length; j++) { if (a.MyBytes[i][j] != b.MyBytes[i][j]) return false; } } return true; } /// Checks whether two sounds are unequal. /// The first sound. /// The second sound. /// Whether the two sounds are unequal. public static bool operator !=(Sound a, Sound b) { if (object.ReferenceEquals(a, b)) return false; if (object.ReferenceEquals(a, null)) return true; if (object.ReferenceEquals(b, null)) return true; if (a.MySampleRate != b.MySampleRate) return true; if (a.MyBitsPerSample != b.MyBitsPerSample) return true; if (a.MyBytes.Length != b.MyBytes.Length) return true; for (int i = 0; i < a.MyBytes.Length; i++) { if (a.MyBytes[i].Length != b.MyBytes[i].Length) return true; for (int j = 0; j < a.MyBytes[i].Length; j++) { if (a.MyBytes[i][j] != b.MyBytes[i][j]) return true; } } return false; } /// Checks whether this instance is equal to the specified object. /// The object. /// Whether this instance is equal to the specified object. public override bool Equals(object obj) { if (object.ReferenceEquals(this, obj)) return true; if (object.ReferenceEquals(this, null)) return false; if (object.ReferenceEquals(obj, null)) return false; if (!(obj is Sound)) return false; Sound x = (Sound)obj; if (this.MySampleRate != x.MySampleRate) return false; if (this.MyBitsPerSample != x.MyBitsPerSample) return false; if (this.MyBytes.Length != x.MyBytes.Length) return false; for (int i = 0; i < this.MyBytes.Length; i++) { if (this.MyBytes[i].Length != x.MyBytes[i].Length) return false; for (int j = 0; j < this.MyBytes[i].Length; j++) { if (this.MyBytes[i][j] != x.MyBytes[i][j]) return false; } } return true; } } // --- handles --- /// Represents a handle to a sound. public abstract class SoundHandle { } // --- interfaces --- /// Represents the interface for loading sounds. Plugins must implement this interface if they wish to expose sounds. public abstract class SoundInterface { /// Called when the plugin is loaded. /// The host that loaded the plugin. public virtual void Load(Hosts.HostInterface host) { } /// Called when the plugin is unloaded. public virtual void Unload() { } /// Checks whether the plugin can load the specified sound. /// The path to the file or folder that contains the sound. /// Whether the plugin can load the specified sound. public abstract bool CanLoadSound(string path); /// Loads the specified sound. /// The path to the file or folder that contains the sound. /// Receives the sound. /// Whether loading the sound was successful. public abstract bool LoadSound(string path, out Sound sound); } }openbve-1.4.0.10/openBVE/OpenBveApi/Texture.cs000066400000000000000000000065451171674032100206620ustar00rootroot00000000000000using System; namespace OpenBveApi.Texture { // --- structures --- /// Represents a texture. public class Texture { // --- members --- /// The width of the texture in pixels. private int MyWidth; /// The height of the texture in pixels. private int MyHeight; /// The RGBA texture data. Pixels are stored row-based from top to bottom, and within a row from left to right. All pixels take four bytes in the order red, green, blue, alpha. private byte[] MyBytes; // --- constructors --- /// Creates a new instance of this class. /// The width of the texture in pixels. /// The height of the texture in pixels. /// The RGBA texture data. Pixels are stored row-based from top to bottom, and within a row from left to right. All pixels take four bytes in the order red, green, blue, alpha. /// Raised when the byte array is a null reference. /// Raised when the length of the byte array is not 4 * width * height. public Texture(int width, int height, byte[] bytes) { if (bytes != null) { throw new ArgumentNullException("The data bytes are a null reference."); } else if (4 * width * height != bytes.Length) { throw new ArgumentException("The data bytes are not of the expected length."); } else { this.MyWidth = width; this.MyHeight = height; this.MyBytes = bytes; } } // properties /// Gets the width of the texture in pixels. public int Width { get { return this.MyWidth; } } /// Gets the height of the texture in pixels. public int Height { get { return this.MyHeight; } } /// Gets the RGBA texture data. Pixels are stored row-based from top to bottom, and within a row from left to right. All pixels take four bytes in the order red, green, blue, alpha. public byte[] Bytes { get { return this.MyBytes; } } } // --- interfaces --- /// Represents the interface for loading textures. public interface ITexture { /// Is called to check whether the plugin can load the specified texture. /// The file to the texture. /// Additional information that describes how to process the file, or a null reference. /// Whether the plugin can load the specified texture. /// The plugin should only inspect file extensions, identifiers or headers. It should not perform a full file validation. bool CanLoadTexture(string file, string optional); /// Is called to let the plugin load the specified texture. /// The file to the texture. /// Additional information that describes how to process the file, or a null reference. /// Receives the texture on success. /// Whether the plugin succeeded in loading the texture. bool LoadTexture(string file, string optional, out Texture texture); } }openbve-1.4.0.10/openBVE/OpenBveApi/Textures.Functions.cs000066400000000000000000000123461171674032100230100ustar00rootroot00000000000000using System; using OpenBveApi.Colors; namespace OpenBveApi.Textures { /// Provides functions for manipulating textures. internal static class Functions { // --- apply parameters --- /// Applies parameters onto a texture. /// The original texture. /// The parameters, or a null reference. /// The texture with the parameters applied. /// Raised when the clip region is outside the texture bounds. /// Raised when the bits per pixel in the texture is other than 32. internal static Texture ApplyParameters(Texture texture, TextureParameters parameters) { Texture result = texture; if (parameters != null) { if (parameters.ClipRegion != null) { result = ExtractClipRegion(result, parameters.ClipRegion); } if (parameters.TransparentColor != null) { result = ApplyTransparentColor(result, parameters.TransparentColor); } } return result; } // --- extract clip region --- /// Extracts a clip region from a texture. /// The original texture. /// The clip region, or a null reference. /// The texture with the extracted clip region. /// Raised when the clip region is outside the texture bounds. /// Raised when the number of bits per pixel in the texture is not supported. internal static Texture ExtractClipRegion(Texture texture, TextureClipRegion region) { if (region == null || region.Left == 0 && region.Top == 0 && region.Width == texture.Width && region.Height == texture.Height) { return texture; } else if (region.Left < 0 || region.Top < 0 || region.Width <= 0 || region.Height <= 0 || region.Left + region.Width > texture.Width || region.Top + region.Height > texture.Height) { throw new ArgumentException(); } else if (texture.BitsPerPixel == 24 | texture.BitsPerPixel == 32) { int width = texture.Width; int height = texture.Height; byte[] bytes = texture.Bytes; int clipLeft = region.Left; int clipTop = region.Top; int clipWidth = region.Width; int clipHeight = region.Height; if (texture.BitsPerPixel == 24) { byte[] newBytes = new byte[3 * clipWidth * clipHeight]; int i = 0; for (int y = 0; y < clipHeight; y++) { int j = 3 * width * (clipTop + y) + 3 * clipLeft; for (int x = 0; x < clipWidth; x++) { newBytes[i + 0] = bytes[j + 0]; newBytes[i + 1] = bytes[j + 1]; newBytes[i + 2] = bytes[j + 2]; i += 3; j += 3; } } return new Texture(clipWidth, clipHeight, 24, newBytes); } else { byte[] newBytes = new byte[4 * clipWidth * clipHeight]; int i = 0; for (int y = 0; y < clipHeight; y++) { int j = 4 * width * (clipTop + y) + 4 * clipLeft; for (int x = 0; x < clipWidth; x++) { newBytes[i + 0] = bytes[j + 0]; newBytes[i + 1] = bytes[j + 1]; newBytes[i + 2] = bytes[j + 2]; newBytes[i + 3] = bytes[j + 3]; i += 4; j += 4; } } return new Texture(clipWidth, clipHeight, 32, newBytes); } } else { throw new NotSupportedException(); } } // --- apply transparent color --- /// Applies a transparent color onto a texture. /// The original texture. /// The transparent color, or a null reference. /// The texture with the transparent color applied. /// Raised when the number of bits per pixel in the texture is not supported. internal static Texture ApplyTransparentColor(Texture texture, Color24? color) { if (color == null) { return texture; } else if (texture.BitsPerPixel == 32) { int width = texture.Width; int height = texture.Height; byte[] source = texture.Bytes; byte[] target = new byte[4 * width * height]; byte r = color.Value.R; byte g = color.Value.G; byte b = color.Value.B; if (source[0] == r && source[1] == g && source[2] == b) { target[0] = 128; target[1] = 128; target[2] = 128; target[3] = 0; } else { target[0] = source[0]; target[1] = source[1]; target[2] = source[2]; target[3] = source[3]; } for (int i = 4; i < source.Length; i += 4) { if (source[i] == r && source[i + 1] == g && source[i + 2] == b) { target[i + 0] = target[i - 4]; target[i + 1] = target[i - 3]; target[i + 2] = target[i - 2]; target[i + 3] = 0; } else { target[i + 0] = source[i + 0]; target[i + 1] = source[i + 1]; target[i + 2] = source[i + 2]; target[i + 3] = source[i + 3]; } } return new Texture(width, height, 32, target); } else { throw new NotSupportedException(); } } } }openbve-1.4.0.10/openBVE/OpenBveApi/Textures.cs000066400000000000000000000410331171674032100210340ustar00rootroot00000000000000#pragma warning disable 0659, 0661 using System; using OpenBveApi.Colors; namespace OpenBveApi.Textures { /* ---------------------------------------- * TODO: This part of the API is unstable. * Modifications can be made at will. * ---------------------------------------- */ // --- structures --- /// Represents the type of transparency encountered in a texture. public enum TextureTransparencyType { /// All pixels in the texture are fully opaque. Opaque = 1, /// All pixels in the texture are either fully opaque or fully transparent. Partial = 2, /// Some pixels in the texture are neither fully opaque nor fully transparent. Alpha = 3 } /// Represents a texture. public class Texture { // --- members --- /// The width of the texture in pixels. private int MyWidth; /// The height of the texture in pixels. private int MyHeight; /// The number of bits per pixel. Must be 32. private int MyBitsPerPixel; /// The texture data. Pixels are stored row-based from top to bottom, and within a row from left to right. For 32 bits per pixel, four bytes are used in the order red, green, blue and alpha. private byte[] MyBytes; // --- constructors --- /// Creates a new instance of this class. /// The width of the texture in pixels. /// The height of the texture in pixels. /// The number of bits per pixel. Must be 32. /// The texture data. Pixels are stored row-based from top to bottom, and within a row from left to right. For 32 bits per pixel, four bytes are used in the order red, green, blue and alpha. /// Raised when the number of bits per pixel is not 32. /// Raised when the byte array is a null reference. /// Raised when the byte array is of unexpected length. public Texture(int width, int height, int bitsPerPixel, byte[] bytes) { if (bitsPerPixel != 32) { throw new ArgumentException("The number of bits per pixel is supported."); } else if (bytes == null) { throw new ArgumentNullException("The data bytes are a null reference."); } else if (bytes.Length != 4 * width * height) { throw new ArgumentException("The data bytes are not of the expected length."); } else { this.MyWidth = width; this.MyHeight = height; this.MyBitsPerPixel = bitsPerPixel; this.MyBytes = bytes; } } // --- properties --- /// Gets the width of the texture in pixels. public int Width { get { return this.MyWidth; } } /// Gets the height of the texture in pixels. public int Height { get { return this.MyHeight; } } /// Gets the number of bits per pixel. public int BitsPerPixel { get { return this.MyBitsPerPixel; } } /// Gets the texture data. Pixels are stored row-based from top to bottom, and within a row from left to right. For 32 bits per pixel, four bytes are used in the order red, green, blue and alpha. public byte[] Bytes { get { return this.MyBytes; } } // --- operators --- /// Checks whether two textures are equal. /// The first texture. /// The second texture. /// Whether the two textures are equal. public static bool operator ==(Texture a, Texture b) { if (object.ReferenceEquals(a, b)) return true; if (object.ReferenceEquals(a, null)) return false; if (object.ReferenceEquals(b, null)) return false; if (a.MyWidth != b.MyWidth) return false; if (a.MyHeight != b.MyHeight) return false; if (a.MyBitsPerPixel != b.MyBitsPerPixel) return false; if (a.MyBytes.Length != b.MyBytes.Length) return false; for (int i = 0; i < a.MyBytes.Length; i++) { if (a.MyBytes[i] != b.MyBytes[i]) return false; } return true; } /// Checks whether two textures are unequal. /// The first texture. /// The second texture. /// Whether the two textures are unequal. public static bool operator !=(Texture a, Texture b) { if (object.ReferenceEquals(a, b)) return false; if (object.ReferenceEquals(a, null)) return true; if (object.ReferenceEquals(b, null)) return true; if (a.MyWidth != b.MyWidth) return true; if (a.MyHeight != b.MyHeight) return true; if (a.MyBitsPerPixel != b.MyBitsPerPixel) return true; if (a.MyBytes.Length != b.MyBytes.Length) return true; for (int i = 0; i < a.MyBytes.Length; i++) { if (a.MyBytes[i] != b.MyBytes[i]) return true; } return false; } /// Checks whether this instance is equal to the specified object. /// The object. /// Whether this instance is equal to the specified object. public override bool Equals(object obj) { if (object.ReferenceEquals(this, obj)) return true; if (object.ReferenceEquals(this, null)) return false; if (object.ReferenceEquals(obj, null)) return false; if (!(obj is Texture)) return false; Texture x = (Texture)obj; if (this.MyWidth != x.MyWidth) return false; if (this.MyHeight != x.MyHeight) return false; if (this.MyBitsPerPixel != x.MyBitsPerPixel) return false; if (this.MyBytes.Length != x.MyBytes.Length) return false; for (int i = 0; i < this.MyBytes.Length; i++) { if (this.MyBytes[i] != x.MyBytes[i]) return false; } return true; } // --- functions --- /// Applies the specified parameters onto this texture. /// The parameters, or a null reference. /// The texture with the parameters applied. /// Raised when the clip region is outside the texture bounds. /// Raised when the bits per pixel in the texture is not supported. public Texture ApplyParameters(TextureParameters parameters) { return Functions.ApplyParameters(this, parameters); } /// Gets the type of transparency encountered in this texture. /// The type of transparency encountered in this texture. /// Raised when the bits per pixel in the texture is not supported. public TextureTransparencyType GetTransparencyType() { if (this.MyBitsPerPixel == 24) { return TextureTransparencyType.Opaque; } else if (this.MyBitsPerPixel == 32) { for (int i = 3; i < this.MyBytes.Length; i += 4) { if (this.MyBytes[i] != 255) { for (int j = i; j < this.MyBytes.Length; j += 4) { if (this.MyBytes[j] != 0 & this.MyBytes[j] != 255) { return TextureTransparencyType.Alpha; } } return TextureTransparencyType.Partial; } } return TextureTransparencyType.Opaque; } else { throw new NotSupportedException(); } } } // --- handles --- /// Represents a handle to a texture. public abstract class TextureHandle { } // --- clip region --- /// Represents a region in a texture to be extracted. public class TextureClipRegion { // --- members --- /// The left coordinate. private int MyLeft; /// The top coordinate. private int MyTop; /// The width. private int MyWidth; /// The height. private int MyHeight; // --- properties --- /// Gets the left coordinate. public int Left { get { return this.MyLeft; } } /// Gets the top coordinate. public int Top { get { return this.MyTop; } } /// Gets the width. public int Width { get { return this.MyWidth; } } /// Gets the height. public int Height { get { return this.MyHeight; } } // --- constructors --- /// Creates a new clip region. /// The left coordinate. /// The top coordinate. /// The width. /// The height. /// Raised when the left or top are negative. /// Raised when the width or height are non-positive. public TextureClipRegion(int left, int top, int width, int height) { if (left < 0 | top < 0) { throw new ArgumentException("The left or top coordinates are negative."); } else if (width <= 0 | height <= 0) { throw new ArgumentException("The width or height are non-positive."); } else { this.MyLeft = left; this.MyTop = top; this.MyWidth = width; this.MyHeight = height; } } // --- operators --- /// Checks whether two clip regions are equal. /// The first clip region. /// The second clip region. /// Whether the two clip regions are equal. public static bool operator ==(TextureClipRegion a, TextureClipRegion b) { if (object.ReferenceEquals(a, b)) return true; if (object.ReferenceEquals(a, null)) return false; if (object.ReferenceEquals(b, null)) return false; if (a.MyLeft != b.MyLeft) return false; if (a.MyTop != b.MyTop) return false; if (a.MyWidth != b.MyWidth) return false; if (a.MyHeight != b.MyHeight) return false; return true; } /// Checks whether two clip regions are unequal. /// The first clip region. /// The second clip region. /// Whether the two clip regions are unequal. public static bool operator !=(TextureClipRegion a, TextureClipRegion b) { if (object.ReferenceEquals(a, b)) return false; if (object.ReferenceEquals(a, null)) return true; if (object.ReferenceEquals(b, null)) return true; if (a.MyLeft != b.MyLeft) return true; if (a.MyTop != b.MyTop) return true; if (a.MyWidth != b.MyWidth) return true; if (a.MyHeight != b.MyHeight) return true; return false; } /// Checks whether this instance is equal to the specified object. /// The object. /// Whether this instance is equal to the specified object. public override bool Equals(object obj) { if (object.ReferenceEquals(this, obj)) return true; if (object.ReferenceEquals(this, null)) return false; if (object.ReferenceEquals(obj, null)) return false; if (!(obj is TextureClipRegion)) return false; TextureClipRegion x = (TextureClipRegion)obj; if (this.MyLeft != x.MyLeft) return false; if (this.MyTop != x.MyTop) return false; if (this.MyWidth != x.MyWidth) return false; if (this.MyHeight != x.MyHeight) return false; return true; } } // --- parameters --- /// Represents additional parameters that specify how to process the texture. public class TextureParameters { // --- members --- /// The region in the texture to be extracted, or a null reference for the entire texture. private TextureClipRegion MyClipRegion; /// The color in the texture that should become transparent, or a null reference for no transparent color. private Nullable MyTransparentColor; // --- properties --- /// Gets the region in the texture to be extracted, or a null reference for the entire texture. public TextureClipRegion ClipRegion { get { return this.MyClipRegion; } } /// Gets the color in the texture that should become transparent, or a null reference for no transparent color. public Color24? TransparentColor { get { return this.MyTransparentColor; } } // --- constructors --- /// Creates new texture parameters. /// The region in the texture to be extracted, or a null reference for the entire texture. /// The color in the texture that should become transparent, or a null reference for no transparent color. public TextureParameters(TextureClipRegion clipRegion, Nullable transparentColor) { this.MyClipRegion = clipRegion; this.MyTransparentColor = transparentColor; } // --- operators --- /// Checks whether two texture parameters are equal. /// The first texture parameter. /// The second texture parameter. /// Whether the two texture parameters are equal. public static bool operator ==(TextureParameters a, TextureParameters b) { if (object.ReferenceEquals(a, b)) return true; if (object.ReferenceEquals(a, null)) return false; if (object.ReferenceEquals(b, null)) return false; if (a.MyClipRegion != b.MyClipRegion) return false; if (a.MyTransparentColor != b.MyTransparentColor) return false; return true; } /// Checks whether two texture parameters are unequal. /// The first texture parameter. /// The second texture parameter. /// Whether the two texture parameters are unequal. public static bool operator !=(TextureParameters a, TextureParameters b) { if (object.ReferenceEquals(a, b)) return false; if (object.ReferenceEquals(a, null)) return true; if (object.ReferenceEquals(b, null)) return true; if (a.MyClipRegion != b.MyClipRegion) return true; if (a.MyTransparentColor != b.MyTransparentColor) return true; return false; } /// Checks whether this instance is equal to the specified object. /// The object. /// Whether this instance is equal to the specified object. public override bool Equals(object obj) { if (object.ReferenceEquals(this, obj)) return true; if (object.ReferenceEquals(this, null)) return false; if (object.ReferenceEquals(obj, null)) return false; if (!(obj is TextureParameters)) return false; TextureParameters x = (TextureParameters)obj; if (this.MyClipRegion != x.MyClipRegion) return false; if (this.MyTransparentColor != x.MyTransparentColor) return false; return true; } } // --- interfaces --- /// Represents the interface for loading textures. Plugins must implement this interface if they wish to expose textures. public abstract class TextureInterface { /// Called when the plugin is loaded. /// The host that loaded the plugin. public virtual void Load(Hosts.HostInterface host) { } /// Called when the plugin is unloaded. public virtual void Unload() { } /// Checks whether the plugin can load the specified texture. /// The path to the file or folder that contains the texture. /// Whether the plugin can load the specified texture. public abstract bool CanLoadTexture(string path); /// Queries the dimensions of a texture. /// The path to the file or folder that contains the texture. /// Receives the width of the texture. /// Receives the height of the texture. /// Whether querying the dimensions was successful. public abstract bool QueryTextureDimensions(string path, out int width, out int height); /// Loads the specified texture. /// The path to the file or folder that contains the texture. /// Receives the texture. /// Whether loading the texture was successful. public abstract bool LoadTexture(string path, out Texture texture); } }openbve-1.4.0.10/openBVE/OpenBveApi/Vector2.cs000066400000000000000000000304371171674032100205430ustar00rootroot00000000000000#pragma warning disable 0660, 0661 using System; namespace OpenBveApi.Math { /// Represents a two-dimensional vector. public struct Vector2 { // --- members --- /// The x-coordinate. public double X; /// The y-coordinate. public double Y; // --- constructors --- /// Creates a new two-dimensional vector. /// The x-coordinate. /// The y-coordinate. public Vector2(double x, double y) { this.X = x; this.Y = y; } // --- arithmetic operators --- /// Adds two vectors. /// The first vector. /// The second vector. /// The sum of the two vectors. public static Vector2 operator +(Vector2 a, Vector2 b) { return new Vector2(a.X + b.X, a.Y + b.Y); } /// Adds a vector and a scalar. /// The vector. /// The scalar. /// The sum of the vector and the scalar. public static Vector2 operator +(Vector2 a, double b) { return new Vector2(a.X + b, a.Y + b); } /// Adds a scalar and a vector. /// The scalar. /// The vector. /// The sum of the scalar and the vector. public static Vector2 operator +(double a, Vector2 b) { return new Vector2(a + b.X, a + b.Y); } /// Subtracts two vectors. /// The first vector. /// The second vector. /// The difference of the two vectors. public static Vector2 operator -(Vector2 a, Vector2 b) { return new Vector2(a.X - b.X, a.Y - b.Y); } /// Subtracts a scalar from a vector. /// The vector. /// The scalar. /// The difference of the vector and the scalar. public static Vector2 operator -(Vector2 a, double b) { return new Vector2(a.X - b, a.Y - b); } /// Subtracts a vector from a scalar. /// The scalar. /// The vector. /// The difference of the scalar and the vector. public static Vector2 operator -(double a, Vector2 b) { return new Vector2(a - b.X, a - b.Y); } /// Negates a vector. /// The vector. /// The negation of the vector. public static Vector2 operator -(Vector2 vector) { return new Vector2(-vector.X, -vector.Y); } /// Multiplies two vectors. /// The first vector. /// The second vector. /// The product of the two vectors. public static Vector2 operator *(Vector2 a, Vector2 b) { return new Vector2(a.X * b.X, a.Y * b.Y); } /// Multiplies a vector and a scalar. /// The vector. /// The scalar. /// The product of the vector and the scalar. public static Vector2 operator *(Vector2 a, double b) { return new Vector2(a.X * b, a.Y * b); } /// Multiplies a scalar and a vector. /// The scalar. /// The vector. /// The product of the scalar and the vector. public static Vector2 operator *(double a, Vector2 b) { return new Vector2(a * b.X, a * b.Y); } /// Divides two vectors. /// The first vector. /// The second vector. /// The quotient of the two vectors. /// Raised when any member of the second vector is zero. public static Vector2 operator /(Vector2 a, Vector2 b) { if (b.X == 0.0 | b.Y == 0.0) { throw new DivideByZeroException(); } else { return new Vector2(a.X / b.X, a.Y / b.Y); } } /// Divides a vector by a scalar. /// The vector. /// The scalar. /// The quotient of the vector and the scalar. /// Raised when the scalar is zero. public static Vector2 operator /(Vector2 a, double b) { if (b == 0.0) { throw new DivideByZeroException(); } else { double factor = 1.0 / b; return new Vector2(a.X * factor, a.Y * factor); } } /// Divides a scalar by a vector. /// The scalar. /// The vector. /// The quotient of the scalar and the vector. /// Raised when any member of the vector is zero. public static Vector2 operator /(double a, Vector2 b) { if (b.X == 0.0 | b.Y == 0.0) { throw new DivideByZeroException(); } else { return new Vector2(a / b.X, a / b.Y); } } // --- comparisons --- /// Checks whether the two specified vectors are equal. /// The first vector. /// The second vector. /// Whether the two vectors are equal. public static bool operator ==(Vector2 a, Vector2 b) { if (a.X != b.X) return false; if (a.Y != b.Y) return false; return true; } /// Checks whether the two specified vectors are unequal. /// The first vector. /// The second vector. /// Whether the two vectors are unequal. public static bool operator !=(Vector2 a, Vector2 b) { if (a.X != b.X) return true; if (a.Y != b.Y) return true; return false; } // --- instance functions --- /// Normalizes the vector. /// Raised when the vector is a null vector. public void Normalize() { double norm = this.X * this.X + this.Y * this.Y; if (norm == 0.0) { throw new DivideByZeroException(); } else { double factor = 1.0 / System.Math.Sqrt(norm); this.X *= factor; this.Y *= factor; } } /// Translates the vector by a specified offset. /// The offset. public void Translate(Vector2 offset) { this.X += offset.X; this.Y += offset.Y; } /// Scales the vector by a specified factor. /// The factor. public void Scale(Vector2 factor) { this.X *= factor.X; this.Y *= factor.Y; } /// Rotates the vector by the specified angle. /// The cosine of the angle. /// The sine of the angle. public void Rotate(double cosineOfAngle, double sineOfAngle) { double x = cosineOfAngle * this.X - sineOfAngle * this.Y; double y = sineOfAngle * this.X + cosineOfAngle * this.Y; this = new Vector2(x, y); } /// Checks whether the vector is a null vector. /// A boolean indicating whether the vector is a null vector. public bool IsNullVector() { return this.X == 0.0 & this.Y == 0.0; } /// Checks whether the vector is considered a null vector. /// The highest absolute value that each component of the vector may have before the vector is not considered a null vector. /// A boolean indicating whether the vector is considered a null vector. public bool IsNullVector(double tolerance) { if (this.X < -tolerance) return false; if (this.X > tolerance) return false; if (this.Y < -tolerance) return false; if (this.Y > tolerance) return false; return true; } /// Gets the euclidean norm. /// The euclidean norm. public double Norm() { return System.Math.Sqrt(this.X * this.X + this.Y * this.Y); } /// Gets the square of the euclidean norm. /// The square of the euclidean norm. public double NormSquared() { return this.X * this.X + this.Y * this.Y; } // --- static functions --- /// Gives the dot product of two vectors. /// The first vector. /// The second vector. /// The dot product of the two vectors. public static double Dot(Vector2 a, Vector2 b) { return a.X * b.X + a.Y * b.Y; } /// Normalizes a vector. /// The vector. /// The normalized vector. /// Raised when the vector is a null vector. public static Vector2 Normalize(Vector2 vector) { double norm = vector.X * vector.X + vector.Y * vector.Y; if (norm == 0.0) { throw new DivideByZeroException(); } else { double factor = 1.0 / System.Math.Sqrt(norm); return new Vector2(vector.X * factor, vector.Y * factor); } } /// Translates a vector by a specified offset. /// The vector. /// The offset. /// The translated vector. public static Vector2 Translate(Vector2 vector, Vector2 offset) { double x = vector.X + offset.X; double y = vector.Y + offset.Y; return new Vector2(x, y); } /// Scales a vector by a specified factor. /// The vector. /// The factor. /// The scaled vector. public static Vector2 Scale(Vector2 vector, Vector2 factor) { double x = vector.X * factor.X; double y = vector.Y * factor.Y; return new Vector2(x, y); } /// Rotates a vector by a specified angle. /// The vector. /// The cosine of the angle. /// The sine of the angle. /// The rotated vector. public static Vector2 Rotate(Vector2 vector, double cosineOfAngle, double sineOfAngle) { double x = cosineOfAngle * vector.X - sineOfAngle * vector.Y; double y = sineOfAngle * vector.X + cosineOfAngle * vector.Y; return new Vector2(x, y); } /// Checks whether a vector is a null vector. /// A boolean indicating whether the vector is a null vector. public static bool IsNullVector(Vector2 vector) { return vector.X == 0.0 & vector.Y == 0.0; } /// Gets the euclidean norm of the specified vector. /// The vector. /// The euclidean norm. public static double Norm(Vector2 vector) { return System.Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y); } /// Gets the square of the euclidean norm of the specified vector. /// The vector. /// The square of the euclidean norm. public static double NormSquared(Vector2 vector) { return vector.X * vector.X + vector.Y * vector.Y; } // --- read-only fields --- /// Represents a null vector. public static readonly Vector2 Null = new Vector2(0.0, 0.0); /// Represents a vector pointing left. public static readonly Vector2 Left = new Vector2(-1.0, 0.0); /// Represents a vector pointing right. public static readonly Vector2 Right = new Vector2(1.0, 0.0); /// Represents a vector pointing up. public static readonly Vector2 Up = new Vector2(0.0, -1.0); /// Represents a vector pointing down. public static readonly Vector2 Down = new Vector2(0.0, 1.0); } }openbve-1.4.0.10/openBVE/OpenBveApi/Vector3.cs000066400000000000000000000472611171674032100205470ustar00rootroot00000000000000#pragma warning disable 0660, 0661 using System; namespace OpenBveApi.Math { /// Represents a three-dimensional vector. public struct Vector3 { // --- members --- /// The x-coordinate. public double X; /// The y-coordinate. public double Y; /// The z-coordinate. public double Z; // --- constructors --- /// Creates a new three-dimensional vector. /// The x-coordinate. /// The y-coordinate. /// The z-coordinate. public Vector3(double x, double y, double z) { this.X = x; this.Y = y; this.Z = z; } // --- arithmetic operators --- /// Adds two vectors. /// The first vector. /// The second vector. /// The sum of the two vectors. public static Vector3 operator +(Vector3 a, Vector3 b) { return new Vector3(a.X + b.X, a.Y + b.Y, a.Z + b.Z); } /// Adds a vector and a scalar. /// The vector. /// The scalar. /// The sum of the vector and the scalar. public static Vector3 operator +(Vector3 a, double b) { return new Vector3(a.X + b, a.Y + b, a.Z + b); } /// Adds a scalar and a vector. /// The scalar. /// The vector. /// The sum of the scalar and the vector. public static Vector3 operator +(double a, Vector3 b) { return new Vector3(a + b.X, a + b.Y, a + b.Z); } /// Subtracts two vectors. /// The first vector. /// The second vector. /// The difference of the two vectors. public static Vector3 operator -(Vector3 a, Vector3 b) { return new Vector3(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } /// Subtracts a scalar from a vector. /// The vector. /// The scalar. /// The difference of the vector and the scalar. public static Vector3 operator -(Vector3 a, double b) { return new Vector3(a.X - b, a.Y - b, a.Z - b); } /// Subtracts a vector from a scalar. /// The scalar. /// The vector. /// The difference of the scalar and the vector. public static Vector3 operator -(double a, Vector3 b) { return new Vector3(a - b.X, a - b.Y, a - b.Z); } /// Negates a vector. /// The vector. /// The negation of the vector. public static Vector3 operator -(Vector3 vector) { return new Vector3(-vector.X, -vector.Y, -vector.Z); } /// Multiplies two vectors. /// The first vector. /// The second vector. /// The product of the two vectors. public static Vector3 operator *(Vector3 a, Vector3 b) { return new Vector3(a.X * b.X, a.Y * b.Y, a.Z * b.Z); } /// Multiplies a vector and a scalar. /// The vector. /// The scalar. /// The product of the vector and the scalar. public static Vector3 operator *(Vector3 a, double b) { return new Vector3(a.X * b, a.Y * b, a.Z * b); } /// Multiplies a scalar and a vector. /// The scalar. /// The vector. /// The product of the scalar and the vector. public static Vector3 operator *(double a, Vector3 b) { return new Vector3(a * b.X, a * b.Y, a * b.Z); } /// Divides two vectors. /// The first vector. /// The second vector. /// The quotient of the two vectors. /// Raised when any member of the second vector is zero. public static Vector3 operator /(Vector3 a, Vector3 b) { if (b.X == 0.0 | b.Y == 0.0 | b.Z == 0.0) { throw new DivideByZeroException(); } else { return new Vector3(a.X / b.X, a.Y / b.Y, a.Z / b.Z); } } /// Divides a vector by a scalar. /// The vector. /// The scalar. /// The quotient of the vector and the scalar. /// Raised when the scalar is zero. public static Vector3 operator /(Vector3 a, double b) { if (b == 0.0) { throw new DivideByZeroException(); } else { double factor = 1.0 / b; return new Vector3(a.X * factor, a.Y * factor, a.Z * factor); } } /// Divides a scalar by a vector. /// The scalar. /// The vector. /// The quotient of the scalar and the vector. /// Raised when any member of the vector is zero. public static Vector3 operator /(double a, Vector3 b) { if (b.X == 0.0 | b.Y == 0.0 | b.Z == 0.0) { throw new DivideByZeroException(); } else { return new Vector3(a / b.X, a / b.Y, a / b.Z); } } // --- comparisons --- /// Checks whether the two specified vectors are equal. /// The first vector. /// The second vector. /// Whether the two vectors are equal. public static bool operator ==(Vector3 a, Vector3 b) { if (a.X != b.X) return false; if (a.Y != b.Y) return false; if (a.Z != b.Z) return false; return true; } /// Checks whether the two specified vectors are unequal. /// The first vector. /// The second vector. /// Whether the two vectors are unequal. public static bool operator !=(Vector3 a, Vector3 b) { if (a.X != b.X) return true; if (a.Y != b.Y) return true; if (a.Z != b.Z) return true; return false; } // --- instance functions --- /// Normalizes the vector. /// Raised when the vector is a null vector. public void Normalize() { double norm = this.X * this.X + this.Y * this.Y + this.Z * this.Z; if (norm == 0.0) { throw new DivideByZeroException(); } else { double factor = 1.0 / System.Math.Sqrt(norm); this.X *= factor; this.Y *= factor; this.Z *= factor; } } /// Translates the vector by a specified offset. /// The offset. public void Translate(Vector3 offset) { this.X += offset.X; this.Y += offset.Y; this.Z += offset.Z; } /// Translates the vector by a specified offset that is measured in a specified orientation. /// The orientation. /// The offset measured in the specified orientation. public void Translate(Orientation3 orientation, Vector3 offset) { this.X += orientation.X.X * offset.X + orientation.Y.X * offset.Y + orientation.Z.X * offset.Z; this.Y += orientation.X.Y * offset.X + orientation.Y.Y * offset.Y + orientation.Z.Y * offset.Z; this.Z += orientation.X.Z * offset.X + orientation.Y.Z * offset.Y + orientation.Z.Z * offset.Z; } /// Scales the vector by a specified factor. /// The factor. public void Scale(Vector3 factor) { this.X *= factor.X; this.Y *= factor.Y; this.Z *= factor.Z; } /// Rotates the vector on the plane perpendicular to a specified direction by a specified angle. /// The direction perpendicular to the plane on which to rotate. /// The cosine of the angle. /// The sine of the angle. public void Rotate(Vector3 direction, double cosineOfAngle, double sineOfAngle) { double cosineComplement = 1.0 - cosineOfAngle; double x = (cosineOfAngle + cosineComplement * direction.X * direction.X) * this.X + (cosineComplement * direction.X * direction.Y - sineOfAngle * direction.Z) * this.Y + (cosineComplement * direction.X * direction.Z + sineOfAngle * direction.Y) * this.Z; double y = (cosineOfAngle + cosineComplement * direction.Y * direction.Y) * this.Y + (cosineComplement * direction.X * direction.Y + sineOfAngle * direction.Z) * this.X + (cosineComplement * direction.Y * direction.Z - sineOfAngle * direction.X) * this.Z; double z = (cosineOfAngle + cosineComplement * direction.Z * direction.Z) * this.Z + (cosineComplement * direction.X * direction.Z - sineOfAngle * direction.Y) * this.X + (cosineComplement * direction.Y * direction.Z + sineOfAngle * direction.X) * this.Y; this = new Vector3(x, y, z); } /// Rotates the vector from the default orientation into a specified orientation. /// The orientation. /// The default orientation is X = {1, 0, 0), Y = {0, 1, 0} and Z = {0, 0, 1}. public void Rotate(Orientation3 orientation) { double x = orientation.X.X * this.X + orientation.Y.X * this.Y + orientation.Z.X * this.Z; double y = orientation.X.Y * this.X + orientation.Y.Y * this.Y + orientation.Z.Y * this.Z; double z = orientation.X.Z * this.X + orientation.Y.Z * this.Y + orientation.Z.Z * this.Z; this = new Vector3(x, y, z); } /// Checks whether the vector is a null vector. /// A boolean indicating whether the vector is a null vector. public bool IsNullVector() { return this.X == 0.0 & this.Y == 0.0 & this.Z == 0.0; } /// Checks whether the vector is considered a null vector. /// The highest absolute value that each component of the vector may have before the vector is not considered a null vector. /// A boolean indicating whether the vector is considered a null vector. public bool IsNullVector(double tolerance) { if (this.X < -tolerance) return false; if (this.X > tolerance) return false; if (this.Y < -tolerance) return false; if (this.Y > tolerance) return false; if (this.Z < -tolerance) return false; if (this.Z > tolerance) return false; return true; } /// Gets the euclidean norm. /// The euclidean norm. public double Norm() { return System.Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z); } /// Gets the square of the euclidean norm. /// The square of the euclidean norm. public double NormSquared() { return this.X * this.X + this.Y * this.Y + this.Z * this.Z; } // --- static functions --- /// Gives the dot product of two vectors. /// The first vector. /// The second vector. /// The dot product of the two vectors. public static double Dot(Vector3 a, Vector3 b) { return a.X * b.X + a.Y * b.Y + a.Z * b.Z; } /// Gives the cross product of two vectors. /// The first vector. /// The second vector. /// The cross product of the two vectors. public static Vector3 Cross(Vector3 a, Vector3 b) { return new Vector3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X); } /// Normalizes a vector. /// The vector. /// The normalized vector. /// Raised when the vector is a null vector. public static Vector3 Normalize(Vector3 vector) { double norm = vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z; if (norm == 0.0) { throw new DivideByZeroException(); } else { double factor = 1.0 / System.Math.Sqrt(norm); return new Vector3(vector.X * factor, vector.Y * factor, vector.Z * factor); } } /// Translates a vector by a specified offset. /// The vector. /// The offset. /// The translated vector. public static Vector3 Translate(Vector3 vector, Vector3 offset) { double x = vector.X + offset.X; double y = vector.Y + offset.Y; double z = vector.Z + offset.Z; return new Vector3(x, y, z); } /// Translates a vector by a specified offset that is measured along a specified orientation. /// The vector. /// The orientation. /// The offset measured in the specified orientation. public static Vector3 Translate(Vector3 vector, Orientation3 orientation, Vector3 offset) { double x = vector.X + orientation.X.X * offset.X + orientation.Y.X * offset.Y + orientation.Z.X * offset.Z; double y = vector.Y + orientation.X.Y * offset.X + orientation.Y.Y * offset.Y + orientation.Z.Y * offset.Z; double z = vector.Z + orientation.X.Z * offset.X + orientation.Y.Z * offset.Y + orientation.Z.Z * offset.Z; return new Vector3(x, y, z); } /// Scales a vector by a specified factor. /// The vector. /// The factor. /// The scaled vector. public static Vector3 Scale(Vector3 vector, Vector3 factor) { double x = vector.X * factor.X; double y = vector.Y * factor.Y; double z = vector.Z * factor.Z; return new Vector3(x, y, z); } /// Rotates a vector on the plane perpendicular to a specified direction by a specified angle. /// The vector. /// The direction perpendicular to the plane on which to rotate. /// The cosine of the angle. /// The sine of the angle. /// The rotated vector. public static Vector3 Rotate(Vector3 vector, Vector3 direction, double cosineOfAngle, double sineOfAngle) { double cosineComplement = 1.0 - cosineOfAngle; double x = (cosineOfAngle + cosineComplement * direction.X * direction.X) * vector.X + (cosineComplement * direction.X * direction.Y - sineOfAngle * direction.Z) * vector.Y + (cosineComplement * direction.X * direction.Z + sineOfAngle * direction.Y) * vector.Z; double y = (cosineOfAngle + cosineComplement * direction.Y * direction.Y) * vector.Y + (cosineComplement * direction.X * direction.Y + sineOfAngle * direction.Z) * vector.X + (cosineComplement * direction.Y * direction.Z - sineOfAngle * direction.X) * vector.Z; double z = (cosineOfAngle + cosineComplement * direction.Z * direction.Z) * vector.Z + (cosineComplement * direction.X * direction.Z - sineOfAngle * direction.Y) * vector.X + (cosineComplement * direction.Y * direction.Z + sineOfAngle * direction.X) * vector.Y; return new Vector3(x, y, z); } /// Rotates a vector from the default orientation into a specified orientation. /// The vector. /// The orientation. /// The rotated vector. /// The default orientation is X = {1, 0, 0), Y = {0, 1, 0} and Z = {0, 0, 1}. public static Vector3 Rotate(Vector3 vector, Orientation3 orientation) { double x = orientation.X.X * vector.X + orientation.Y.X * vector.Y + orientation.Z.X * vector.Z; double y = orientation.X.Y * vector.X + orientation.Y.Y * vector.Y + orientation.Z.Y * vector.Z; double z = orientation.X.Z * vector.X + orientation.Y.Z * vector.Y + orientation.Z.Z * vector.Z; return new Vector3(x, y, z); } /// Creates a unit vector perpendicular to the plane described by three spatial coordinates, suitable for being a surface normal. /// The first spatial coordinate. /// The second spatial coordinate. /// The third spatial coordinate. /// On success, receives the vector perpendicular to the described plane. On failure, receives Vector3.Up. /// The success of the operation. This operation fails if the specified three vectors are colinear. public static bool CreateNormal(Vector3 a, Vector3 b, Vector3 c, out Vector3 normal) { normal = Vector3.Cross(b - a, c - a); double norm = normal.X * normal.X + normal.Y * normal.Y + normal.Z * normal.Z; if (norm != 0.0) { normal *= 1.0 / System.Math.Sqrt(norm); return true; } else { normal = Vector3.Up; return false; } } /// Checks whether three spatial coordinates are colinear. /// The first spatial coordinate. /// The second spatial coordinate. /// The third spatial coordinate. /// A boolean indicating whether the three spatial coordinates are colinear. public static bool AreColinear(Vector3 a, Vector3 b, Vector3 c) { Vector3 normal = Vector3.Cross(b - a, c - a); return IsNullVector(normal); } /// Checks whether a vector is a null vector. /// A boolean indicating whether the vector is a null vector. public static bool IsNullVector(Vector3 vector) { return vector.X == 0.0 & vector.Y == 0.0 & vector.Z == 0.0; } /// Gets the euclidean norm of the specified vector. /// The vector. /// The euclidean norm. public static double Norm(Vector3 vector) { return System.Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z); } /// Gets the square of the euclidean norm of the specified vector. /// The vector. /// The square of the euclidean norm. public static double NormSquared(Vector3 vector) { return vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z; } // --- read-only fields --- /// Represents a null vector. public static readonly Vector3 Null = new Vector3(0.0, 0.0, 0.0); /// Represents a vector pointing left. public static readonly Vector3 Left = new Vector3(-1.0, 0.0, 0.0); /// Represents a vector pointing right. public static readonly Vector3 Right = new Vector3(1.0, 0.0, 0.0); /// Represents a vector pointing up. public static readonly Vector3 Up = new Vector3(0.0, -1.0, 0.0); /// Represents a vector pointing down. public static readonly Vector3 Down = new Vector3(0.0, 1.0, 0.0); /// Represents a vector pointing up. public static readonly Vector3 Backward = new Vector3(0.0, 0.0, -1.0); /// Represents a vector pointing down. public static readonly Vector3 Forward = new Vector3(0.0, 0.0, 1.0); } }openbve-1.4.0.10/openBVE/OpenBveAts/000077500000000000000000000000001171674032100166775ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBveAts/AI.cs000066400000000000000000000167001171674032100175230ustar00rootroot00000000000000using System; using OpenBveApi.Runtime; namespace Plugin { internal class AI { // --- members --- /// The underlying train. private Train Train; // --- constructors --- /// Creates a new AI. /// The underlying train. internal AI(Train train) { this.Train = train; } // --- functions --- /// Is called when the plugin should perform the AI. /// The AI data. internal void Perform(AIData data) { // --- ats-sx --- if (this.Train.AtsSx != null) { if (this.Train.AtsSx.State == AtsSx.States.Disabled) { this.Train.KeyDown(VirtualKeys.D); data.Response = AIResponse.Long; return; } else if (this.Train.AtsSx.State == AtsSx.States.Chime) { bool cancel = false; if (this.Train.State.Location > this.Train.AtsSx.RedSignalLocation) { cancel = true; } if (cancel) { this.Train.KeyDown(VirtualKeys.A1); data.Response = AIResponse.Medium; return; } } else if (this.Train.AtsSx.State == AtsSx.States.Alarm) { if (data.Handles.PowerNotch > 0) { data.Handles.PowerNotch--; data.Response = data.Handles.PowerNotch > 0 ? AIResponse.Short : AIResponse.Medium; return; } else if (data.Handles.BrakeNotch < this.Train.Specs.AtsNotch) { data.Handles.BrakeNotch++; data.Response = data.Handles.BrakeNotch < this.Train.Specs.AtsNotch ? AIResponse.Short : AIResponse.Medium; return; } else { this.Train.KeyDown(VirtualKeys.S); data.Response = AIResponse.Medium; return; } } else if (this.Train.AtsSx.State == AtsSx.States.Emergency) { if (data.Handles.PowerNotch > 0) { data.Handles.PowerNotch--; data.Response = data.Handles.PowerNotch > 0 ? AIResponse.Short : AIResponse.Medium; return; } else if (data.Handles.BrakeNotch <= this.Train.Specs.BrakeNotches) { data.Handles.BrakeNotch++; data.Response = data.Handles.BrakeNotch <= this.Train.Specs.BrakeNotches ? AIResponse.Short : AIResponse.Medium; return; } else if (data.Handles.Reverser != 0) { data.Handles.Reverser = 0; data.Response = AIResponse.Medium; return; } else if (Math.Abs(this.Train.State.Speed.KilometersPerHour) < 1.0) { this.Train.KeyDown(VirtualKeys.B1); data.Response = AIResponse.Long; return; } else { data.Response = AIResponse.Long; return; } } } // --- ats-p --- if (this.Train.AtsP != null) { if (this.Train.AtsP.State == AtsP.States.Disabled) { this.Train.KeyDown(VirtualKeys.E); data.Response = AIResponse.Long; return; } else if (this.Train.AtsP.State == AtsP.States.Pattern) { if (this.Train.State.Speed.MetersPerSecond > 15.0 / 3.6) { if (data.Handles.PowerNotch > 0) { data.Handles.PowerNotch--; data.Response = data.Handles.PowerNotch > 0 ? AIResponse.Short : AIResponse.Medium; return; } else if (data.Handles.BrakeNotch <= this.Train.Specs.AtsNotch) { data.Handles.BrakeNotch++; data.Response = data.Handles.BrakeNotch <= this.Train.Specs.AtsNotch ? AIResponse.Short : AIResponse.Long; return; } } } else if (this.Train.AtsP.State == AtsP.States.Brake) { if (data.Handles.PowerNotch > 0) { data.Handles.PowerNotch--; data.Response = data.Handles.PowerNotch > 0 ? AIResponse.Short : AIResponse.Medium; return; } else if (Math.Abs(this.Train.State.Speed.MetersPerSecond) < 1.0 / 3.6) { if (data.Handles.BrakeNotch < this.Train.Specs.BrakeNotches) { data.Handles.BrakeNotch++; data.Response = data.Handles.BrakeNotch < this.Train.Specs.BrakeNotches ? AIResponse.Short : AIResponse.Medium; return; } else if (data.Handles.Reverser != 0) { data.Handles.Reverser = 0; data.Response = AIResponse.Medium; return; } else { this.Train.KeyDown(VirtualKeys.B1); data.Response = AIResponse.Long; return; } } } else if (this.Train.AtsP.State == AtsP.States.Service | this.Train.AtsP.State == AtsP.States.Emergency) { if (data.Handles.PowerNotch > 0) { data.Handles.PowerNotch--; data.Response = data.Handles.PowerNotch > 0 ? AIResponse.Short : AIResponse.Medium; return; } else if (data.Handles.BrakeNotch < this.Train.Specs.BrakeNotches) { data.Handles.BrakeNotch++; data.Response = data.Handles.BrakeNotch < this.Train.Specs.BrakeNotches ? AIResponse.Short : AIResponse.Medium; return; } else if (data.Handles.Reverser != 0) { data.Handles.Reverser = 0; data.Response = AIResponse.Medium; return; } else if (Math.Abs(this.Train.State.Speed.KilometersPerHour) < 1.0) { this.Train.KeyDown(VirtualKeys.B1); data.Response = AIResponse.Long; return; } else { data.Response = AIResponse.Long; return; } } } // --- atc --- if (this.Train.Atc != null) { if (this.Train.Atc.State == Atc.States.Disabled) { this.Train.KeyDown(VirtualKeys.G); data.Response = AIResponse.Long; return; } else if (this.Train.Atc.ShouldSwitchToAts()) { this.Train.KeyDown(VirtualKeys.C1); data.Response = AIResponse.Long; return; } else if (this.Train.Atc.ShouldSwitchToAtc()) { this.Train.KeyDown(VirtualKeys.C2); data.Response = AIResponse.Long; return; } else if (this.Train.Atc.State == Atc.States.Normal | this.Train.Atc.State == Atc.States.Service) { if (this.Train.State.Speed.KilometersPerHour > 15.0) { if (this.Train.State.Speed.MetersPerSecond > this.Train.Atc.CurrentAtcSpeed - 5.0 / 3.6) { if (data.Handles.PowerNotch > 0) { data.Handles.PowerNotch--; data.Response = data.Handles.PowerNotch > 0 ? AIResponse.Short : AIResponse.Medium; return; } else if (data.Handles.BrakeNotch <= this.Train.Specs.AtsNotch) { data.Handles.BrakeNotch++; data.Response = data.Handles.BrakeNotch <= this.Train.Specs.AtsNotch ? AIResponse.Short : AIResponse.Long; return; } } else if (this.Train.State.Speed.MetersPerSecond > this.Train.Atc.CurrentAtcSpeed - 10.0 / 3.6) { if (data.Handles.PowerNotch > 0) { data.Handles.PowerNotch--; data.Response = data.Handles.PowerNotch > 0 ? AIResponse.Short : AIResponse.Long; return; } } if (this.Train.Atc.CurrentAtcSpeed == 0.0) { data.Response = AIResponse.Long; } } } else if (this.Train.Atc.State == Atc.States.Emergency) { if (data.Handles.PowerNotch > 0) { data.Handles.PowerNotch--; data.Response = data.Handles.PowerNotch > 0 ? AIResponse.Short : AIResponse.Medium; return; } else if (data.Handles.BrakeNotch < this.Train.Specs.B67Notch) { data.Handles.BrakeNotch++; data.Response = data.Handles.BrakeNotch < this.Train.Specs.B67Notch ? AIResponse.Short : AIResponse.Medium; return; } else { data.Response = AIResponse.Long; return; } } } // --- eb --- if (this.Train.Eb != null) { if (this.Train.Eb.Counter >= this.Train.Eb.TimeUntilBell) { this.Train.KeyDown(VirtualKeys.A2); data.Response = AIResponse.Long; } } } } }openbve-1.4.0.10/openBVE/OpenBveAts/Atc.cs000066400000000000000000000365621171674032100177510ustar00rootroot00000000000000using System; using System.Collections.Generic; using OpenBveApi.Runtime; namespace Plugin { /// Represents ATC. internal class Atc : Device { // --- enumerations and structures --- /// Represents different states of ATC. internal enum States { /// The system is disabled. Disabled = 0, /// The system is enabled, but currently suppressed. This will change to States.Ats once the emergency brakes are released. Suppressed = 1, /// The system has been set to ATS mode. Ats = 2, /// The system is operating normally. Normal = 3, /// The system is applying the service brakes. Service = 4, /// The system is applying the emergency brakes. Emergency = 5 } /// Represents different states of the compatibility ATC track. private enum CompatibilityStates { /// ATC is not available. Ats = 0, /// ATC is available. The ToAtc reminder plays when the train has come to a stop. ToAtc = 1, /// ATC is available. Atc = 2, /// ATC is available. The ToAts reminder plays when the train has come to a stop. ToAts = 3 } /// Represents a speed limit at a specific track position. private struct CompatibilityLimit { // --- members --- /// The speed limit. internal double Limit; /// The track position. internal double Location; // --- constructors --- /// Creates a new compatibility limit. /// The speed limit. /// The track position. internal CompatibilityLimit(double limit, double location) { this.Limit = limit; this.Location = location; } } // --- members --- /// The underlying train. private Train Train; /// The current state of the system. internal States State; /// Whether to switch to ATC in the next Elapse call. This is set by the Initialize call if the train should start in ATC mode. It is necessary to switch in the Elapse call because at the time of the Initialize call, the ATC track status is not yet known. private bool StateSwitch; /// The currently permitted ATC speed, or -1 if ATC is not available. internal double CurrentAtcSpeed; /// The state of the compatibility ATC track. private CompatibilityStates CompatibilityState; /// A list of all ATC speed limits in the route. private List CompatibilityLimits; /// The element in the CompatibilityLimits list that holds the last encountered speed limit. private int CompatibilityLimitPointer; /// The state of the preceding train, or a null reference. private PrecedingVehicleState PrecedingTrain; // --- parameters --- /// Whether to automatically switch between ATS and ATC. private bool AutomaticSwitch = false; /// The permitted compatibility ATC speeds, which are X, 0, 15, 25, 45, 65, 75, 90, 100, 110 and 120. private readonly double[] CompatibilitySpeeds = new double[] { -1.0, 0.0 / 3.6, 15.0 / 3.6, 25.0 / 3.6, 45.0 / 3.6, 55.0 / 3.6, 65.0 / 3.6, 75.0 / 3.6, 90.0 / 3.6, 100.0 / 3.6, 110.0 / 3.6, 120.0 / 3.6 }; // --- constructors --- /// Creates a new instance of this system. /// The train. /// Whether to switch automatically between ATS to ATC. internal Atc(Train train, bool automaticSwitch) { this.Train = train; this.State = States.Disabled; this.CompatibilityState = CompatibilityStates.Ats; this.CompatibilityLimits = new List(); this.AutomaticSwitch = automaticSwitch; } // --- functions --- /// Gets the current ATC speed, or -1 if ATC is not available. In emergency operation mode, returns 15 km/h. /// The ATC speed, or -1 if ATC is not available. private double GetCurrentAtcSpeed() { if (this.CompatibilityState != CompatibilityStates.Ats) { double a = GetAtcSpeedFromTrain(); double b = GetAtcSpeedFromLimit(); return Math.Min(a, b); } else { return -1.0; } } /// Gets the ATC speed from the distance to the preceding train if operating in compatibility ATC mode. /// The ATC speed, or -1 if ATC is not available. private double GetAtcSpeedFromTrain() { if (this.CompatibilityState != CompatibilityStates.Ats) { if (this.PrecedingTrain == null) { return this.CompatibilitySpeeds[11]; } else { const double blockLength = 100.0; int a = (int)Math.Floor(this.PrecedingTrain.Location / blockLength); int b = (int)Math.Floor(this.Train.State.Location / blockLength); int blocks = a - b; switch (blocks) { case 0: return this.CompatibilitySpeeds[0]; case 1: return this.CompatibilitySpeeds[1]; case 2: return this.CompatibilitySpeeds[3]; case 3: return this.CompatibilitySpeeds[4]; case 4: return this.CompatibilitySpeeds[5]; case 5: return this.CompatibilitySpeeds[6]; case 6: return this.CompatibilitySpeeds[7]; case 7: return this.CompatibilitySpeeds[8]; case 8: return this.CompatibilitySpeeds[9]; case 9: return this.CompatibilitySpeeds[10]; default: return this.CompatibilitySpeeds[11]; } } } else { return -1.0; } } /// Gets the ATC speed from the current and upcoming speed limits. /// The ATC speed, or -1 if ATC is not available. private double GetAtcSpeedFromLimit() { if (this.CompatibilityState != CompatibilityStates.Ats) { if (this.CompatibilityLimits.Count == 0) { return double.MaxValue; } else if (this.CompatibilityLimits.Count == 1) { return this.CompatibilityLimits[0].Limit; } else { while (CompatibilityLimitPointer > 0 && this.CompatibilityLimits[CompatibilityLimitPointer].Location > this.Train.State.Location) { CompatibilityLimitPointer--; } while (CompatibilityLimitPointer < this.CompatibilityLimits.Count - 1 && this.CompatibilityLimits[CompatibilityLimitPointer + 1].Location <= this.Train.State.Location) { CompatibilityLimitPointer++; } if (this.CompatibilityLimitPointer == this.CompatibilityLimits.Count - 1) { return this.CompatibilityLimits[this.CompatibilityLimitPointer].Limit; } else if (this.CompatibilityLimits[this.CompatibilityLimitPointer].Limit <= this.CompatibilityLimits[this.CompatibilityLimitPointer + 1].Limit) { return this.CompatibilityLimits[this.CompatibilityLimitPointer].Limit; } else { const double deceleration = 1.910 / 3.6; double currentLimit = this.CompatibilityLimits[this.CompatibilityLimitPointer].Limit; double upcomingLimit = this.CompatibilityLimits[this.CompatibilityLimitPointer + 1].Limit; double distance = (currentLimit * currentLimit - upcomingLimit * upcomingLimit) / (2.0 * deceleration); if (this.Train.State.Location < this.CompatibilityLimits[this.CompatibilityLimitPointer + 1].Location - distance) { return this.CompatibilityLimits[this.CompatibilityLimitPointer].Limit; } else { return this.CompatibilityLimits[this.CompatibilityLimitPointer + 1].Limit; } } } } else { return -1.0; } } /// Whether the driver should switch to ATS. This returns false if already operating in ATS. /// Whether the driver should switch to ATS. internal bool ShouldSwitchToAts() { if (this.CompatibilityState == CompatibilityStates.ToAts) { if (Math.Abs(this.Train.State.Speed.MetersPerSecond) < 0.01) { if (this.State == States.Normal | this.State == States.Service | this.State == States.Emergency) { return true; } } } else if (this.CompatibilityState == CompatibilityStates.Ats) { if (this.State == States.Normal | this.State == States.Service | this.State == States.Emergency) { return true; } } return false; } /// Whether the driver should switch to ATC. This returns false if already operating in ATC. /// Whether the driver should switch to ATC. internal bool ShouldSwitchToAtc() { if (this.CompatibilityState == CompatibilityStates.Atc) { if (this.State == States.Ats) { return true; } } else if (this.CompatibilityState == CompatibilityStates.ToAtc) { if (Math.Abs(this.Train.State.Speed.MetersPerSecond) < 0.01) { if (this.State == States.Ats) { return true; } } } return false; } // --- inherited functions --- /// Is called when the system should initialize. /// The initialization mode. internal override void Initialize(InitializationModes mode) { if (mode == InitializationModes.OffEmergency) { this.State = States.Suppressed; } else { this.State = States.Ats; this.StateSwitch = true; } } /// Is called every frame. /// The data. /// Whether the device is blocked or will block subsequent devices. internal override void Elapse(ElapseData data, ref bool blocking) { // --- behavior --- this.PrecedingTrain = data.PrecedingVehicle; if (this.StateSwitch) { this.State = States.Normal; this.StateSwitch = false; } if (this.State == States.Suppressed) { if (data.Handles.BrakeNotch <= this.Train.Specs.BrakeNotches) { this.State = States.Ats; } } if (blocking) { if (this.State != States.Disabled & this.State != States.Suppressed) { this.State = States.Ats; } } else { if (this.State == States.Normal | this.State == States.Service | this.State == States.Emergency) { double speed; speed = GetCurrentAtcSpeed(); if (speed != this.CurrentAtcSpeed) { this.Train.Sounds.AtcBell.Play(); } this.CurrentAtcSpeed = speed; if (speed < 0.0) { if (this.State != States.Emergency) { this.State = States.Emergency; this.Train.Sounds.AtcBell.Play(); } } else if (Math.Abs(data.Vehicle.Speed.MetersPerSecond) > speed) { if (this.State != States.Service) { this.State = States.Service; this.Train.Sounds.AtcBell.Play(); } } else if (Math.Abs(data.Vehicle.Speed.MetersPerSecond) < speed - 1.0 / 3.6) { if (this.State != States.Normal) { this.State = States.Normal; this.Train.Sounds.AtcBell.Play(); } } if (this.State == States.Service) { if (data.Handles.BrakeNotch < this.Train.Specs.BrakeNotches) { data.Handles.BrakeNotch = this.Train.Specs.BrakeNotches; } } else if (this.State == States.Emergency) { data.Handles.BrakeNotch = this.Train.Specs.BrakeNotches + 1; } blocking = true; } if (this.State != States.Disabled & (this.Train.Doors != DoorStates.None | data.Handles.BrakeNotch > 0)) { data.Handles.PowerNotch = 0; } } // --- panel --- if (this.State == States.Ats) { this.Train.Panel[271] = 12; } else if (this.State == States.Normal | this.State == States.Service | this.State == States.Emergency) { this.Train.Panel[265] = 1; if (this.CurrentAtcSpeed < 0.0) { this.Train.Panel[271] = 0; } else { int value = this.CompatibilitySpeeds.Length - 1; for (int i = 2; i < this.CompatibilitySpeeds.Length; i++) { if (this.CompatibilitySpeeds[i] > this.CurrentAtcSpeed) { value = i - 1; break; } } this.Train.Panel[271] = value; } } if (this.State == States.Service) { this.Train.Panel[267] = 1; } else if (this.State == States.Emergency) { this.Train.Panel[268] = 1; } if (this.State != States.Disabled & this.State != States.Suppressed) { this.Train.Panel[266] = 1; } // --- manual or automatic switch --- if (ShouldSwitchToAts()) { if (this.AutomaticSwitch & Math.Abs(data.Vehicle.Speed.MetersPerSecond) < 1.0 / 3.6) { KeyDown(VirtualKeys.C1); } } else if (ShouldSwitchToAtc()) { if (this.AutomaticSwitch & Math.Abs(data.Vehicle.Speed.MetersPerSecond) < 1.0 / 3.6) { KeyDown(VirtualKeys.C2); } } } /// Is called when the driver changes the reverser. /// The new reverser position. internal override void SetReverser(int reverser) { } /// Is called when the driver changes the power notch. /// The new power notch. internal override void SetPower(int powerNotch) { } /// Is called when the driver changes the brake notch. /// The new brake notch. internal override void SetBrake(int brakeNotch) { } /// Is called when a key is pressed. /// The key. internal override void KeyDown(VirtualKeys key) { switch (key) { case VirtualKeys.C1: // --- switch to ats --- if (this.State == States.Normal | this.State == States.Service | this.State == States.Emergency) { this.State = States.Ats; this.Train.Sounds.ToAts.Play(); } break; case VirtualKeys.C2: // --- switch to atc --- if (this.State == States.Ats) { this.State = States.Normal; this.Train.Sounds.ToAtc.Play(); } break; } } /// Is called when a key is released. /// The key. internal override void KeyUp(VirtualKeys key) { } /// Is called when a horn is played or when the music horn is stopped. /// The type of horn. internal override void HornBlow(HornTypes type) { } /// Is called to inform about signals. /// The signal data. internal override void SetSignal(SignalData[] signal) { } /// Is called when a beacon is passed. /// The beacon data. internal override void SetBeacon(BeaconData beacon) { switch (beacon.Type) { case -16777215: if (beacon.Optional >= 0 & beacon.Optional <= 3) { this.CompatibilityState = (CompatibilityStates)beacon.Optional; } break; case -16777214: { double limit = (double)(beacon.Optional & 4095) / 3.6; double position = (beacon.Optional >> 12); CompatibilityLimit item = new CompatibilityLimit(limit, position); if (!this.CompatibilityLimits.Contains(item)) { this.CompatibilityLimits.Add(item); } } break; } } } }openbve-1.4.0.10/openBVE/OpenBveAts/AtsP.cs000066400000000000000000000511761171674032100201070ustar00rootroot00000000000000using System; using System.Collections.Generic; using OpenBveApi.Runtime; namespace Plugin { /// Represents ATS-P. internal class AtsP : Device { // --- enumerations --- /// Represents different states of ATS-P. internal enum States { /// The system is disabled. Disabled = 0, /// The system is enabled, but currently suppressed. This will change to States.Initializing once the emergency brakes are released. Suppressed = 1, /// The system is initializing. This will change to States.Standby once the initialization is complete. Initializing = 2, /// The system is available but no ATS-P signal was yet picked up. Standby = 3, /// The system is operating normally. Normal = 4, /// The system is approaching a brake pattern. Pattern = 5, /// The system is braking due to speed excess. Brake = 6, /// The system applies the service brakes due to an immediate stop command. Service = 7, /// The system applies the emergency brakes due to an immediate stop command. Emergency = 8 } // --- pattern --- /// Represents a pattern. private class Pattern { // --- members --- /// The underlying ATS-P device. internal AtsP Device; /// The position of the point of danger, or System.Double.MinValue, or System.Double.MaxValue. internal double Position; /// The warning pattern, or System.Double.MaxValue. internal double WarningPattern; /// The brake pattern, or System.Double.MaxValue. internal double BrakePattern; /// The speed limit at the point of danger, or System.Double.MaxValue. internal double TargetSpeed; /// The current gradient. internal double Gradient; // --- constructors --- /// Creates a new pattern. /// A reference to the underlying ATS-P device. internal Pattern(AtsP device) { this.Device = device; this.Position = double.MaxValue; this.WarningPattern = double.MaxValue; this.BrakePattern = double.MaxValue; this.TargetSpeed = double.MaxValue; this.Gradient = 0.0; } // --- functions --- /// Updates the pattern. /// The current ATS-P system. /// The elapse data. internal void Perform(AtsP system, ElapseData data) { if (this.Position == double.MaxValue | this.TargetSpeed == double.MaxValue) { this.WarningPattern = double.MaxValue; this.BrakePattern = double.MaxValue; } else if (this.Position == double.MinValue) { this.WarningPattern = this.TargetSpeed - this.Device.PatternSpeedDifference; this.BrakePattern = Math.Max(this.TargetSpeed, this.Device.ReleaseSpeed); } else { const double earthGravity = 9.81; double accelerationDueToGravity = earthGravity * this.Gradient / Math.Sqrt(1.0 + this.Gradient * this.Gradient); double deceleration = this.Device.DesignDeceleration + accelerationDueToGravity; double distance = this.Position - system.Position; /* * Calculate the warning pattern. * */ { double sqrtTerm = 2.0 * deceleration * (distance - 50.0) + deceleration * deceleration * this.Device.ReactionDelay * this.Device.ReactionDelay + this.TargetSpeed * this.TargetSpeed; if (sqrtTerm <= 0.0) { this.WarningPattern = this.TargetSpeed - this.Device.PatternSpeedDifference; } else { this.WarningPattern = Math.Max(Math.Sqrt(sqrtTerm) - deceleration * this.Device.ReactionDelay, this.TargetSpeed - this.Device.PatternSpeedDifference); } } /* * Calculate the brake pattern. * */ { double sqrtTerm = 2.0 * deceleration * distance + this.TargetSpeed * this.TargetSpeed; if (sqrtTerm <= 0.0) { this.BrakePattern = this.TargetSpeed; } else { this.BrakePattern = Math.Max(Math.Sqrt(sqrtTerm), TargetSpeed); } if (this.BrakePattern < this.Device.ReleaseSpeed) { this.BrakePattern = this.Device.ReleaseSpeed; } } } } /// Sets the position of the red signal. /// The position. internal void SetSignal(double position) { this.Position = position; this.TargetSpeed = 0.0; } /// Sets a speed limit and the position of the speed limit. /// The speed. /// The position. internal void SetLimit(double speed, double position) { this.Position = position; this.TargetSpeed = speed; } /// Sets the gradient. /// The gradient. internal void SetGradient(double gradient) { this.Gradient = gradient; } /// Clears the pattern. internal void Clear() { this.Position = double.MaxValue; this.WarningPattern = double.MaxValue; this.BrakePattern = double.MaxValue; this.TargetSpeed = 0.0; this.Gradient = 0.0; } } // --- compatibility limit --- /// Represents a speed limit at a specific track position. private struct CompatibilityLimit { // --- members --- /// The speed limit. internal double Limit; /// The track position. internal double Location; // --- constructors --- /// Creates a new compatibility limit. /// The speed limit. /// The track position. internal CompatibilityLimit(double limit, double location) { this.Limit = limit; this.Location = location; } } // --- members --- /// The underlying train. private Train Train; /// The current state of the system. internal States State; /// Whether the brake release is currently active. private bool BrakeRelease; /// The remaining time before the brake release is over. private double BrakeReleaseCountdown; /// The current initialization countdown. private double InitializationCountdown; /// The position of the train as obtained from odometry. private double Position; /// A list of all compatibility temporary speed limits in the route. private List CompatibilityLimits; /// The element in the CompatibilityLimits list that holds the last speed limit. private int CompatibilityLimitPointer; // --- patterns --- /// The signal pattern. private Pattern SignalPattern; /// The compatibility temporary pattern. private Pattern CompatibilityTemporaryPattern; /// The compatibility permanent pattern. private Pattern CompatibilityPermanentPattern; /// A list of all patterns. private Pattern[] Patterns; // --- parameters --- /// The duration of the initialization process. internal readonly double DurationOfInitialization = 3.0; /// The duration of the brake release. If zero, the brake release is not available. internal readonly double DurationOfBrakeRelease = 60.0; /// The design deceleration. internal readonly double DesignDeceleration = 2.445 / 3.6; /// The reaction delay. internal readonly double ReactionDelay = 5.5; /// The release speed. internal readonly double ReleaseSpeed = 15.0 / 3.6; /// The pattern speed difference. internal readonly double PatternSpeedDifference = 5.0 / 3.6; /// The signal offset. internal readonly double SignalOffset = 0.0; // --- constructors --- /// Creates a new instance of this system. /// The train. internal AtsP(Train train) { this.Train = train; this.State = States.Disabled; this.InitializationCountdown = 0.0; this.CompatibilityLimits = new List(); this.CompatibilityLimitPointer = 0; this.SignalPattern = new Pattern(this); this.CompatibilityTemporaryPattern = new Pattern(this); this.CompatibilityPermanentPattern = new Pattern(this); List patterns = new List(); patterns.Add(this.SignalPattern); patterns.Add(this.CompatibilityTemporaryPattern); patterns.Add(this.CompatibilityPermanentPattern); this.Patterns = patterns.ToArray(); } // --- functions --- /// Changes to standby mode and continues in ATS-Sx. private void SwitchToSx() { if (this.Train.AtsSx != null) { foreach (Pattern pattern in this.Patterns) { pattern.Clear(); } this.State = States.Standby; this.Train.Sounds.AtsPBell.Play(); this.Train.AtsSx.State = AtsSx.States.Chime; } else if (this.State != States.Emergency) { this.State = States.Emergency; if (this.State != States.Brake & this.State != States.Service) { this.Train.Sounds.AtsPBell.Play(); } } } /// Switches to ATS-P. /// The desired state. private void SwitchToP(States state) { if (this.State == States.Standby) { if (this.Train.AtsSx == null || this.Train.AtsSx.State != AtsSx.States.Emergency) { this.State = state; this.Train.Sounds.AtsPBell.Play(); } } else if (state == States.Service | state == States.Emergency) { if (this.State != States.Brake & this.State != States.Service & this.State != States.Emergency) { this.Train.Sounds.AtsPBell.Play(); } this.State = state; } } /// Updates the compatibility temporary speed pattern from the list of known speed limits. private void UpdateCompatibilityTemporarySpeedPattern() { if (this.CompatibilityLimits.Count != 0) { if (this.CompatibilityTemporaryPattern.Position != double.MaxValue) { if (this.CompatibilityTemporaryPattern.BrakePattern < this.Train.State.Speed.MetersPerSecond) { return; } double delta = this.CompatibilityTemporaryPattern.Position - this.Train.State.Location; if (delta >= -50.0 & delta <= 0.0) { return; } } while (CompatibilityLimitPointer > 0 && this.CompatibilityLimits[CompatibilityLimitPointer].Location > this.Train.State.Location) { CompatibilityLimitPointer--; } while (CompatibilityLimitPointer < this.CompatibilityLimits.Count - 1 && this.CompatibilityLimits[CompatibilityLimitPointer + 1].Location <= this.Train.State.Location) { CompatibilityLimitPointer++; } if (this.CompatibilityLimitPointer == 0 && this.CompatibilityLimits[0].Location > this.Train.State.Location) { this.CompatibilityTemporaryPattern.SetLimit(this.CompatibilityLimits[0].Limit, this.CompatibilityLimits[0].Location); } else if (this.CompatibilityLimitPointer < this.CompatibilityLimits.Count - 1) { this.CompatibilityTemporaryPattern.SetLimit(this.CompatibilityLimits[this.CompatibilityLimitPointer + 1].Limit, this.CompatibilityLimits[this.CompatibilityLimitPointer + 1].Location); } else { this.CompatibilityTemporaryPattern.Clear(); } } } // --- inherited functions --- /// Is called when the system should initialize. /// The initialization mode. internal override void Initialize(InitializationModes mode) { if (mode == InitializationModes.OffEmergency) { this.State = States.Suppressed; } else { this.State = States.Standby; } foreach (Pattern pattern in this.Patterns) { if (Math.Abs(this.Train.State.Speed.MetersPerSecond) >= pattern.WarningPattern) { pattern.Clear(); } } } /// Is called every frame. /// The data. /// Whether the device is blocked or will block subsequent devices. internal override void Elapse(ElapseData data, ref bool blocking) { // --- behavior --- if (this.State == States.Suppressed) { if (data.Handles.BrakeNotch <= this.Train.Specs.BrakeNotches) { this.InitializationCountdown = DurationOfInitialization; this.State = States.Initializing; } } if (this.State == States.Initializing) { this.InitializationCountdown -= data.ElapsedTime.Seconds; if (this.InitializationCountdown <= 0.0) { this.State = States.Standby; foreach (Pattern pattern in this.Patterns) { if (Math.Abs(data.Vehicle.Speed.MetersPerSecond) >= pattern.WarningPattern) { pattern.Clear(); } } this.Train.Sounds.AtsPBell.Play(); } } if (this.BrakeRelease) { this.BrakeReleaseCountdown -= data.ElapsedTime.Seconds; if (this.BrakeReleaseCountdown <= 0.0) { this.BrakeRelease = false; this.Train.Sounds.AtsPBell.Play(); } } if (this.State != States.Disabled & this.State != States.Initializing) { this.Position += data.Vehicle.Speed.MetersPerSecond * data.ElapsedTime.Seconds; } if (blocking) { if (this.State != States.Disabled & this.State != States.Suppressed) { this.State = States.Standby; } } else { if (this.State == States.Normal | this.State == States.Pattern | this.State == States.Brake) { bool brake = false; bool warning = false; UpdateCompatibilityTemporarySpeedPattern(); foreach (Pattern pattern in this.Patterns) { pattern.Perform(this, data); if (Math.Abs(data.Vehicle.Speed.MetersPerSecond) >= pattern.WarningPattern) { warning = true; } if (Math.Abs(data.Vehicle.Speed.MetersPerSecond) >= pattern.BrakePattern) { brake = true; } } if (BrakeRelease) { brake = false; } if (brake & this.State != States.Brake) { this.State = States.Brake; this.Train.Sounds.AtsPBell.Play(); } else if (warning & this.State == States.Normal) { this.State = States.Pattern; this.Train.Sounds.AtsPBell.Play(); } else if (!brake & !warning & (this.State == States.Pattern | this.State == States.Brake)) { this.State = States.Normal; this.Train.Sounds.AtsPBell.Play(); } if (this.State == States.Brake) { if (data.Handles.BrakeNotch < this.Train.Specs.BrakeNotches) { data.Handles.BrakeNotch = this.Train.Specs.BrakeNotches; } } blocking = true; } else if (this.State == States.Service) { if (data.Handles.BrakeNotch < this.Train.Specs.BrakeNotches) { data.Handles.BrakeNotch = this.Train.Specs.BrakeNotches; } blocking = true; } else if (this.State == States.Emergency) { data.Handles.BrakeNotch = this.Train.Specs.BrakeNotches + 1; blocking = true; } if (this.State != States.Disabled & (this.Train.Doors != DoorStates.None | data.Handles.BrakeNotch > 0)) { data.Handles.PowerNotch = 0; } } // --- panel --- if (this.State != States.Disabled & this.State != States.Suppressed) { this.Train.Panel[2] = 1; this.Train.Panel[259] = 1; } if (this.State == States.Pattern | this.State == States.Brake | this.State == States.Service | this.State == States.Emergency) { this.Train.Panel[3] = 1; this.Train.Panel[260] = 1; } if (this.State == States.Brake | this.State == States.Service | this.State == States.Emergency) { this.Train.Panel[5] = 1; this.Train.Panel[262] = 1; } if (this.State != States.Disabled & this.State != States.Suppressed & this.State != States.Standby) { this.Train.Panel[6] = 1; this.Train.Panel[263] = 1; } if (this.State == States.Initializing) { this.Train.Panel[7] = 1; this.Train.Panel[264] = 1; } if (this.State == States.Disabled) { this.Train.Panel[50] = 1; } if (this.BrakeRelease) { this.Train.Panel[4] = 1; this.Train.Panel[261] = 1; } } /// Is called when the driver changes the reverser. /// The new reverser position. internal override void SetReverser(int reverser) { } /// Is called when the driver changes the power notch. /// The new power notch. internal override void SetPower(int powerNotch) { } /// Is called when the driver changes the brake notch. /// The new brake notch. internal override void SetBrake(int brakeNotch) { } /// Is called when a key is pressed. /// The key. internal override void KeyDown(VirtualKeys key) { switch (key) { case VirtualKeys.B1: // --- reset the system --- if ((this.State == States.Brake | this.State == States.Service | this.State == States.Emergency) & this.Train.Handles.Reverser == 0 & this.Train.Handles.PowerNotch == 0 & this.Train.Handles.BrakeNotch >= this.Train.Specs.BrakeNotches) { foreach (Pattern pattern in this.Patterns) { if (Math.Abs(this.Train.State.Speed.MetersPerSecond) >= pattern.WarningPattern) { pattern.Clear(); } } this.State = States.Normal; this.Train.Sounds.AtsPBell.Play(); } break; case VirtualKeys.B2: // --- brake release --- if ((this.State == States.Normal | this.State == States.Pattern) & !BrakeRelease & DurationOfBrakeRelease > 0.0) { BrakeRelease = true; BrakeReleaseCountdown = DurationOfBrakeRelease; this.Train.Sounds.AtsPBell.Play(); } break; } } /// Is called when a key is released. /// The key. internal override void KeyUp(VirtualKeys key) { } /// Is called when a horn is played or when the music horn is stopped. /// The type of horn. internal override void HornBlow(HornTypes type) { } /// Is called to inform about signals. /// The signal data. internal override void SetSignal(SignalData[] signal) { } /// Is called when a beacon is passed. /// The beacon data. internal override void SetBeacon(BeaconData beacon) { if (this.State != States.Disabled & this.State != States.Suppressed & this.State != States.Initializing) { switch (beacon.Type) { case 0: case 1: // --- P -> S --- if (beacon.Optional == 0) { if (this.State == States.Normal | this.State == States.Pattern | this.State == States.Brake) { SwitchToSx(); } } break; case 3: case 4: // --- P pattern / P immediate stop --- this.Position = this.Train.State.Location; if (this.State != States.Service & this.State != States.Emergency) { if (this.State == States.Standby & (beacon.Type != 3 | beacon.Optional != -1)) { SwitchToP(States.Normal); } if (this.State != States.Standby) { if (beacon.Signal.Aspect == 0) { this.SignalPattern.SetSignal(this.Position + beacon.Signal.Distance - SignalOffset); if (beacon.Type != 3 & beacon.Signal.Distance < 50.0 & !BrakeRelease) { SwitchToP(States.Emergency); } } else { this.SignalPattern.Clear(); } } } break; } } switch (beacon.Type) { case -16777213: // --- compatibility temporary pattern --- { double limit = (double)(beacon.Optional & 4095) / 3.6; double position = (beacon.Optional >> 12); CompatibilityLimit item = new CompatibilityLimit(limit, position); if (!this.CompatibilityLimits.Contains(item)) { this.CompatibilityLimits.Add(item); } } break; case -16777212: // --- compatibility permanent pattern --- if (beacon.Optional == 0) { this.CompatibilityPermanentPattern.Clear(); } else { double limit = (double)beacon.Optional / 3.6; this.CompatibilityPermanentPattern.SetLimit(limit, double.MinValue); } break; } } } }openbve-1.4.0.10/openBVE/OpenBveAts/AtsSx.cs000066400000000000000000000232331171674032100202730ustar00rootroot00000000000000using System; using OpenBveApi.Runtime; namespace Plugin { /// Represents ATS-Sx. internal class AtsSx : Device { // --- enumerations --- /// Represents different states of ATS-Sx. internal enum States { /// The system is disabled. Disabled = 0, /// The system is enabled, but currently suppressed. This will change to States.Initializing once the emergency brakes are released. Suppressed = 1, /// The system is initializing. This will change to States.Chime once the initialization is complete. Initializing = 2, /// The chime is ringing. Chime = 3, /// The system is operating normally. Normal = 4, /// The alarm is ringing. This will change to States.Emergency once the countdown runs out. Alarm = 5, /// The system applies the emergency brakes. Emergency = 6 } // --- members --- /// The underlying train. private Train Train; /// The current state of the system. internal States State; /// The current alarm countdown. /// With States.Initializing, this counts down until the initialization is complete. /// With States.Alarm, this counts down until the emergency brakes are engaged. private double AlarmCountdown; /// The current speed check countdown. private double SpeedCheckCountdown; /// The distance traveled since the last IIYAMA-style Ps2 beacon. private double CompatibilityDistanceAccumulator; /// The location of the farthest known red signal, or System.Double.MinValue. internal double RedSignalLocation; // --- parameters --- /// The duration of the alarm until the emergency brakes are applied. internal readonly double DurationOfAlarm = 5.0; /// The duration of the initialization process. internal readonly double DurationOfInitialization = 3.0; /// The duration of the Sx speed check. internal readonly double DurationOfSpeedCheck = 0.5; // --- constructors --- /// Creates a new instance of this system with default parameters. /// The train. internal AtsSx(Train train) { this.Train = train; this.State = States.Disabled; this.AlarmCountdown = 0.0; this.SpeedCheckCountdown = 0.0; this.RedSignalLocation = 0.0; } // --- inherited functions --- /// Is called when the system should initialize. /// The initialization mode. internal override void Initialize(InitializationModes mode) { if (mode == InitializationModes.OffEmergency) { this.State = States.Suppressed; } else { this.State = States.Normal; } } /// Is called every frame. /// The data. /// Whether the device is blocked or will block subsequent devices. internal override void Elapse(ElapseData data, ref bool blocking) { // --- behavior --- if (this.State == States.Suppressed) { if (data.Handles.BrakeNotch <= this.Train.Specs.BrakeNotches) { this.AlarmCountdown = DurationOfInitialization; this.State = States.Initializing; } } if (this.State == States.Initializing) { this.AlarmCountdown -= data.ElapsedTime.Seconds; if (this.AlarmCountdown <= 0.0) { this.State = States.Chime; } else { data.Handles.BrakeNotch = this.Train.Specs.BrakeNotches + 1; this.Train.Sounds.AtsBell.Play(); } } if (blocking) { if (this.State != States.Disabled & this.State != States.Suppressed) { this.State = States.Normal; } } else { if (this.State == States.Chime) { this.Train.Sounds.AtsChime.Play(); } else if (this.State == States.Alarm) { this.Train.Sounds.AtsBell.Play(); this.AlarmCountdown -= data.ElapsedTime.Seconds; if (this.AlarmCountdown <= 0.0) { this.State = States.Emergency; } } else if (this.State == States.Emergency) { this.Train.Sounds.AtsBell.Play(); data.Handles.BrakeNotch = this.Train.Specs.BrakeNotches + 1; } if (this.SpeedCheckCountdown > 0.0 & data.ElapsedTime.Seconds > 0.0) { this.SpeedCheckCountdown -= data.ElapsedTime.Seconds; } if (this.CompatibilityDistanceAccumulator != 0.0) { this.CompatibilityDistanceAccumulator += data.Vehicle.Speed.MetersPerSecond * data.ElapsedTime.Seconds; if (this.CompatibilityDistanceAccumulator > 27.7) { this.CompatibilityDistanceAccumulator = 0.0; } } if (this.State != States.Disabled & (this.Train.Doors != DoorStates.None | data.Handles.BrakeNotch > 0)) { data.Handles.PowerNotch = 0; } } // --- panel --- if ((this.State == States.Chime | this.State == States.Normal) & !blocking) { this.Train.Panel[256] = 1; } if (this.State == States.Initializing | this.State == States.Alarm) { this.Train.Panel[257] = 1; this.Train.Panel[258] = 1; } else if (this.State == States.Emergency) { int value = (int)data.TotalTime.Milliseconds % 1000 < 500 ? 1 : 0; this.Train.Panel[257] = 2; this.Train.Panel[258] = value; } } /// Is called when the driver changes the reverser. /// The new reverser position. internal override void SetReverser(int reverser) { } /// Is called when the driver changes the power notch. /// The new power notch. internal override void SetPower(int powerNotch) { } /// Is called when the driver changes the brake notch. /// The new brake notch. internal override void SetBrake(int brakeNotch) { } /// Is called when a key is pressed. /// The key. internal override void KeyDown(VirtualKeys key) { switch (key) { case VirtualKeys.S: // --- acknowledge the alarm --- if (this.State == States.Alarm & this.Train.Handles.PowerNotch == 0 & this.Train.Handles.BrakeNotch >= this.Train.Specs.AtsNotch) { this.State = States.Chime; } break; case VirtualKeys.A1: // --- stop the chime --- if (this.State == States.Chime) { this.State = States.Normal; } break; case VirtualKeys.B1: // --- reset the system --- if (this.State == States.Emergency & this.Train.Handles.Reverser == 0 & this.Train.Handles.PowerNotch == 0 & this.Train.Handles.BrakeNotch == this.Train.Specs.BrakeNotches + 1) { this.State = States.Chime; } break; } } /// Is called when a key is released. /// The key. internal override void KeyUp(VirtualKeys key) { } /// Is called when a horn is played or when the music horn is stopped. /// The type of horn. internal override void HornBlow(HornTypes type) { } /// Is called to inform about signals. /// The signal data. internal override void SetSignal(SignalData[] signal) { if (this.RedSignalLocation != double.MinValue) { for (int i = 0; i < signal.Length; i++) { const double visibility = 200.0; if (signal[i].Distance < visibility) { double location = this.Train.State.Location + signal[i].Distance; if (Math.Abs(location - this.RedSignalLocation) < 50.0) { if (signal[i].Aspect != 0) { this.RedSignalLocation = double.MinValue; } } } } } } /// Is called when a beacon is passed. /// The beacon data. internal override void SetBeacon(BeaconData beacon) { if (this.State != States.Disabled & this.State != States.Initializing) { switch (beacon.Type) { case 0: // --- Sx long --- if (beacon.Signal.Aspect == 0) { if (this.State == States.Chime | this.State == States.Normal) { this.AlarmCountdown = DurationOfAlarm; this.State = States.Alarm; UpdateRedSignalLocation(beacon); } } break; case 1: // --- Sx immediate stop --- if (beacon.Signal.Aspect == 0) { if (this.State == States.Chime | this.State == States.Normal | this.State == States.Alarm) { this.State = States.Emergency; } } break; case 2: // --- accidental departure --- if (beacon.Signal.Aspect == 0 & (beacon.Optional == 0 | beacon.Optional >= this.Train.Specs.Cars)) { if (this.State == States.Chime | this.State == States.Normal | this.State == States.Alarm) { this.State = States.Emergency; } } break; } } } // --- private functions --- /// Updates the location of the farthest known red signal from the specified beacon. /// The beacon that holds the distance to a known red signal. private void UpdateRedSignalLocation(BeaconData beacon) { if (beacon.Signal.Distance < 1200.0) { double signalLocation = this.Train.State.Location + beacon.Signal.Distance; if (signalLocation > this.RedSignalLocation) { this.RedSignalLocation = signalLocation; } } } } }openbve-1.4.0.10/openBVE/OpenBveAts/Device.cs000066400000000000000000000040361171674032100204300ustar00rootroot00000000000000using System; using OpenBveApi.Runtime; namespace Plugin { /// Represents an abstract device. internal abstract class Device { /// Is called when the device should initialize. /// The initialization mode. internal abstract void Initialize(InitializationModes mode); /// Is called every frame. /// The data. /// Whether the device is blocked or will block subsequent devices. internal abstract void Elapse(ElapseData data, ref bool blocking); /// Is called when the driver changes the reverser. /// The new reverser position. internal abstract void SetReverser(int reverser); /// Is called when the driver changes the power notch. /// The new power notch. internal abstract void SetPower(int powerNotch); /// Is called when the driver changes the brake notch. /// The new brake notch. internal abstract void SetBrake(int brakeNotch); /// Is called when a key is pressed. /// The key. internal abstract void KeyDown(VirtualKeys key); /// Is called when a key is released. /// The key. internal abstract void KeyUp(VirtualKeys key); /// Is called when a horn is played or when the music horn is stopped. /// The type of horn. internal abstract void HornBlow(HornTypes type); /// Is called to inform about signals. /// The signal data. internal abstract void SetSignal(SignalData[] signal); /// Is called when a beacon is passed. /// The beacon data. internal abstract void SetBeacon(BeaconData beacon); } }openbve-1.4.0.10/openBVE/OpenBveAts/Eb.cs000066400000000000000000000121541171674032100175570ustar00rootroot00000000000000using System; using OpenBveApi.Runtime; namespace Plugin { /// Represents the EB device. internal class Eb : Device { // --- members --- /// The underlying train. private Train Train; /// The counter. This starts at zero and counts up until the EbAlarm or EbBrake constants have been reached. internal double Counter; // --- constants --- /// The time after which the bell is sound. internal readonly double TimeUntilBell = 60.0; /// The time after which the brakes are applied. internal readonly double TimeUntilBrake = 65.0; /// The speed beyond which the EB is active. internal readonly double SpeedThreshold = 5.0 / 3.6; // --- constructors --- /// Creates a new instance of this system. /// The train. internal Eb(Train train) { this.Train = train; this.Counter = 0.0; } // --- inherited functions --- /// Is called when the system should initialize. /// The initialization mode. internal override void Initialize(InitializationModes mode) { this.Counter = 0.0; } /// Is called every frame. /// The data. /// Whether the device is blocked or will block subsequent devices. internal override void Elapse(ElapseData data, ref bool blocking) { // --- behavior --- if (!blocking) { if (Math.Abs(data.Vehicle.Speed.KilometersPerHour) > SpeedThreshold | this.Counter >= TimeUntilBell) { this.Counter += data.ElapsedTime.Seconds; if (this.Counter >= TimeUntilBrake) { if (this.Train.AtsSx != null) { if (this.Train.AtsSx.State != AtsSx.States.Disabled) { this.Train.AtsSx.State = AtsSx.States.Emergency; if (this.Train.AtsP != null && (this.Train.AtsP.State == AtsP.States.Normal | this.Train.AtsP.State == AtsP.States.Pattern | this.Train.AtsP.State == AtsP.States.Brake | this.Train.AtsP.State == AtsP.States.Service | this.Train.AtsP.State == AtsP.States.Emergency)) { this.Train.AtsP.State = AtsP.States.Standby; } if (this.Train.Atc != null && (this.Train.Atc.State == Atc.States.Normal | this.Train.Atc.State == Atc.States.Service | this.Train.Atc.State == Atc.States.Emergency)) { this.Train.Atc.State = Atc.States.Ats; } this.Counter = 0.0; } } else { this.Train.Sounds.AtsBell.Play(); data.Handles.BrakeNotch = this.Train.Specs.BrakeNotches + 1; } } else if (this.Counter >= TimeUntilBell) { this.Train.Sounds.Eb.Play(); } } else { this.Counter = 0.0; } } else { this.Counter = 0.0; } // --- panel --- if (this.Counter >= TimeUntilBrake) { int value = (int)data.TotalTime.Milliseconds % 1000 < 500 ? 1 : 0; this.Train.Panel[8] = value; this.Train.Panel[270] = value; } else if (this.Counter >= TimeUntilBell) { this.Train.Panel[8] = 1; this.Train.Panel[270] = 1; } } /// Is called when the driver changes the reverser. /// The new reverser position. internal override void SetReverser(int reverser) { if (this.Counter < TimeUntilBell) { this.Counter = 0.0; } } /// Is called when the driver changes the power notch. /// The new power notch. internal override void SetPower(int powerNotch) { if (this.Counter < TimeUntilBell) { this.Counter = 0.0; } } /// Is called when the driver changes the brake notch. /// The new brake notch. internal override void SetBrake(int brakeNotch) { if (this.Counter < TimeUntilBell) { this.Counter = 0.0; } } /// Is called when a key is pressed. /// The key. internal override void KeyDown(VirtualKeys key) { switch (key) { case VirtualKeys.A2: // --- acknowledge the EB --- if (this.Counter >= TimeUntilBell) { this.Counter = 0.0; } break; } } /// Is called when a key is released. /// The key. internal override void KeyUp(VirtualKeys key) { } /// Is called when a horn is played or when the music horn is stopped. /// The type of horn. internal override void HornBlow(HornTypes type) { if (this.Counter < TimeUntilBrake) { this.Counter = 0.0; } } /// Is called to inform about signals. /// The signal data. internal override void SetSignal(SignalData[] signal) { } /// Is called when a beacon is passed. /// The beacon data. internal override void SetBeacon(BeaconData beacon) { } } }openbve-1.4.0.10/openBVE/OpenBveAts/OpenBveAts.csproj000066400000000000000000000055261171674032100221370ustar00rootroot00000000000000 {15142E7B-35F5-4E81-AA18-79C4D60B4C26} Debug x86 Library OpenBveAts OpenBveAts v4.0 Properties C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis False False 4 false Client bin\Debug\ true Full False True DEBUG;TRACE bin\Release\ false None True False TRACE False Auto 4194304 AnyCPU 4096 {27134980-4415-4375-A564-40A9014DFA5F} OpenBveApi False openbve-1.4.0.10/openBVE/OpenBveAts/Plugin.cs000066400000000000000000000111261171674032100204650ustar00rootroot00000000000000using System; using System.IO; using OpenBveApi.Runtime; namespace Plugin { /// The interface to be implemented by the plugin. public class Plugin : IRuntime { // --- members --- /// The train that is simulated by this plugin. private Train Train = null; // --- interface functions --- /// Is called when the plugin is loaded. /// The properties supplied to the plugin on loading. /// Whether the plugin was loaded successfully. public bool Load(LoadProperties properties) { properties.Panel = new int[272]; properties.AISupport = AISupport.Basic; this.Train = new Train(properties.Panel, properties.PlaySound); try { string file = OpenBveApi.Path.CombineFile(properties.TrainFolder, "train.dat"); if (!this.Train.LoadTrainDatFile(file)) { this.Train.AtsSx = new AtsSx(this.Train); this.Train.Devices = new Device[] { this.Train.AtsSx }; } } catch { this.Train.AtsSx = new AtsSx(this.Train); this.Train.Devices = new Device[] { this.Train.AtsSx }; } return true; } /// Is called when the plugin is unloaded. public void Unload() { } /// Is called after loading to inform the plugin about the specifications of the train. /// The specifications of the train. public void SetVehicleSpecs(VehicleSpecs specs) { this.Train.Specs = specs; } /// Is called when the plugin should initialize or reinitialize. /// The mode of initialization. public void Initialize(InitializationModes mode) { this.Train.Initialize(mode); } /// Is called every frame. /// The data passed to the plugin. public void Elapse(ElapseData data) { this.Train.Elapse(data); } /// Is called when the driver changes the reverser. /// The new reverser position. public void SetReverser(int reverser) { this.Train.SetReverser(reverser); } /// Is called when the driver changes the power notch. /// The new power notch. public void SetPower(int powerNotch) { this.Train.SetPower(powerNotch); } /// Is called when the driver changes the brake notch. /// The new brake notch. public void SetBrake(int brakeNotch) { this.Train.SetBrake(brakeNotch); } /// Is called when a virtual key is pressed. /// The virtual key that was pressed. public void KeyDown(VirtualKeys key) { this.Train.KeyDown(key); } /// Is called when a virtual key is released. /// The virtual key that was released. public void KeyUp(VirtualKeys key) { this.Train.KeyUp(key); } /// Is called when a horn is played or when the music horn is stopped. /// The type of horn. public void HornBlow(HornTypes type) { this.Train.HornBlow(type); } /// Is called when the state of the doors changes. /// The old state of the doors. /// The new state of the doors. public void DoorChange(DoorStates oldState, DoorStates newState) { this.Train.Doors = newState; this.Train.DoorChange(oldState, newState); } /// Is called when the aspect in the current or in any of the upcoming sections changes, or when passing section boundaries. /// Signal information per section. In the array, index 0 is the current section, index 1 the upcoming section, and so on. /// The signal array is guaranteed to have at least one element. When accessing elements other than index 0, you must check the bounds of the array first. public void SetSignal(SignalData[] signal) { this.Train.SetSignal(signal); } /// Is called when the train passes a beacon. /// The beacon data. public void SetBeacon(BeaconData beacon) { this.Train.SetBeacon(beacon); } /// Is called when the plugin should perform the AI. /// The AI data. public void PerformAI(AIData data) { this.Train.AI.Perform(data); } } }openbve-1.4.0.10/openBVE/OpenBveAts/Properties/000077500000000000000000000000001171674032100210335ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/OpenBveAts/Properties/AssemblyInfo.cs000066400000000000000000000010761171674032100237610ustar00rootroot00000000000000using System; using System.Reflection; using System.Runtime.InteropServices; /* Created and released into the public domain by odakyufan * http://odakyufan.zxq.net/ */ [assembly: AssemblyTitle("OpenBveAts")] [assembly: AssemblyDescription("The default ATS/ATC train plugin used by openBVE if a train does not come with its own plugin.")] [assembly: AssemblyProduct("openBVE")] [assembly: AssemblyCopyright("(Public Domain) http://trainsimframework.org/")] [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] [assembly: AssemblyVersion("0.0.0.0")]openbve-1.4.0.10/openBVE/OpenBveAts/Sounds.cs000066400000000000000000000044301171674032100205020ustar00rootroot00000000000000using System; using OpenBveApi.Runtime; namespace Plugin { internal class Sounds { // --- classes --- /// Represents a looping sound. internal class Sound { internal int Index; internal SoundHandle Handle; internal bool IsToBePlayed; internal Sound(int index) { this.Index = index; this.Handle = null; } internal void Play() { this.IsToBePlayed = true; } } // --- members --- private PlaySoundDelegate PlaySound; // --- looping sounds --- internal Sound AtsBell; internal Sound AtsChime; internal Sound Eb; private Sound[] LoopingSounds; // --- play once sounds --- internal Sound AtsPBell; internal Sound AtcBell; internal Sound ToAts; internal Sound ToAtc; private Sound[] PlayOnceSounds; // --- constructors --- /// Creates a new instance of sounds. /// The delegate to the function to play sounds. internal Sounds(PlaySoundDelegate playSound) { this.PlaySound = playSound; // --- looping --- this.AtsBell = new Sound(0); this.AtsChime = new Sound(1); this.Eb = new Sounds.Sound(5); this.LoopingSounds = new Sound[] { this.AtsBell, this.AtsChime, this.Eb }; // --- play once --- this.AtsPBell = new Sound(2); this.AtcBell = new Sound(2); this.ToAts = new Sound(3); this.ToAtc = new Sound(4); this.PlayOnceSounds = new Sound[] { this.AtsPBell, this.AtcBell, this.ToAts, this.ToAtc }; } // --- functions --- /// Is called every frame. /// The data. internal void Elapse(ElapseData data) { foreach (Sound sound in this.LoopingSounds) { if (sound.IsToBePlayed) { if (sound.Handle == null || sound.Handle.Stopped) { sound.Handle = PlaySound(sound.Index, 1.0, 1.0, true); } } else { if (sound.Handle != null && sound.Handle.Playing) { sound.Handle.Stop(); } } sound.IsToBePlayed = false; } foreach (Sound sound in this.PlayOnceSounds) { if (sound.IsToBePlayed) { PlaySound(sound.Index, 1.0, 1.0, false); sound.IsToBePlayed = false; } } } } }openbve-1.4.0.10/openBVE/OpenBveAts/Train.cs000066400000000000000000000256471171674032100203210ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using OpenBveApi.Runtime; namespace Plugin { /// Represents a train that is simulated by this plugin. internal class Train { // --- classes and enumerations --- /// Represents handles that can only be read from. internal class ReadOnlyHandles { // --- members --- /// The reverser position. private int MyReverser; /// The power notch. private int MyPowerNotch; /// The brake notch. private int MyBrakeNotch; /// Whether the const speed system is enabled. private bool MyConstSpeed; // --- properties --- /// Gets or sets the reverser position. internal int Reverser { get { return this.MyReverser; } } /// Gets or sets the power notch. internal int PowerNotch { get { return this.MyPowerNotch; } } /// Gets or sets the brake notch. internal int BrakeNotch { get { return this.MyBrakeNotch; } } /// Gets or sets whether the const speed system is enabled. internal bool ConstSpeed { get { return this.MyConstSpeed; } } // --- constructors --- /// Creates a new instance of this class. /// The handles internal ReadOnlyHandles(Handles handles) { this.MyReverser = handles.Reverser; this.MyPowerNotch = handles.PowerNotch; this.MyBrakeNotch = handles.BrakeNotch; this.MyConstSpeed = handles.ConstSpeed; } } // --- plugin --- /// Whether the plugin is currently initializing. This happens in-between Initialize and Elapse calls, for example when jumping to a station from the menu. internal bool PluginInitializing; // --- train --- /// The train specifications. internal VehicleSpecs Specs; /// The current state of the train. internal VehicleState State; /// The driver handles at the last Elapse call. internal ReadOnlyHandles Handles; /// The current state of the doors. internal DoorStates Doors; // --- panel and sound --- /// The panel variables. internal int[] Panel; /// The sounds used on this train. internal Sounds Sounds; // --- AI --- /// The AI component that drives the train. internal AI AI; // --- devices --- /// The ATS-Sx device, or a null reference if not installed. internal AtsSx AtsSx; /// The ATS-P device, or a null reference if not installed. internal AtsP AtsP; /// The ATC device, or a null reference if not installed. internal Atc Atc; /// The EB device, or a null reference if not installed. internal Eb Eb; /// A list of all the devices installed on this train. The devices must be in the order EB, ATC, ATS-P and ATS-Sx. internal Device[] Devices; // --- constructors --- /// Creates a new train without any devices installed. /// The array of panel variables. /// The delegate to play sounds. internal Train(int[] panel, PlaySoundDelegate playSound) { this.PluginInitializing = false; this.Specs = new VehicleSpecs(0, BrakeTypes.ElectromagneticStraightAirBrake, 0, false, 0); this.State = new VehicleState(0.0, new Speed(0.0), 0.0, 0.0, 0.0, 0.0, 0.0); this.Handles = new ReadOnlyHandles(new Handles(0, 0, 0, false)); this.Doors = DoorStates.None; this.Panel = panel; this.Sounds = new Sounds(playSound); this.AI = new AI(this); } // --- functions --- /// Sets up the devices from the specified train.dat file. /// The train.dat file. /// Whether loading the train.dat file was successful. internal bool LoadTrainDatFile(string file) { string[] lines = File.ReadAllLines(file, Encoding.UTF8); for (int i = 0; i < lines.Length; i++) { int semicolon = lines[i].IndexOf(';'); if (semicolon >= 0) { lines[i] = lines[i].Substring(0, semicolon).Trim(); } else { lines[i] = lines[i].Trim(); } } for (int i = 0; i < lines.Length; i++) { if (lines[i].Equals("#DEVICE", StringComparison.OrdinalIgnoreCase)) { if (i < lines.Length - 1) { int value = int.Parse(lines[i + 1], NumberStyles.Integer, CultureInfo.InvariantCulture); if (value == 0) { this.AtsSx = new AtsSx(this); } else if (value == 1) { this.AtsSx = new AtsSx(this); this.AtsP = new AtsP(this); } } if (i < lines.Length - 2) { int value = int.Parse(lines[i + 2], NumberStyles.Integer, CultureInfo.InvariantCulture); if (value == 1) { this.Atc = new Atc(this, false); } else if (value == 2) { this.Atc = new Atc(this, true); } } if (i < lines.Length - 3) { int value = int.Parse(lines[i + 3], NumberStyles.Integer, CultureInfo.InvariantCulture); if (value == 1) { this.Eb = new Eb(this); } } } } // --- devices --- List devices = new List(); if (this.Eb != null) { devices.Add(this.Eb); } if (this.Atc != null) { devices.Add(this.Atc); } if (this.AtsP != null) { devices.Add(this.AtsP); } if (this.AtsSx != null) { devices.Add(this.AtsSx); } this.Devices = devices.ToArray(); return true; } /// Is called when the system should initialize. /// The initialization mode. internal void Initialize(InitializationModes mode) { this.PluginInitializing = true; for (int i = this.Devices.Length - 1; i >= 0; i--) { this.Devices[i].Initialize(mode); } } /// Is called every frame. /// The data. internal void Elapse(ElapseData data) { this.PluginInitializing = false; if (data.ElapsedTime.Seconds > 0.0 & data.ElapsedTime.Seconds < 1.0) { // --- panel --- for (int i = 0; i < this.Panel.Length; i++) { this.Panel[i] = 0; } // --- devices --- this.State = data.Vehicle; this.Handles = new ReadOnlyHandles(data.Handles); bool blocking = false; foreach (Device device in this.Devices) { device.Elapse(data, ref blocking); } // --- panel --- int seconds = (int)Math.Floor(data.TotalTime.Seconds); this.Panel[10] = (seconds / 3600) % 24; this.Panel[11] = (seconds / 60) % 60; this.Panel[12] = seconds % 60; this.Panel[269] = data.Handles.ConstSpeed ? 1 : 0; if (data.Handles.Reverser != 0 & (this.Handles.PowerNotch > 0 & this.Handles.BrakeNotch == 0 | this.Handles.PowerNotch == 0 & this.Handles.BrakeNotch == 1 & this.Specs.HasHoldBrake)) { this.Panel[100] = 1; } if (data.Handles.BrakeNotch >= this.Specs.AtsNotch & data.Handles.BrakeNotch <= this.Specs.BrakeNotches | data.Handles.Reverser != 0 & data.Handles.BrakeNotch == 1 & this.Specs.HasHoldBrake) { this.Panel[101] = 1; } // --- sound --- this.Sounds.Elapse(data); } } /// Is called when the driver changes the reverser. /// The new reverser position. internal void SetReverser(int reverser) { foreach (Device device in this.Devices) { device.SetReverser(reverser); } } /// Is called when the driver changes the power notch. /// The new power notch. internal void SetPower(int powerNotch) { foreach (Device device in this.Devices) { device.SetPower(powerNotch); } } /// Is called when the driver changes the brake notch. /// The new brake notch. internal void SetBrake(int brakeNotch) { foreach (Device device in this.Devices) { device.SetBrake(brakeNotch); } } /// Is called when a key is pressed. /// The key. internal void KeyDown(VirtualKeys key) { if (key == VirtualKeys.D) { // --- enable safety systems --- if (this.AtsSx != null) { if (this.AtsSx.State == AtsSx.States.Disabled) { this.AtsSx.State = AtsSx.States.Suppressed; } } if (this.AtsP != null) { if (this.AtsP.State == AtsP.States.Disabled) { this.AtsP.State = AtsP.States.Suppressed; } } if (this.Atc != null) { if (this.Atc.State == Atc.States.Disabled) { this.Atc.State = Atc.States.Suppressed; } } } else if (key == VirtualKeys.E) { // --- disable safety systems --- if (this.AtsSx != null) { if (this.AtsSx.State != AtsSx.States.Disabled) { this.AtsSx.State = AtsSx.States.Disabled; } } if (this.AtsP != null) { if (this.AtsP.State != AtsP.States.Disabled) { this.AtsP.State = AtsP.States.Disabled; } } if (this.Atc != null) { if (this.Atc.State != Atc.States.Disabled) { this.Atc.State = Atc.States.Disabled; } } } else { // --- other functions --- foreach (Device device in this.Devices) { device.KeyDown(key); } } } /// Is called when a key is released. /// The key. internal void KeyUp(VirtualKeys key) { foreach (Device device in this.Devices) { device.KeyUp(key); } } /// Is called when a horn is played or when the music horn is stopped. /// The type of horn. internal void HornBlow(HornTypes type) { foreach (Device device in this.Devices) { device.HornBlow(type); } } /// Is called when the state of the doors changes. /// The old state of the doors. /// The new state of the doors. public void DoorChange(DoorStates oldState, DoorStates newState) { } /// Is called to inform about signals. /// The signal data. internal void SetSignal(SignalData[] signal) { foreach (Device device in this.Devices) { device.SetSignal(signal); } } /// Is called when a beacon is passed. /// The beacon data. internal void SetBeacon(BeaconData beacon) { foreach (Device device in this.Devices) { device.SetBeacon(beacon); } } } }openbve-1.4.0.10/openBVE/Sound.Flac/000077500000000000000000000000001171674032100166255ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/Sound.Flac/BitReader.cs000066400000000000000000000317021171674032100210200ustar00rootroot00000000000000using System; using System.IO; namespace Flac { /// Represents a bit reader using a big-endian bit and byte order. internal class BitReader { // --- members --- /// The array of bytes from which is read. internal byte[] Bytes; /// The byte position from which will be read next. internal int BytePosition; /// The bit position in the current byte from which will be read next. Values can range from 7 (most-significant bit) to 0 (least-significant bit). internal int BitPosition; // --- constructors --- /// Creates a new bit reader. /// The array of bytes from which is read. internal BitReader(byte[] bytes) { this.Bytes = bytes; this.BytePosition = 0; this.BitPosition = 7; } // --- functions --- /// Checks whether the end of the stream has been reached. /// Whether the end of the stream has been reached. internal bool EndOfStream() { return this.BytePosition == this.Bytes.Length; } /// Aligns the reader with the next byte boundary. internal void Align() { if (this.BitPosition != 7) { this.BitPosition = 7; this.BytePosition++; } } /// Reads a single bit. /// The bit. internal uint ReadBit() { if (this.BitPosition == 0) { this.BitPosition = 7; return (uint)this.Bytes[this.BytePosition++] & 1; } else { return ((uint)this.Bytes[this.BytePosition] >> (this.BitPosition--)) & 1; } } /// Reads the specified number of bits. /// The number of bits. Must be between 0 and 32. /// The bits. internal uint ReadBits(int number) { if (this.BitPosition == 7) { /* The start is on a byte boundary. */ if ((number & 7) == 0) { /* The start and end are on a byte boundary. */ if (number < 16) { if (number == 0) { return 0; } else { return (uint)this.Bytes[this.BytePosition++]; } } else { if (number == 16) { return ((uint)this.Bytes[this.BytePosition++] << 8) | (uint)this.Bytes[this.BytePosition++]; } else if (number == 24) { return ((uint)this.Bytes[this.BytePosition++] << 16) | ((uint)this.Bytes[this.BytePosition++] << 8) | (uint)this.Bytes[this.BytePosition++]; } else { return ((uint)this.Bytes[this.BytePosition++] << 24) | ((uint)this.Bytes[this.BytePosition++] << 16) | ((uint)this.Bytes[this.BytePosition++] << 8) | (uint)this.Bytes[this.BytePosition++]; } } } else { /* The start is on a byte boundary, but the end is not. */ if (number < 16) { if (number < 8) { this.BitPosition = 7 - number; return (uint)this.Bytes[this.BytePosition] >> (8 - number); } else { this.BitPosition = 15 - number; return ((uint)this.Bytes[this.BytePosition++] << (number - 8)) | ((uint)this.Bytes[this.BytePosition] >> (16 - number)); } } else { if (number < 24) { this.BitPosition = 23 - number; return ((uint)this.Bytes[this.BytePosition++] << (number - 8)) | ((uint)this.Bytes[this.BytePosition++] << (number - 16)) | ((uint)this.Bytes[this.BytePosition] >> (24 - number)); } else { this.BitPosition = 31 - number; return ((uint)this.Bytes[this.BytePosition++] << (number - 8)) | ((uint)this.Bytes[this.BytePosition++] << (number - 16)) | ((uint)this.Bytes[this.BytePosition++] << (number - 24)) | ((uint)this.Bytes[this.BytePosition] >> (32 - number)); } } } } else { /* The start is not on a byte boundary. */ if (((number - this.BitPosition - 1) & 7) == 0) { /* The start is not on a byte boundary, but the end is. */ if (number < 16) { if (number < 8) { this.BitPosition = 7; return (uint)this.Bytes[this.BytePosition++] & (((uint)1 << number) - 1); } else { this.BitPosition = 7; return (((uint)this.Bytes[this.BytePosition++] & (((uint)1 << (number - 8)) - 1)) << 8) | (uint)this.Bytes[this.BytePosition++]; } } else { if (number < 24) { this.BitPosition = 7; return (((uint)this.Bytes[this.BytePosition++] & (((uint)1 << (number - 16)) - 1)) << 16) | ((uint)this.Bytes[this.BytePosition++] << 8) | (uint)this.Bytes[this.BytePosition++]; } else { this.BitPosition = 7; return (((uint)this.Bytes[this.BytePosition++] & (((uint)1 << (number - 24)) - 1)) << 24) | ((uint)this.Bytes[this.BytePosition++] << 16) | ((uint)this.Bytes[this.BytePosition++] << 8) | (uint)this.Bytes[this.BytePosition++]; } } } else { /* Neither start nor end are on a byte boundary. */ int bytes = (number - this.BitPosition + 15) >> 3; if (bytes < 3) { if (bytes == 1) { this.BitPosition -= number; return ((uint)this.Bytes[this.BytePosition] >> (this.BitPosition + 1)) & (((uint)1 << number) - 1); } else { this.BitPosition -= number - 8; return (((uint)this.Bytes[this.BytePosition++] & (((uint)1 << (this.BitPosition + number - 7)) - 1)) << (7 - this.BitPosition)) | ((uint)this.Bytes[this.BytePosition] >> (this.BitPosition + 1)); } } else if (bytes < 5) { if (bytes == 3) { this.BitPosition -= number - 16; return (((uint)this.Bytes[this.BytePosition++] & (((uint)1 << (this.BitPosition + number - 15)) - 1)) << (15 - this.BitPosition)) | ((uint)this.Bytes[this.BytePosition++] << (7 - this.BitPosition)) | ((uint)this.Bytes[this.BytePosition] >> (this.BitPosition + 1)); } else { this.BitPosition -= number - 24; return (((uint)this.Bytes[this.BytePosition++] & (((uint)1 << (this.BitPosition + number - 23)) - 1)) << (23 - this.BitPosition)) | ((uint)this.Bytes[this.BytePosition++] << (15 - this.BitPosition)) | ((uint)this.Bytes[this.BytePosition++] << (7 - this.BitPosition)) | ((uint)this.Bytes[this.BytePosition] >> (this.BitPosition + 1)); } } else { this.BitPosition -= number - 32; return (((uint)this.Bytes[this.BytePosition++] & (((uint)1 << (this.BitPosition + number - 31)) - 1)) << (31 - this.BitPosition)) | ((uint)this.Bytes[this.BytePosition++] << (23 - this.BitPosition)) | ((uint)this.Bytes[this.BytePosition++] << (15 - this.BitPosition)) | ((uint)this.Bytes[this.BytePosition++] << (7 - this.BitPosition)) | ((uint)this.Bytes[this.BytePosition] >> (this.BitPosition + 1)); } } } } /// Reads an unsigned 8-bit integer. /// The unsigned 8-bit integer. internal uint ReadByte() { if (this.BitPosition == 7) { return this.Bytes[this.BytePosition++]; } else { return this.ReadBits(8); } } /// Reads an unsigned 16-bit integer. /// The unsigned 16-bit integer. internal uint ReadUInt16BE() { if (this.BitPosition == 7) { return ((uint)this.Bytes[this.BytePosition++] << 8) | (uint)this.Bytes[this.BytePosition++]; } else { return this.ReadBits(16); } } /// Reads an unsigned 24-bit integer. /// The unsigned 24-bit integer. internal uint ReadUInt24BE() { if (this.BitPosition == 7) { return ((uint)this.Bytes[this.BytePosition++] << 16) | ((uint)this.Bytes[this.BytePosition++] << 8) | (uint)this.Bytes[this.BytePosition++]; } else { return this.ReadBits(24); } } /// Reads an unsigned 32-bit integer. /// The unsigned 32-bit integer. internal uint ReadUInt32BE() { if (this.BitPosition == 7) { return ((uint)this.Bytes[this.BytePosition++] << 24) | ((uint)this.Bytes[this.BytePosition++] << 16) | ((uint)this.Bytes[this.BytePosition++] << 8) | (uint)this.Bytes[this.BytePosition++]; } else { return this.ReadBits(32); } } /// Reads the specified number of bytes. /// The number of bytes. /// The bytes. internal byte[] ReadBytes(int number) { if (this.BitPosition == 7) { byte[] bytes = new byte[number]; for (int i = 0; i < number; i++) { bytes[i] = this.Bytes[this.BytePosition++]; } return bytes; } else { byte[] bytes = new byte[number]; for (int i = 0; i < number; i++) { bytes[i] = (byte)this.ReadBits(8); } return bytes; } } /// Reads a unary-encoded integer according to the scheme 0=0, 1=11, 2=101, 3=1001, 4=10001, etc. /// The integer. internal uint ReadUnaryEncodedInteger() { if (this.ReadBit() == 0) { return 0; } else { uint value = 1; while (this.ReadBit() == 0) { value++; } return value; } } /// Reads a signed rice-encoded integer. /// The rice parameter. /// The signed integer. internal int ReadRiceEncodedInteger(int riceParameter) { uint quotient = 0; while (this.ReadBit() == 0) { quotient++; } uint mod = this.ReadBits(riceParameter); uint value = (quotient << (int)riceParameter) | mod; if ((value & 1) == 0) { return (int)(value >> 1); } else { return ~(int)(value >> 1); } } /// Reads an unsigned integer encoded according to the extended UTF-8 scheme with up to 36 bits. /// The integer. internal ulong ReadUTF8EncodedInteger() { ulong a = (ulong)this.ReadByte(); if ((a & 0x80) == 0) { return a & 0x7F; } else if ((a & 0xE0) == 0xC0) { ulong b = (ulong)this.ReadByte(); if ((b & 0xC0) != 0x80) { throw new InvalidDataException(); } return ((a & 0x1F) << 6) | (b & 0x3F); } else if ((a & 0xF0) == 0xE0) { ulong b = (ulong)this.ReadByte(); if ((b & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong c = (ulong)this.ReadByte(); if ((c & 0xC0) != 0x80) { throw new InvalidDataException(); } return ((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F); } else if ((a & 0xF8) == 0xF0) { ulong b = (ulong)this.ReadByte(); if ((b & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong c = (ulong)this.ReadByte(); if ((c & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong d = (ulong)this.ReadByte(); if ((d & 0xC0) != 0x80) { throw new InvalidDataException(); } return ((a & 0x07) << 18) | ((b & 0x3F) << 12) | ((c & 0x3F) << 6) | (d & 0x3F); } else if ((a & 0xFC) == 0xF8) { ulong b = (ulong)this.ReadByte(); if ((b & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong c = (ulong)this.ReadByte(); if ((c & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong d = (ulong)this.ReadByte(); if ((d & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong e = (ulong)this.ReadByte(); if ((e & 0xC0) != 0x80) { throw new InvalidDataException(); } return ((a & 0x03) << 24) | ((b & 0x3F) << 18) | ((c & 0x3F) << 12) | ((d & 0x3F) << 6) | (e & 0x3F); } else if ((a & 0xFE) == 0xFC) { ulong b = (ulong)this.ReadByte(); if ((b & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong c = (ulong)this.ReadByte(); if ((c & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong d = (ulong)this.ReadByte(); if ((d & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong e = (ulong)this.ReadByte(); if ((e & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong f = (ulong)this.ReadByte(); if ((f & 0xC0) != 0x80) { throw new InvalidDataException(); } return ((a & 0x01) << 30) | ((b & 0x3F) << 24) | ((c & 0x3F) << 18) | ((d & 0x3F) << 12) | ((e & 0x3F) << 6) | (f & 0x3F); } else if (a == 0xFE) { ulong b = (ulong)this.ReadByte(); if ((b & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong c = (ulong)this.ReadByte(); if ((c & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong d = (ulong)this.ReadByte(); if ((d & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong e = (ulong)this.ReadByte(); if ((e & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong f = (ulong)this.ReadByte(); if ((f & 0xC0) != 0x80) { throw new InvalidDataException(); } ulong g = (ulong)this.ReadByte(); if ((g & 0xC0) != 0x80) { throw new InvalidDataException(); } return ((b & 0x3F) << 30) | ((c & 0x3F) << 24) | ((d & 0x3F) << 18) | ((e & 0x3F) << 12) | ((f & 0x3F) << 6) | (g & 0x3F); } else { throw new InvalidDataException(); } } } }openbve-1.4.0.10/openBVE/Sound.Flac/Crc16.cs000066400000000000000000000014461171674032100200370ustar00rootroot00000000000000using System; namespace Flac { internal static class Crc16 { private const ushort Polynomial = 0x8005; private static ushort[] Table = new ushort[256]; internal static ushort ComputeHash(byte[] bytes, int offset, int count) { unchecked { ushort crc = 0; for (int i = offset; i < offset + count; i++) { crc = (ushort)((crc << 8) ^ Table[((crc >> 8) ^ bytes[i])]); } return crc; } } static Crc16() { unchecked { for (int i = 0; i < 256; i++) { ushort a = 0; ushort b = (ushort)(i << 8); for (int j = 0; j < 8; j++) { if (((a ^ b) & 0x8000) != 0) { a = (ushort)((a << 1) ^ Polynomial); } else { a <<= 1; } b <<= 1; } Table[i] = a; } } } } }openbve-1.4.0.10/openBVE/Sound.Flac/Crc8.cs000066400000000000000000000012661171674032100177600ustar00rootroot00000000000000using System; namespace Flac { internal static class Crc8 { private const byte Polynomial = 0x07; private static byte[] Table = new byte[256]; internal static byte ComputeHash(byte[] bytes, int offset, int count) { unchecked { byte crc = 0; for (int i = offset; i < offset + count; i++) { crc = Table[crc ^ bytes[i]]; } return crc; } } static Crc8() { unchecked { for (int i = 0; i < 256; i++) { int a = i; for (int j = 0; j < 8; j++) { if ((a & 0x80) != 0) { a = (a << 1) ^ Polynomial; } else { a <<= 1; } } Table[i] = (byte)a; } } } } }openbve-1.4.0.10/openBVE/Sound.Flac/Decoder.cs000066400000000000000000000526051171674032100205310ustar00rootroot00000000000000#define CHECKSUMS // whether to check for CRC-8, CRC-16 and MD5 using System; using System.IO; using System.Security.Cryptography; namespace Flac { internal static class Decoder { /// Decodes the specified native FLAC file and extracts the raw samples. /// The path to the native FLAC file. /// Receives the sample rate in Hz. /// Receives the number of bits per sample. /// The raw samples per channel, padded into the most-significant bits (makes all samples 32-bits wide and signed). internal static int[][] Decode(string file, out int sampleRate, out int bitsPerSample) { unchecked { byte[] bytes = File.ReadAllBytes(file); BitReader reader = new BitReader(bytes); // --- identifier --- if (reader.ReadUInt32BE() != 0x664C6143) { throw new InvalidDataException(); } sampleRate = 0; bitsPerSample = 0; int numberOfChannels = 0; int totalNumberOfSamples = 0; bool streaminfoPresent = false; byte[] md5 = null; // --- metadata blocks --- while (true) { // --- block header --- bool lastBlock = reader.ReadBit() == 1; int blockType = (int)reader.ReadBits(7); int blockLength = (int)reader.ReadUInt24BE(); if (blockType == 0) { // --- STREAMINFO --- if (streaminfoPresent) { throw new InvalidDataException(); } if (blockLength != 34) { throw new InvalidDataException(); } int minimumBlockSize = (int)reader.ReadUInt16BE(); if (minimumBlockSize < 16) { throw new InvalidDataException(); } int maximumBlockSize = (int)reader.ReadUInt16BE(); if (minimumBlockSize > maximumBlockSize | maximumBlockSize > 65535) { throw new InvalidDataException(); } int minimumFrameSize = (int)reader.ReadUInt24BE(); int maximumFrameSize = (int)reader.ReadUInt24BE(); if (minimumFrameSize > maximumFrameSize & maximumFrameSize != 0) { throw new InvalidDataException(); } sampleRate = (int)reader.ReadBits(20); if (sampleRate == 0 | sampleRate > 655350) { throw new InvalidDataException(); } numberOfChannels = (int)reader.ReadBits(3) + 1; bitsPerSample = (int)reader.ReadBits(5) + 1; if (bitsPerSample < 4) { throw new InvalidDataException(); } /* total number of samples: 36 bits * only the lower 31 bits are supported */ if (reader.ReadBits(4) != 0) { throw new NotSupportedException("Too many samples for this decoder."); } totalNumberOfSamples = (int)reader.ReadUInt32BE(); if (totalNumberOfSamples < 0) { throw new NotSupportedException("Too many samples for this decoder."); } md5 = reader.ReadBytes(16); streaminfoPresent = true; } else if (blockType >= 1 & blockType <= 6) { // --- ignored --- reader.BytePosition += (int)blockLength; } else { // --- invalid --- throw new InvalidDataException(); } if (lastBlock) { break; } } // --- prepare samples per channel --- if (!streaminfoPresent) { throw new InvalidDataException(); } int[][] samples = new int[numberOfChannels][]; int sampleCount = totalNumberOfSamples != 0 ? (int)totalNumberOfSamples : 65536; for (int i = 0; i < numberOfChannels; i++) { samples[i] = new int[sampleCount]; } int samplesUsed = 0; // --- frames --- while (!reader.EndOfStream()) { // --- frame header --- int frameHeaderPosition = reader.BytePosition; uint syncCode = reader.ReadBits(14); if (syncCode != 0x3FFE) { throw new InvalidDataException(); } uint reserved1 = reader.ReadBit(); if (reserved1 != 0) { throw new InvalidDataException(); } uint blockingStrategy = reader.ReadBit(); int blockNumberOfSamples = (int)reader.ReadBits(4); if (blockNumberOfSamples == 0) { throw new InvalidDataException(); } else if (blockNumberOfSamples == 1) { blockNumberOfSamples = 192; } else if (blockNumberOfSamples >= 2 & blockNumberOfSamples <= 5) { blockNumberOfSamples = 576 << (int)(blockNumberOfSamples - 2); } else if (blockNumberOfSamples >= 8 & blockNumberOfSamples <= 15) { blockNumberOfSamples = 256 << (int)(blockNumberOfSamples - 8); } int blockSampleRate = (int)reader.ReadBits(4); if (blockSampleRate == 0) { blockSampleRate = sampleRate; } else if (blockSampleRate == 1) { blockSampleRate = 88200; } else if (blockSampleRate == 2) { blockSampleRate = 176400; } else if (blockSampleRate == 3) { blockSampleRate = 192000; } else if (blockSampleRate == 4) { blockSampleRate = 8000; } else if (blockSampleRate == 5) { blockSampleRate = 16000; } else if (blockSampleRate == 6) { blockSampleRate = 22050; } else if (blockSampleRate == 7) { blockSampleRate = 24000; } else if (blockSampleRate == 8) { blockSampleRate = 32000; } else if (blockSampleRate == 9) { blockSampleRate = 44100; } else if (blockSampleRate == 10) { blockSampleRate = 48000; } else if (blockSampleRate == 11) { blockSampleRate = 96000; } else if (blockSampleRate == 15) { throw new InvalidDataException(); } uint channelAssignment = reader.ReadBits(4); int blockBitsPerSample = (int)reader.ReadBits(3); if (blockBitsPerSample == 0) { blockBitsPerSample = bitsPerSample; } else if (blockBitsPerSample == 1) { blockBitsPerSample = 8; } else if (blockBitsPerSample == 2) { blockBitsPerSample = 12; } else if (blockBitsPerSample == 4) { blockBitsPerSample = 16; } else if (blockBitsPerSample == 5) { blockBitsPerSample = 20; } else if (blockBitsPerSample == 6) { blockBitsPerSample = 24; } else { throw new InvalidDataException(); } uint reserved2 = reader.ReadBit(); if (reserved2 != 0) { throw new InvalidDataException(); } ulong sampleOrFrameNumber = (ulong)reader.ReadUTF8EncodedInteger(); if (blockNumberOfSamples == 6) { blockNumberOfSamples = (int)reader.ReadByte() + 1; } else if (blockNumberOfSamples == 7) { blockNumberOfSamples = (int)reader.ReadUInt16BE() + 1; } if (blockSampleRate == 12) { blockSampleRate = (int)reader.ReadByte(); } else if (blockSampleRate == 13) { blockSampleRate = (int)reader.ReadUInt16BE(); } else if (blockSampleRate == 14) { blockSampleRate = 10 * (int)reader.ReadUInt16BE(); } uint crc8 = reader.ReadByte(); #if CHECKSUMS if (crc8 != Crc8.ComputeHash(reader.Bytes, frameHeaderPosition, reader.BytePosition - frameHeaderPosition - 1)) { throw new InvalidDataException("CRC-8 failed."); } #endif // --- subframes --- while (samplesUsed + blockNumberOfSamples > sampleCount) { sampleCount <<= 1; for (int i = 0; i < numberOfChannels; i++) { Array.Resize(ref samples[i], (int)sampleCount); } } for (int i = 0; i < numberOfChannels; i++) { uint zero = reader.ReadBit(); if (zero != 0) { throw new InvalidDataException(); } uint subframeType = reader.ReadBits(6); int wastedBitsPerSample = (int)reader.ReadUnaryEncodedInteger(); if (wastedBitsPerSample > bitsPerSample) { throw new InvalidDataException(); } int subframeBitsPerSample = bitsPerSample - wastedBitsPerSample; if (channelAssignment == 8 & i == 1) { subframeBitsPerSample++; } else if (channelAssignment == 9 & i == 0) { subframeBitsPerSample++; } else if (channelAssignment == 10 & i == 1) { subframeBitsPerSample++; } if (subframeType == 0) { // --- SUBFRAME_CONSTANT --- int value = FromTwosComplement(reader.ReadBits((int)subframeBitsPerSample), (uint)1 << subframeBitsPerSample) << (int)(32 - subframeBitsPerSample); int numberOfSamples = blockNumberOfSamples * bitsPerSample / subframeBitsPerSample; for (int j = 0; j < numberOfSamples; j++) { samples[i][samplesUsed + j] = value; } } else if (subframeType == 1) { // --- SUBFRAME_VERBATIM --- if (blockSampleRate == sampleRate) { for (int j = 0; j < blockNumberOfSamples; j++) { int value = FromTwosComplement(reader.ReadBits((int)subframeBitsPerSample), (uint)1 << subframeBitsPerSample) << (int)(32 - subframeBitsPerSample); samples[i][samplesUsed + j] = value; } } else { throw new NotSupportedException("Variable sample rates are not supported by this decoder."); } } else if ((subframeType & 0x38) == 8) { // --- SUBFRAME_FIXED --- int predictorOrder = (int)subframeType & 7; if (predictorOrder > 4) { throw new InvalidDataException(); } int[] blockSamples = new int[blockNumberOfSamples]; for (int j = 0; j < predictorOrder; j++) { blockSamples[j] = FromTwosComplement(reader.ReadBits(subframeBitsPerSample), (uint)1 << subframeBitsPerSample); } int[] residuals = ReadResiduals(reader, predictorOrder, blockNumberOfSamples); int mask = (1 << subframeBitsPerSample) - 1; if (predictorOrder == 0) { for (int j = 0; j < blockNumberOfSamples; j++) { blockSamples[j] = residuals[j] & mask; } } else if (predictorOrder == 1) { for (int j = 1; j < blockNumberOfSamples; j++) { int predictor = blockSamples[j - 1]; blockSamples[j] = (residuals[j] + predictor) & mask; } } else if (predictorOrder == 2) { for (int j = 2; j < blockNumberOfSamples; j++) { int predictor = 2 * blockSamples[j - 1] - blockSamples[j - 2]; blockSamples[j] = (residuals[j] + predictor) & mask; } } else if (predictorOrder == 3) { for (int j = 3; j < blockNumberOfSamples; j++) { int predictor = 3 * blockSamples[j - 1] - 3 * blockSamples[j - 2] + blockSamples[j - 3]; blockSamples[j] = (residuals[j] + predictor) & mask; } } else if (predictorOrder == 4) { for (int j = 4; j < blockNumberOfSamples; j++) { int predictor = 4 * blockSamples[j - 1] - 6 * blockSamples[j - 2] + 4 * blockSamples[j - 3] - blockSamples[j - 4]; blockSamples[j] = (residuals[j] + predictor) & mask; } } else { throw new InvalidOperationException(); } if (blockSampleRate == sampleRate) { for (int j = 0; j < blockNumberOfSamples; j++) { samples[i][samplesUsed + j] = blockSamples[j] << (int)(32 - subframeBitsPerSample); } } else { throw new NotSupportedException("Variable sample rates are not supported by this decoder."); } } else if ((subframeType & 0x20) == 0x20) { // --- SUBFRAME_LPC --- int predictorOrder = ((int)subframeType & 0x1F) + 1; int[] blockSamples = new int[blockNumberOfSamples]; for (int j = 0; j < predictorOrder; j++) { blockSamples[j] = FromTwosComplement(reader.ReadBits((int)subframeBitsPerSample), (uint)1 << subframeBitsPerSample); if (blockSamples[j] != 0) { int asdgfefe = blockSamples[j]; } } int coefficientPrecision = (int)reader.ReadBits(4) + 1; if (coefficientPrecision == 16) { throw new InvalidDataException(); } int coefficientRange = 1 << coefficientPrecision; int predictorShift = FromTwosComplement(reader.ReadBits(5), 32); if (predictorShift < 0) { throw new NotSupportedException("A negative predictor shift is not supported by this decoder."); } int[] coefficients = new int[predictorOrder]; for (int j = 0; j < predictorOrder; j++) { coefficients[j] = FromTwosComplement(reader.ReadBits((int)coefficientPrecision), (uint)coefficientRange); } int[] residuals = ReadResiduals(reader, predictorOrder, blockNumberOfSamples); if (subframeBitsPerSample == 8) { if (coefficientPrecision <= 18) { for (int j = predictorOrder; j < blockNumberOfSamples; j++) { int predictor = 0; for (int k = 0; k < predictorOrder; k++) { predictor += coefficients[k] * blockSamples[j - k - 1]; } predictor >>= predictorShift; blockSamples[j] = (int)(sbyte)(predictor + residuals[j]); } } else { for (int j = predictorOrder; j < blockNumberOfSamples; j++) { long predictor = 0; for (int k = 0; k < predictorOrder; k++) { predictor += (long)coefficients[k] * (long)blockSamples[j - k - 1]; } predictor >>= predictorShift; blockSamples[j] = (int)(sbyte)(predictor + residuals[j]); } } } else if (subframeBitsPerSample == 16) { if (coefficientPrecision <= 11) { for (int j = predictorOrder; j < blockNumberOfSamples; j++) { int predictor = 0; for (int k = 0; k < predictorOrder; k++) { predictor += coefficients[k] * blockSamples[j - k - 1]; } predictor >>= predictorShift; blockSamples[j] = (int)(short)(predictor + residuals[j]); } } else { for (int j = predictorOrder; j < blockNumberOfSamples; j++) { long predictor = 0; for (int k = 0; k < predictorOrder; k++) { predictor += (long)coefficients[k] * (long)blockSamples[j - k - 1]; } predictor >>= predictorShift; blockSamples[j] = (int)(short)(predictor + residuals[j]); } } } else { int range = 1 << subframeBitsPerSample; int mask = range - 1; for (int j = predictorOrder; j < blockNumberOfSamples; j++) { long predictor = 0; for (int k = 0; k < predictorOrder; k++) { predictor += (long)coefficients[k] * (long)blockSamples[j - k - 1]; } predictor >>= predictorShift; blockSamples[j] = (int)(predictor + residuals[j]) & mask; if (blockSamples[j] >= range / 2) { blockSamples[j] -= range; } } } if (blockSampleRate == sampleRate) { for (int j = 0; j < blockNumberOfSamples; j++) { samples[i][samplesUsed + j] = blockSamples[j] << (int)(32 - subframeBitsPerSample); } } else { throw new NotSupportedException("Variable sample rates are not supported by this decoder."); } } else { // --- not supported --- throw new InvalidDataException(); } } // --- inter-channel decorreleation --- if (channelAssignment == 8) { // --- left + difference --- for (int i = samplesUsed; i < samplesUsed + blockNumberOfSamples; i++) { samples[1][i] = ((samples[0][i] >> 1) - samples[1][i]) << 1; } } else if (channelAssignment == 9) { // --- difference + right --- for (int i = samplesUsed; i < samplesUsed + blockNumberOfSamples; i++) { samples[0][i] = ((samples[1][i] >> 1) + samples[0][i]) << 1; } } else if (channelAssignment == 10) { // --- average + difference --- int mask = 1 << (31 - blockBitsPerSample); for (int i = samplesUsed; i < samplesUsed + blockNumberOfSamples; i++) { int mid = samples[0][i]; int side = samples[1][i]; samples[0][i] = ((mid | (side & mask)) + side); samples[1][i] = ((mid | (side & mask)) - side); } } samplesUsed += blockNumberOfSamples; // --- padding --- reader.Align(); // --- footer --- uint crc16 = reader.ReadUInt16BE(); #if CHECKSUMS if (crc16 != Crc16.ComputeHash(reader.Bytes, frameHeaderPosition, reader.BytePosition - frameHeaderPosition - 2)) { throw new InvalidDataException("CRC-16 failed."); } #endif } // --- check md5 --- #if CHECKSUMS bool md5Set = false; for (int i = 0; i < md5.Length; i++) { if (md5[i] != 0) { md5Set = true; break; } } if (md5Set) { if (bitsPerSample == 8) { /* For 8 bits per sample */ bytes = new byte[numberOfChannels * samplesUsed]; int pos = 0; for (int i = 0; i < samplesUsed; i++) { for (int j = 0; j < numberOfChannels; j++) { bytes[pos] = (byte)(samples[j][i] >> 24); pos++; } } MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); byte[] check = provider.ComputeHash(bytes); if (check.Length != md5.Length) { throw new InvalidOperationException(); } for (int i = 0; i < check.Length; i++) { if (check[i] != md5[i]) { throw new InvalidDataException("MD5 failed."); } } } else if (bitsPerSample == 16) { /* For 16 bits per sample */ bytes = new byte[2 * numberOfChannels * samplesUsed]; int pos = 0; for (int i = 0; i < samplesUsed; i++) { for (int j = 0; j < numberOfChannels; j++) { bytes[pos + 0] = (byte)(samples[j][i] >> 16); bytes[pos + 1] = (byte)(samples[j][i] >> 24); pos += 2; } } MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); byte[] check = provider.ComputeHash(bytes); if (check.Length != md5.Length) { throw new InvalidOperationException(); } for (int i = 0; i < check.Length; i++) { if (check[i] != md5[i]) { throw new InvalidDataException("MD5 failed."); } } } else if (bitsPerSample == 24) { /* For 24 bits per sample */ bytes = new byte[3 * numberOfChannels * samplesUsed]; int pos = 0; for (int i = 0; i < samplesUsed; i++) { for (int j = 0; j < numberOfChannels; j++) { bytes[pos + 0] = (byte)(samples[j][i] >> 8); bytes[pos + 1] = (byte)(samples[j][i] >> 16); bytes[pos + 2] = (byte)(samples[j][i] >> 24); pos += 3; } } MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); byte[] check = provider.ComputeHash(bytes); if (check.Length != md5.Length) { throw new InvalidOperationException(); } for (int i = 0; i < check.Length; i++) { if (check[i] != md5[i]) { throw new InvalidDataException("MD5 failed."); } } } else { /* Let's just skip the MD5 check in other cases * or feel free to implement the check here. */ } } #endif // --- end of file --- if (sampleCount != samplesUsed) { for (int i = 0; i < numberOfChannels; i++) { Array.Resize(ref samples[i], samplesUsed); } } return samples; } } /// Reads residuals and stores them in the specified array. /// The bit reader. /// The predictor order. /// The block size. /// The signed residuals. private static int[] ReadResiduals(BitReader reader, int predictorOrder, int blockSize) { int[] residuals = new int[blockSize]; uint method = reader.ReadBits(2); if (method == 0 | method == 1) { // --- RESIDUAL_CODING_METHOD_PARTITIONED_RICE / // RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 --- uint partitionOrder = reader.ReadBits(4); int numberOfPartitions = 1 << (int)partitionOrder; int numberOfBits = 4 + (int)method; uint escape = method == 0 ? (uint)15 : (uint)31; int offset = predictorOrder; for (int i = 0; i < numberOfPartitions; i++) { int riceParameter = (int)reader.ReadBits(numberOfBits); int numberOfSamples; if (partitionOrder == 0) { numberOfSamples = blockSize - predictorOrder; } else if (i == 0) { numberOfSamples = (blockSize >> (int)partitionOrder) - predictorOrder; } else { numberOfSamples = blockSize >> (int)partitionOrder; } if (riceParameter == escape) { int bitsPerSample = (int)reader.ReadBits(5); for (int j = 0; j < numberOfSamples; j++) { residuals[offset + j] = (int)reader.ReadBits(bitsPerSample); } offset += numberOfSamples; } else { for (int j = 0; j < numberOfSamples; j++) { int value = reader.ReadRiceEncodedInteger(riceParameter); residuals[offset + j] = value; } offset += numberOfSamples; } } return residuals; } else { // --- not supported --- throw new InvalidDataException(); } } /// Gets a signed integer from an unsigned integer assuming the unsigned integer is stored in two's complement notation. /// The unsigned integer. /// The value range, e.g. 16 for 4 bits, 256 for 8 bits, 65536 for 16 bits, etc. /// The signed integer. private static int FromTwosComplement(uint value, uint range) { if (value < (range >> 1)) { return (int)value; } else { return (int)value - (int)range; } } } }openbve-1.4.0.10/openBVE/Sound.Flac/Plugin.cs000066400000000000000000000046561171674032100204250ustar00rootroot00000000000000using System; using System.IO; using OpenBveApi.Hosts; using OpenBveApi.Sounds; namespace Plugin { public partial class Plugin : SoundInterface { // --- members --- /// The host that loaded the plugin. private HostInterface CurrentHost = null; // --- functions --- /// Called when the plugin is loaded. /// The host that loaded the plugin. public override void Load(HostInterface host) { CurrentHost = host; } /// Checks whether the plugin can load the specified sound. /// The path to the file or folder that contains the sound. /// Whether the plugin can load the specified sound. public override bool CanLoadSound(string path) { if (File.Exists(path)) { using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { using (BinaryReader reader = new BinaryReader(stream)) { if (reader.ReadUInt32() != 0x43614C66) { return false; } } } return true; } return false; } /// Loads the specified sound. /// The path to the file or folder that contains the sound. /// Receives the sound. /// Whether loading the sound was successful. public override bool LoadSound(string path, out Sound sound) { // --- decode file --- int sampleRate; int bitsPerSample; int[][] samples = Flac.Decoder.Decode(path, out sampleRate, out bitsPerSample); // --- format data for API structure --- byte[][] bytes = new byte[samples.Length][]; unchecked { int bytesPerSample = (int)((bitsPerSample + 7) >> 3); for (int i = 0; i < samples.Length; i++) { bytes[i] = new byte[samples[i].Length * bytesPerSample]; if (bitsPerSample <= 8) { for (int j = 0; j < samples[i].Length; j++) { int value = (samples[i][j] >> 24) + 128; bytes[i][j] = (byte)value; } bitsPerSample = 8; } else { for (int j = 0; j < samples[i].Length; j++) { int value = samples[i][j] >> 16; bytes[i][2 * j + 0] = (byte)value; bytes[i][2 * j + 1] = (byte)(value >> 8); } bitsPerSample = 16; } } } sound = new Sound(sampleRate, bitsPerSample, bytes); return true; } } }openbve-1.4.0.10/openBVE/Sound.Flac/Properties/000077500000000000000000000000001171674032100207615ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/Sound.Flac/Properties/AssemblyInfo.cs000066400000000000000000000006541171674032100237100ustar00rootroot00000000000000using System; using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle("FLAC Sound Loader")] [assembly: AssemblyDescription("Supports loading native FLAC files.")] [assembly: AssemblyProduct("openBVE")] [assembly: AssemblyCopyright("(Public Domain) http://trainsimframework.org/")] [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] [assembly: AssemblyVersion("0.0.0.0")]openbve-1.4.0.10/openBVE/Sound.Flac/Sound.Flac.csproj000066400000000000000000000053221171674032100220050ustar00rootroot00000000000000 {081F5739-33DA-421A-B177-7B548D96646F} Debug x86 Library Sound.Flac Sound.Flac v4.0 Properties C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis False False 4 false Client AnyCPU False Auto 4194304 4096 bin\Debug\ True Full False True DEBUG;TRACE bin\Release\ false None True False TRACE {27134980-4415-4375-A564-40A9014DFA5F} OpenBveApi False openbve-1.4.0.10/openBVE/Sound.RiffWave/000077500000000000000000000000001171674032100174715ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/Sound.RiffWave/Plugin.Parser.cs000066400000000000000000000354501171674032100225200ustar00rootroot00000000000000#pragma warning disable 0660, 0661 using System; using System.IO; using OpenBveApi.Sounds; namespace Plugin { public partial class Plugin : SoundInterface { // --- structures and enumerations --- /// Represents the format of wave data. private struct WaveFormat { // members /// The number of samples per second per channel. internal int SampleRate; /// The number of bits per sample. internal int BitsPerSample; /// The number of channels. internal int Channels; // constructors /// Creates a new instance of this structure. /// The number of samples per second per channel. /// The number of bits per sample. /// The number of channels. internal WaveFormat(int sampleRate, int bitsPerSample, int channels) { this.SampleRate = sampleRate; this.BitsPerSample = bitsPerSample; this.Channels = channels; } // operators public static bool operator ==(WaveFormat a, WaveFormat b) { if (a.SampleRate != b.SampleRate) return false; if (a.BitsPerSample != b.BitsPerSample) return false; if (a.Channels != b.Channels) return false; return true; } public static bool operator !=(WaveFormat a, WaveFormat b) { if (a.SampleRate != b.SampleRate) return true; if (a.BitsPerSample != b.BitsPerSample) return true; if (a.Channels != b.Channels) return true; return false; } } /// Represents wave data. private class WaveData { // members /// The format of the wave data. internal WaveFormat Format; /// The wave data in little endian byte order. If the bits per sample are not a multiple of 8, each sample is padded into a multiple-of-8 byte. For bytes per sample higher than 1, the values are stored as signed integers, otherwise as unsigned integers. internal byte[] Bytes; // constructors /// Creates a new instance of this class. /// The format of the wave data. /// The wave data in little endian byte order. If the bits per sample are not a multiple of 8, each sample is padded into a multiple-of-8 byte. For bytes per sample higher than 1, the values are stored as signed integers, otherwise as unsigned integers. internal WaveData(WaveFormat format, byte[] bytes) { this.Format = format; this.Bytes = bytes; } } /// Represents the endianness of an integer. private enum Endianness { /// Represents little endian byte order, i.e. least-significant byte first. Little = 0, /// Represents big endian byte order, i.e. most-significant byte first. Big = 1 } // --- format-specific data --- /// Represents format-specific data. private abstract class FormatData { internal int BlockSize; } /// Represents PCM-specific data. private class PcmData : FormatData { } /// Represents Microsoft-ADPCM-specific data. private class MicrosoftAdPcmData : FormatData { // structures internal struct ChannelData { internal int bPredictor; internal short iDelta; internal short iSamp1; internal short iSamp2; internal int iCoef1; internal int iCoef2; } // members internal int SamplesPerBlock; internal short[][] Coefficients = null; // read-only fields internal static readonly short[] AdaptionTable = new short[] { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 }; } // --- functions --- /// Reads wave data from a WAVE file. /// The file name of the WAVE file. /// The wave data. private static Sound LoadFromFile(string fileName) { byte[] fileBytes = File.ReadAllBytes(fileName); using (MemoryStream stream = new MemoryStream(fileBytes)) { using (BinaryReader reader = new BinaryReader(stream)) { // RIFF/RIFX chunk Endianness endianness; uint headerCkID = reader.ReadUInt32(); if (headerCkID == 0x46464952) { endianness = Endianness.Little; } else if (headerCkID == 0x58464952) { endianness = Endianness.Big; } else { throw new InvalidDataException("Invalid chunk ID"); } uint headerCkSize = ReadUInt32(reader, endianness); uint formType = ReadUInt32(reader, endianness); if (formType != 0x45564157) { throw new InvalidDataException("Unsupported format"); } // data chunks WaveFormat format = new WaveFormat(); FormatData data = null; byte[] dataBytes = null; while (stream.Position + 8 <= stream.Length) { uint ckID = reader.ReadUInt32(); uint ckSize = ReadUInt32(reader, endianness); if (ckID == 0x20746d66) { // "fmt " chunk if (ckSize < 14) { throw new InvalidDataException("Unsupported fmt chunk size"); } ushort wFormatTag = ReadUInt16(reader, endianness); ushort wChannels = ReadUInt16(reader, endianness); uint dwSamplesPerSec = ReadUInt32(reader, endianness); if (dwSamplesPerSec >= 0x80000000) { throw new InvalidDataException("Unsupported dwSamplesPerSec"); } uint dwAvgBytesPerSec = ReadUInt32(reader, endianness); ushort wBlockAlign = ReadUInt16(reader, endianness); if (wFormatTag == 1) { // PCM if (ckSize < 16) { throw new InvalidDataException("Unsupported fmt chunk size"); } ushort wBitsPerSample = ReadUInt16(reader, endianness); stream.Position += ckSize - 16; if (wBitsPerSample < 1) { throw new InvalidDataException("Unsupported wBitsPerSample"); } if (wBlockAlign != ((wBitsPerSample + 7) / 8) * wChannels) { throw new InvalidDataException("Unexpected wBlockAlign"); } format.SampleRate = (int)dwSamplesPerSec; format.BitsPerSample = (int)wBitsPerSample; format.Channels = (int)wChannels; PcmData pcmData = new PcmData(); pcmData.BlockSize = (int)wBlockAlign; data = pcmData; } else if (wFormatTag == 2) { // Microsoft ADPCM if (ckSize < 22) { throw new InvalidDataException("Unsupported fmt chunk size"); } ushort wBitsPerSample = ReadUInt16(reader, endianness); if (wBitsPerSample != 4) { throw new InvalidDataException("Unsupported wBitsPerSample"); } ushort cbSize = ReadUInt16(reader, endianness); MicrosoftAdPcmData adpcmData = new MicrosoftAdPcmData(); adpcmData.SamplesPerBlock = ReadUInt16(reader, endianness); if (adpcmData.SamplesPerBlock == 0 | adpcmData.SamplesPerBlock > 2 * ((int)wBlockAlign - 6)) { throw new InvalidDataException("Unexpected nSamplesPerBlock"); } ushort wNumCoef = ReadUInt16(reader, endianness); if (ckSize < 22 + 4 * wNumCoef) { throw new InvalidDataException("Unsupported fmt chunk size"); } adpcmData.Coefficients = new short[wNumCoef][]; unchecked { for (int i = 0; i < wNumCoef; i++) { adpcmData.Coefficients[i] = new short[] { (short)ReadUInt16(reader, endianness), (short)ReadUInt16(reader, endianness) }; } } stream.Position += ckSize - (22 + 4 * wNumCoef); format.SampleRate = (int)dwSamplesPerSec; format.BitsPerSample = 16; format.Channels = (int)wChannels; adpcmData.BlockSize = wBlockAlign; data = adpcmData; } else { // unsupported format throw new InvalidDataException("Unsupported wFormatTag"); } } else if (ckID == 0x61746164) { // "data" chunk if (ckSize >= 0x80000000) { throw new InvalidDataException("Unsupported data chunk size"); } if (data is PcmData) { // PCM int bytesPerSample = (format.BitsPerSample + 7) / 8; int samples = (int)ckSize / (format.Channels * bytesPerSample); int dataSize = samples * format.Channels * bytesPerSample; dataBytes = reader.ReadBytes(dataSize); stream.Position += ckSize - dataSize; } else if (data is MicrosoftAdPcmData) { // Microsoft ADPCM MicrosoftAdPcmData adpcmData = (MicrosoftAdPcmData)data; int blocks = (int)ckSize / adpcmData.BlockSize; dataBytes = new byte[2 * blocks * format.Channels * adpcmData.SamplesPerBlock]; int position = 0; for (int i = 0; i < blocks; i++) { unchecked { MicrosoftAdPcmData.ChannelData[] channelData = new MicrosoftAdPcmData.ChannelData[format.Channels]; for (int j = 0; j < format.Channels; j++) { channelData[j].bPredictor = (int)reader.ReadByte(); if (channelData[j].bPredictor >= adpcmData.Coefficients.Length) { throw new InvalidDataException("Invalid bPredictor"); } else { channelData[j].iCoef1 = (int)adpcmData.Coefficients[channelData[j].bPredictor][0]; channelData[j].iCoef2 = (int)adpcmData.Coefficients[channelData[j].bPredictor][1]; } } for (int j = 0; j < format.Channels; j++) { channelData[j].iDelta = (short)ReadUInt16(reader, endianness); } for (int j = 0; j < format.Channels; j++) { channelData[j].iSamp1 = (short)ReadUInt16(reader, endianness); } for (int j = 0; j < format.Channels; j++) { channelData[j].iSamp2 = (short)ReadUInt16(reader, endianness); } for (int j = 0; j < format.Channels; j++) { dataBytes[position] = (byte)(ushort)channelData[j].iSamp2; dataBytes[position + 1] = (byte)((ushort)channelData[j].iSamp2 >> 8); position += 2; } for (int j = 0; j < format.Channels; j++) { dataBytes[position] = (byte)(ushort)channelData[j].iSamp1; dataBytes[position + 1] = (byte)((ushort)channelData[j].iSamp1 >> 8); position += 2; } uint nibbleByte = 0; bool nibbleFirst = true; for (int j = 0; j < adpcmData.SamplesPerBlock - 2; j++) { for (int k = 0; k < format.Channels; k++) { int lPredSample = (int)channelData[k].iSamp1 * channelData[k].iCoef1 + (int)channelData[k].iSamp2 * channelData[k].iCoef2 >> 8; int iErrorDeltaUnsigned; if (nibbleFirst) { nibbleByte = (uint)reader.ReadByte(); iErrorDeltaUnsigned = (int)(nibbleByte >> 4); nibbleFirst = false; } else { iErrorDeltaUnsigned = (int)(nibbleByte & 15); nibbleFirst = true; } int iErrorDeltaSigned = iErrorDeltaUnsigned >= 8 ? iErrorDeltaUnsigned - 16 : iErrorDeltaUnsigned; int lNewSampInt = lPredSample + (int)channelData[k].iDelta * iErrorDeltaSigned; short lNewSamp = lNewSampInt <= -32768 ? (short)-32768 : lNewSampInt >= 32767 ? (short)32767 : (short)lNewSampInt; channelData[k].iDelta = (short)( (int)channelData[k].iDelta * (int)MicrosoftAdPcmData.AdaptionTable[iErrorDeltaUnsigned] >> 8 ); if (channelData[k].iDelta < 16) { channelData[k].iDelta = 16; } channelData[k].iSamp2 = channelData[k].iSamp1; channelData[k].iSamp1 = lNewSamp; dataBytes[position] = (byte)(ushort)lNewSamp; dataBytes[position + 1] = (byte)((ushort)lNewSamp >> 8); position += 2; } } } stream.Position += adpcmData.BlockSize - (format.Channels * (adpcmData.SamplesPerBlock - 2) + 1 >> 1) - 7 * format.Channels; } stream.Position += (int)ckSize - blocks * adpcmData.BlockSize; } else { // invalid throw new InvalidDataException("No fmt chunk before the data chunk"); } } else { // unsupported chunk stream.Position += (long)ckSize; } // pad byte if ((ckSize & 1) == 1) { stream.Position++; } } // finalize if (dataBytes == null) { throw new InvalidDataException("No data chunk before the end of the file"); } else if (format.Channels == 1) { return new Sound(format.SampleRate, ((format.BitsPerSample + 7) >> 3) << 3, new byte[][] { dataBytes }); } else { byte[][] bytes = new byte[format.Channels][]; for (int i = 0; i < format.Channels; i++) { bytes[i] = new byte[dataBytes.Length / format.Channels]; } int bytesPerSample = (format.BitsPerSample + 7) >> 3; int samples = dataBytes.Length / (format.Channels * bytesPerSample); int pos1 = 0; int pos2 = 0; for (int i = 0; i < samples; i++) { for (int j = 0; j < format.Channels; j++) { for (int k = 0; k < bytesPerSample; k++) { bytes[j][pos1 + k] = dataBytes[pos2 + k]; } pos2 += bytesPerSample; } pos1 += bytesPerSample; } return new Sound(format.SampleRate, ((format.BitsPerSample + 7) >> 3) << 3, bytes); } } } } /// Reads a System.UInt32 from a binary reader with the specified endianness. /// The binary reader. /// The endianness. /// The System.UInt32 read from the reader. private static uint ReadUInt32(BinaryReader reader, Endianness endianness) { uint value = reader.ReadUInt32(); if (endianness == Endianness.Big) { unchecked { return (value << 24) | (value & ((uint)0xFF00 << 8)) | ((value & (uint)0xFF0000) >> 8) | (value >> 24); } } else { return value; } } /// Reads a System.UInt16 from a binary reader with the specified endianness. /// The binary reader. /// The endianness. /// The System.UInt16 read from the reader. private static ushort ReadUInt16(BinaryReader reader, Endianness endianness) { ushort value = reader.ReadUInt16(); if (endianness == Endianness.Big) { unchecked { return (ushort)(((uint)value << 8) | ((uint)value >> 8)); } } else { return value; } } } }openbve-1.4.0.10/openBVE/Sound.RiffWave/Plugin.cs000066400000000000000000000036301171674032100212600ustar00rootroot00000000000000using System; using System.IO; using OpenBveApi.Hosts; using OpenBveApi.Sounds; namespace Plugin { public partial class Plugin : SoundInterface { // --- members --- /// The host that loaded the plugin. private HostInterface CurrentHost = null; // --- functions --- /// Called when the plugin is loaded. /// The host that loaded the plugin. public override void Load(HostInterface host) { CurrentHost = host; } /// Checks whether the plugin can load the specified sound. /// The path to the file or folder that contains the sound. /// Whether the plugin can load the specified sound. public override bool CanLoadSound(string path) { if (File.Exists(path)) { using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { using (BinaryReader reader = new BinaryReader(stream)) { Endianness endianness; uint headerCkID = reader.ReadUInt32(); if (headerCkID == 0x46464952) { endianness = Endianness.Little; } else if (headerCkID == 0x58464952) { endianness = Endianness.Big; } else { return false; } uint headerCkSize = ReadUInt32(reader, endianness); uint formType = ReadUInt32(reader, endianness); if (formType != 0x45564157) { return false; } } } return true; } return false; } /// Loads the specified sound. /// The path to the file or folder that contains the sound. /// Receives the sound. /// Whether loading the sound was successful. public override bool LoadSound(string path, out Sound sound) { sound = LoadFromFile(path); return true; } } }openbve-1.4.0.10/openBVE/Sound.RiffWave/Properties/000077500000000000000000000000001171674032100216255ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/Sound.RiffWave/Properties/AssemblyInfo.cs000066400000000000000000000007421171674032100245520ustar00rootroot00000000000000using System; using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle("RIFF/WAV Sound Loader")] [assembly: AssemblyDescription("Supports loading WAV PCM or Microsoft ADPCM sounds embedded in a RIFF/RIFX container.")] [assembly: AssemblyProduct("openBVE")] [assembly: AssemblyCopyright("(Public Domain) http://trainsimframework.org/")] [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] [assembly: AssemblyVersion("0.0.0.0")]openbve-1.4.0.10/openBVE/Sound.RiffWave/Sound.RiffWave.csproj000066400000000000000000000052471171674032100235230ustar00rootroot00000000000000 {67418D38-1E2E-4944-A1B0-09E00FC2D055} Debug x86 Library Plugin Sound.RiffWave v4.0 Properties C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis False False 4 false Client AnyCPU False Auto 4194304 4096 bin\Debug\ True Full False True DEBUG;TRACE bin\Release\ false None True False TRACE Plugin.cs {27134980-4415-4375-A564-40A9014DFA5F} OpenBveApi False openbve-1.4.0.10/openBVE/Texture.Ace/000077500000000000000000000000001171674032100170205ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/Texture.Ace/Plugin.Parser.cs000066400000000000000000000357041171674032100220510ustar00rootroot00000000000000using System; using System.IO; using System.IO.Compression; using OpenBveApi.Colors; using OpenBveApi.Textures; namespace Plugin { public partial class Plugin : TextureInterface { // --- get colors --- /// Gets a color from the specified integer. /// The color comprised of 5 red bits in the most significant bits, 6 green bits, and 5 blue bits in the least significant bits. /// private static Color32 GetColor(ushort color) { return new Color32( (byte)((color >> 11) << 3), (byte)(((color >> 5) & 0x3F) << 2), (byte)((color & 0x1F) << 3), 255 ); } /// Gets the color that is half-way between the two specified colors. /// The first color. /// The second color. /// The mixed color. private static Color32 GetInterpolatedColor11(Color32 a, Color32 b) { return new Color32( (byte)((uint)a.R + (uint)b.R >> 1), (byte)((uint)a.G + (uint)b.G >> 1), (byte)((uint)a.B + (uint)b.B >> 1), (byte)((uint)a.A + (uint)b.A >> 1) ); } /// Gets the color that is one third the way between the two specified colors. /// The first color. /// The second color. /// The mixed color. private static Color32 GetInterpolatedColor12(Color32 a, Color32 b) { return new Color32( (byte)(((uint)a.R + 2 * (uint)b.R) / 3), (byte)(((uint)a.G + 2 * (uint)b.G) / 3), (byte)(((uint)a.B + 2 * (uint)b.B) / 3), (byte)(((uint)a.A + 2 * (uint)b.A) / 3) ); } /// Gets the color that is two thirds the way between the two specified colors. /// The first color. /// The second color. /// The mixed color. private static Color32 GetInterpolatedColor21(Color32 a, Color32 b) { return new Color32( (byte)((2 * (uint)a.R + (uint)b.R) / 3), (byte)((2 * (uint)a.G + (uint)b.G) / 3), (byte)((2 * (uint)a.B + (uint)b.B) / 3), (byte)((2 * (uint)a.A + (uint)b.A) / 3) ); } // --- can load file --- /// Checks whether the specified file can be loaded as an ACE texture. /// The path to the file. /// Whether the file can be load as an ACE texture. private static bool CanLoadFile(string file) { ulong identifier; using (FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read)) { using (BinaryReader reader = new BinaryReader(stream)) { identifier = reader.ReadUInt64(); } } if (identifier == 0x40404153494D4953) { byte[] bytes = File.ReadAllBytes(file); return CanLoadUncompressedData(bytes); } else if (identifier == 0x46404153494D4953) { byte[] bytes = File.ReadAllBytes(file); return CanLoadUncompressedData(DecompressAce(bytes)); } else { return false; } } /// Checks whether the specified uncompressed data can be loaded as an ACE texture. /// The uncompressed data. /// Whether the uncompressed data can be load as an ACE texture. private static bool CanLoadUncompressedData(byte[] data) { using (MemoryStream stream = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(stream)) { ulong identifier; identifier = reader.ReadUInt64(); if (identifier != 0x40404153494D4953) { return false; } identifier = reader.ReadUInt64(); if (identifier != 0x4040404040404040) { return false; } int unknown1 = reader.ReadInt32(); if (unknown1 != 1) { return false; } return true; } } } // --- query dimensions --- /// Queries the texture dimensions of the specified file. /// The path to the file. /// Receives the width. /// Receives the height. private static void QueryDimensionsFromFile(string file, out int width, out int height) { byte[] bytes = File.ReadAllBytes(file); ulong identifier; using (MemoryStream stream = new MemoryStream(bytes)) { using (BinaryReader reader = new BinaryReader(stream)) { identifier = reader.ReadUInt64(); } } if (identifier == 0x40404153494D4953) { QueryDimensionsFromUncompressedData(bytes, out width, out height); } else if (identifier == 0x46404153494D4953) { QueryDimensionsFromUncompressedData(DecompressAce(bytes), out width, out height); } else { throw new InvalidDataException(); } } /// Queries the texture dimensions of the specified uncompressed data. /// The byte data. /// Receives the width. /// Receives the height. private static void QueryDimensionsFromUncompressedData(byte[] data, out int width, out int height) { using (MemoryStream stream = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(stream)) { ulong identifier; identifier = reader.ReadUInt64(); if (identifier != 0x40404153494D4953) { throw new InvalidDataException(); } identifier = reader.ReadUInt64(); if (identifier != 0x4040404040404040) { throw new InvalidDataException(); } int unknown1 = reader.ReadInt32(); if (unknown1 != 1) { throw new InvalidDataException(); } int unknown2 = reader.ReadInt32(); width = reader.ReadInt32(); height = reader.ReadInt32(); } } } // --- load from file --- /// Loads an ACE texture from the specified file. /// The path to the file. /// The texture. private static Texture LoadFromFile(string file) { byte[] bytes = File.ReadAllBytes(file); ulong identifier; using (MemoryStream stream = new MemoryStream(bytes)) { using (BinaryReader reader = new BinaryReader(stream)) { identifier = reader.ReadUInt64(); } } if (identifier == 0x40404153494D4953) { return LoadFromUncompressedData(bytes); } else if (identifier == 0x46404153494D4953) { return LoadFromUncompressedData(DecompressAce(bytes)); } else { throw new InvalidDataException(); } } /// Loads an ACE texture from uncompressed data. /// The uncompressed data. /// The texture. private static Texture LoadFromUncompressedData(byte[] data) { using (MemoryStream stream = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(stream)) { // --- header --- ulong identifier; identifier = reader.ReadUInt64(); if (identifier != 0x40404153494D4953) { throw new InvalidDataException(); } identifier = reader.ReadUInt64(); if (identifier != 0x4040404040404040) { throw new InvalidDataException(); } int unknown1 = reader.ReadInt32(); if (unknown1 != 1) { throw new InvalidDataException(); } int unknown2 = reader.ReadInt32(); int width = reader.ReadInt32(); int height = reader.ReadInt32(); int type = reader.ReadInt32(); /* 14 = 24 bits * 16 = 24 bit 1 bit alpha * 17 = 24 bit 8 bit alpha * 18 = dtx1 */ if (type != 14 & type != 16 & type != 17 & type != 18) { throw new InvalidDataException(); } int channels = reader.ReadInt32(); /* 3 = 24 bit * 4 = 24 bits 1 bit alpha * 5 = 24 bit 8 bit alpha */ if (channels != 3 & channels != 4 & channels != 5) { throw new InvalidDataException(); } int unknown3 = reader.ReadInt32(); byte[] name = reader.ReadBytes(16); byte[] copyright = reader.ReadBytes(72); int unknown4 = reader.ReadInt32(); if (channels == 3) { byte[] unknown = reader.ReadBytes(80); } else if (channels == 4) { byte[] unknown = reader.ReadBytes(96); } else if (channels == 5) { byte[] unknown = reader.ReadBytes(112); } // --- actual pixel data --- byte[] bytes = new byte[4 * width * height]; if (type == 14 & channels == 3) { // --- rgb --- int[] streamOffsets = new int[height]; for (int y = 0; y < height; y++) { streamOffsets[y] = 16 + reader.ReadInt32(); } int offset = 0; int offsetIncrement = -4 * width + 1; for (int y = 0; y < height; y++) { stream.Position = streamOffsets[y]; for (int x = 0; x < width; x++) { bytes[offset] = reader.ReadByte(); offset += 4; } offset += offsetIncrement; for (int x = 0; x < width; x++) { bytes[offset] = reader.ReadByte(); offset += 4; } offset += offsetIncrement; for (int x = 0; x < width; x++) { bytes[offset] = reader.ReadByte(); offset += 4; } offset += offsetIncrement; for (int x = 0; x < width; x++) { bytes[offset] = 255; offset += 4; } offset -= 3; } } else if (type == 16 & channels == 4) { // --- rgb (1-bit transparency) --- int[] streamOffsets = new int[height]; for (int y = 0; y < height; y++) { streamOffsets[y] = 16 + reader.ReadInt32(); } int offset = 0; int offsetIncrement = -4 * width + 1; for (int y = 0; y < height; y++) { stream.Position = streamOffsets[y]; for (int x = 0; x < width; x++) { bytes[offset] = reader.ReadByte(); offset += 4; } offset += offsetIncrement; for (int x = 0; x < width; x++) { bytes[offset] = reader.ReadByte(); offset += 4; } offset += offsetIncrement; for (int x = 0; x < width; x++) { bytes[offset] = reader.ReadByte(); offset += 4; } offset += offsetIncrement; int value = 0; int counter = 0; for (int x = 0; x < width; x++) { if (counter == 0) { value = reader.ReadByte(); counter = 7; } else { counter--; } bytes[offset] = (value >> 7) == 0 ? (byte)0 : (byte)255; value <<= 1; offset += 4; } offset -= 3; } } else if (type == 17 & channels == 5) { // --- rgb (8-bit alpha) --- int[] streamOffsets = new int[height]; for (int y = 0; y < height; y++) { streamOffsets[y] = 16 + reader.ReadInt32(); } int offset = 0; int offsetIncrement = -4 * width + 1; for (int y = 0; y < height; y++) { stream.Position = streamOffsets[y]; for (int x = 0; x < width; x++) { bytes[offset] = reader.ReadByte(); offset += 4; } offset += offsetIncrement; for (int x = 0; x < width; x++) { bytes[offset] = reader.ReadByte(); offset += 4; } offset += offsetIncrement; for (int x = 0; x < width; x++) { bytes[offset] = reader.ReadByte(); offset += 4; } offset += offsetIncrement; stream.Position += (width + 7) / 8; for (int x = 0; x < width; x++) { bytes[offset] = reader.ReadByte(); offset += 4; } offset -= 3; } } else if (type == 18 & (channels == 3 | channels == 4)) { // --- dxt1 --- int mipmapOffset0 = reader.ReadInt32() + 20; stream.Position = mipmapOffset0; int offset = 0; int offsetIncrementY = 12 * width; int offsetIncrementX = -16 * width + 16; int offsetIncrementDy = 4 * width - 16; Color32[] colors = new Color32[4]; Color32 black = channels == 4 ? Color32.Transparent : Color32.Black; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { ushort entry0 = reader.ReadUInt16(); ushort entry1 = reader.ReadUInt16(); colors[0] = GetColor(entry0); colors[1] = GetColor(entry1); if (entry0 > entry1) { colors[2] = GetInterpolatedColor21(colors[0], colors[1]); colors[3] = GetInterpolatedColor12(colors[0], colors[1]); } else { colors[2] = GetInterpolatedColor11(colors[0], colors[1]); colors[3] = black; } uint lookup = reader.ReadUInt32(); for (int dy = 0; dy < 4; dy++) { for (int dx = 0; dx < 4; dx++) { uint index = lookup & 3; lookup >>= 2; bytes[offset + 0] = colors[index].R; bytes[offset + 1] = colors[index].G; bytes[offset + 2] = colors[index].B; bytes[offset + 3] = colors[index].A; offset += 4; } offset += offsetIncrementDy; } offset += offsetIncrementX; } offset += offsetIncrementY; } } else { // --- not supported --- throw new NotSupportedException(); } // --- return texture --- return new Texture(width, height, 32, bytes); } } } // --- decompress ace --- /// Decompresses the specified zlib-compressed data. /// The compressed data including the ACE header. /// The uncompressed data including the ACE header. private static byte[] DecompressAce(byte[] data) { // --- decompress data --- byte[] result; using (MemoryStream stream = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(stream)) { // --- ACE header --- ulong identifier; identifier = reader.ReadUInt64(); if (identifier != 0x46404153494D4953) { throw new InvalidDataException(); } int uncompressedLength = reader.ReadInt32(); identifier = reader.ReadUInt32(); if (identifier != 0x40404040) { throw new InvalidDataException(); } // --- zlib header --- byte cmf = reader.ReadByte(); int cm = cmf & 15; if (cm != 8) { throw new InvalidDataException(); } byte flg = reader.ReadByte(); int fcheck = flg & 31; if ((256 * cmf + flg) % 31 != 0) { throw new InvalidDataException(); } // --- deflate data --- result = new byte[uncompressedLength + 16]; Array.Copy(new byte[] { 0x53, 0x49, 0x4D, 0x49, 0x53, 0x41, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 }, result, 16); using (DeflateStream deflate = new DeflateStream(stream, CompressionMode.Decompress, true)) { int length = deflate.Read(result, 16, uncompressedLength); if (length != uncompressedLength) { throw new InvalidDataException(); } } } } return result; } } }openbve-1.4.0.10/openBVE/Texture.Ace/Plugin.cs000066400000000000000000000036711171674032100206140ustar00rootroot00000000000000using System; using System.IO; using OpenBveApi.Hosts; using OpenBveApi.Textures; namespace Plugin { /// Implements the texture interface. public partial class Plugin : TextureInterface { // --- members --- /// The host that loaded the plugin. private HostInterface CurrentHost = null; // --- functions --- /// Called when the plugin is loaded. /// The host that loaded the plugin. public override void Load(HostInterface host) { CurrentHost = host; } /// Queries the dimensions of a texture. /// The path to the file or folder that contains the texture. /// Receives the width of the texture. /// Receives the height of the texture. /// Whether querying the dimensions was successful. public override bool QueryTextureDimensions(string path, out int width, out int height) { QueryDimensionsFromFile(path, out width, out height); return true; } /// Checks whether the plugin can load the specified texture. /// The path to the file or folder that contains the texture. /// Whether the plugin can load the specified texture. public override bool CanLoadTexture(string path) { if (File.Exists(path)) { return CanLoadFile(path); } else { return false; } } /// Loads the specified texture. /// The path to the file or folder that contains the texture. /// Receives the texture. /// Whether loading the texture was successful. public override bool LoadTexture(string path, out Texture texture) { texture = LoadFromFile(path); return true; } } }openbve-1.4.0.10/openBVE/Texture.Ace/Properties/000077500000000000000000000000001171674032100211545ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/Texture.Ace/Properties/AssemblyInfo.cs000066400000000000000000000007011171674032100240740ustar00rootroot00000000000000using System; using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle("MSTS ACE Texture Loader")] [assembly: AssemblyDescription("Supports loading the MSTS ACE texture file format.")] [assembly: AssemblyProduct("openBVE")] [assembly: AssemblyCopyright("(Public Domain) http://trainsimframework.org/")] [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] [assembly: AssemblyVersion("0.0.0.0")]openbve-1.4.0.10/openBVE/Texture.Ace/Texture.Ace.csproj000066400000000000000000000052511171674032100223740ustar00rootroot00000000000000 {06D89847-9C7A-47D5-8C7A-95AEBFFF5F1E} Debug x86 Library Texture.Ace Texture.Ace v4.0 Properties C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis False False 4 false Client AnyCPU False Auto 4194304 4096 bin\Debug\ true Full False True DEBUG;TRACE bin\Release\ False None True False TRACE Plugin.cs {27134980-4415-4375-A564-40A9014DFA5F} OpenBveApi False openbve-1.4.0.10/openBVE/Texture.BmpGifJpegPngTiff/000077500000000000000000000000001171674032100215605ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/Texture.BmpGifJpegPngTiff/Plugin.Parser.cs000066400000000000000000000050561171674032100246060ustar00rootroot00000000000000using System; using System.Drawing; using System.Drawing.Imaging; using OpenBveApi.Textures; using OpenBveApi.Hosts; namespace Plugin { public partial class Plugin : TextureInterface { /// Loads a texture from the specified file. /// The file that holds the texture. /// Receives the texture. /// Whether loading the texture was successful. internal bool Parse(string file, out Texture texture) { /* * Read the bitmap. This will be a bitmap of just * any format, not necessarily the one that allows * us to extract the bitmap data easily. * */ System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(file); Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); /* * If the bitmap format is not already 32-bit BGRA, * then convert it to 32-bit BGRA. * */ if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) { Bitmap compatibleBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb); Graphics graphics = Graphics.FromImage(compatibleBitmap); graphics.DrawImage(bitmap, rect, rect, GraphicsUnit.Pixel); graphics.Dispose(); bitmap.Dispose(); bitmap = compatibleBitmap; } /* * Extract the raw bitmap data. * */ BitmapData data = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat); if (data.Stride == 4 * data.Width) { /* * Copy the data from the bitmap * to the array in BGRA format. * */ byte[] raw = new byte[data.Stride * data.Height]; System.Runtime.InteropServices.Marshal.Copy(data.Scan0, raw, 0, data.Stride * data.Height); bitmap.UnlockBits(data); int width = bitmap.Width; int height = bitmap.Height; bitmap.Dispose(); /* * Change the byte order from BGRA to RGBA. * */ for (int i = 0; i < raw.Length; i += 4) { byte temp = raw[i]; raw[i] = raw[i + 2]; raw[i + 2] = temp; } texture = new Texture(width, height, 32, raw); return true; } else { /* * The stride is invalid. This indicates that the * CLI either does not implement the conversion to * 32-bit BGRA correctly, or that the CLI has * applied additional padding that we do not * support. * */ bitmap.UnlockBits(data); bitmap.Dispose(); CurrentHost.ReportProblem(ProblemType.InvalidOperation, "Invalid stride encountered."); texture = null; return false; } } } }openbve-1.4.0.10/openBVE/Texture.BmpGifJpegPngTiff/Plugin.cs000066400000000000000000000100361171674032100233450ustar00rootroot00000000000000using System; using System.Drawing; using System.IO; using OpenBveApi.Hosts; using OpenBveApi.Textures; namespace Plugin { /// Implements the texture interface. public partial class Plugin : TextureInterface { // --- members --- /// The host that loaded the plugin. private HostInterface CurrentHost = null; // --- functions --- /// Called when the plugin is loaded. /// The host that loaded the plugin. public override void Load(HostInterface host) { CurrentHost = host; } /// Queries the dimensions of a texture. /// The path to the file or folder that contains the texture. /// Receives the width of the texture. /// Receives the height of the texture. /// Whether querying the dimensions was successful. public override bool QueryTextureDimensions(string path, out int width, out int height) { using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { using (BinaryReader reader = new BinaryReader(stream)) { uint identifier1 = reader.ReadUInt32(); uint identifier2 = reader.ReadUInt32(); if ((identifier1 & 0xFFFF) == 0x4D42) { /* BMP */ stream.Position = 18; width = reader.ReadInt32(); height = reader.ReadInt32(); return true; } else if (identifier1 == 0x38464947 & ((identifier2 & 0xFFFF) == 0x6137 | (identifier2 & 0xFFFF) == 0x6139)) { /* GIF */ stream.Position = 6; width = (int)reader.ReadUInt16(); height = (int)reader.ReadUInt16(); return true; } else if (identifier1 == 0x474E5089 & identifier2 == 0x0A1A0A0D) { /* PNG */ if (reader.ReadUInt32() == 0x0D000000) { if (reader.ReadUInt32() == 0x52444849) { uint bigWidth = reader.ReadUInt32(); uint bigHeight = reader.ReadUInt32(); width = (int)((bigWidth >> 24) | ((bigWidth >> 8) & 0xFF00) | ((bigWidth & 0xFF00) << 8) | (bigWidth << 24)); height = (int)((bigHeight >> 24) | ((bigHeight >> 8) & 0xFF00) | ((bigHeight & 0xFF00) << 8) | (bigHeight << 24)); return true; } } } } } using (Image image = Image.FromFile(path)) { width = image.Width; height = image.Height; return true; } } /// Checks whether the plugin can load the specified texture. /// The path to the file or folder that contains the texture. /// Whether the plugin can load the specified texture. public override bool CanLoadTexture(string path) { if (File.Exists(path)) { using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { using (BinaryReader reader = new BinaryReader(stream)) { uint identifier1 = reader.ReadUInt32(); uint identifier2 = reader.ReadUInt32(); if ((identifier1 & 0xFFFF) == 0x4D42) { /* BMP */ return true; } else if (identifier1 == 0x38464947 & ((identifier2 & 0xFFFF) == 0x6137 | (identifier2 & 0xFFFF) == 0x6139)) { /* GIF */ return true; } else if (identifier1 == 0xE0FFD8FF | identifier1 == 0xE1FFD8FF) { /* JPEG */ return true; } else if (identifier1 == 0x474E5089 & identifier2 == 0x0A1A0A0D) { /* PNG */ return true; } else if (identifier1 == 0x002A4949 | identifier1 == 0x2A004D4D) { /* TIFF */ return true; } } } } return false; } /// Loads the specified texture. /// The path to the file or folder that contains the texture. /// Receives the texture. /// Whether loading the texture was successful. public override bool LoadTexture(string path, out Texture texture) { return Parse(path, out texture); } } }openbve-1.4.0.10/openBVE/Texture.BmpGifJpegPngTiff/Properties/000077500000000000000000000000001171674032100237145ustar00rootroot00000000000000openbve-1.4.0.10/openBVE/Texture.BmpGifJpegPngTiff/Properties/AssemblyInfo.cs000066400000000000000000000010131171674032100266310ustar00rootroot00000000000000using System; using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle("BMP/GIF/JPEG/PNG/TIFF Texture Loader")] [assembly: AssemblyDescription("Supports loading all bitmap formats that are exposed via the CLI, which should be BMP, GIF, JPEG, PNG and TIFF.")] [assembly: AssemblyProduct("openBVE")] [assembly: AssemblyCopyright("(Public Domain) http://trainsimframework.org/")] [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] [assembly: AssemblyVersion("0.0.0.0")]openbve-1.4.0.10/openBVE/Texture.BmpGifJpegPngTiff/Texture.BmpGifJpegPngTiff.csproj000066400000000000000000000053361171674032100277000ustar00rootroot00000000000000 {4B775819-3574-443E-95AD-B40BC6EA6469} Debug x86 Library Plugin Texture.BmpGifJpegPngTiff v4.0 Properties C:\Documents and Settings\Administrator\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis False False 4 false Client AnyCPU False Auto 4194304 4096 bin\Debug\ True Full False True DEBUG;TRACE bin\Release\ false None True False TRACE Plugin.cs {27134980-4415-4375-A564-40A9014DFA5F} OpenBveApi False