pax_global_header 0000666 0000000 0000000 00000000064 11714575155 0014525 g ustar 00root root 0000000 0000000 52 comment=61176347a8aca693be17c0e71d53cfa6fcb75800
openbve-1.4.0.9/ 0000775 0000000 0000000 00000000000 11714575155 0013334 5 ustar 00root root 0000000 0000000 openbve-1.4.0.9/AtsPluginProxy/ 0000775 0000000 0000000 00000000000 11714575155 0016304 5 ustar 00root root 0000000 0000000 openbve-1.4.0.9/AtsPluginProxy/AtsPluginProxy.cpp 0000664 0000000 0000000 00000017241 11714575155 0021765 0 ustar 00root root 0000000 0000000 #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.9/AtsPluginProxy/AtsPluginProxy.def 0000664 0000000 0000000 00000000334 11714575155 0021734 0 ustar 00root root 0000000 0000000 EXPORTS
LoadDLL
UnloadDLL
Load
Dispose
GetPluginVersion
SetVehicleSpec
Initialize
Elapse
SetPower
SetBrake
SetReverser
KeyDown
KeyUp
HornBlow
DoorOpen
DoorClose
SetSignal
SetBeaconData openbve-1.4.0.9/AtsPluginProxy/Readme.txt 0000664 0000000 0000000 00000000503 11714575155 0020240 0 ustar 00root root 0000000 0000000 This 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.9/Readme.txt 0000664 0000000 0000000 00000000647 11714575155 0015301 0 ustar 00root root 0000000 0000000 ============================
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.9/openBVE/ 0000775 0000000 0000000 00000000000 11714575155 0014632 5 ustar 00root root 0000000 0000000 openbve-1.4.0.9/openBVE/OpenBve.sln 0000664 0000000 0000000 00000014752 11714575155 0016717 0 ustar 00root root 0000000 0000000
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.9/openBVE/OpenBve/ 0000775 0000000 0000000 00000000000 11714575155 0016170 5 ustar 00root root 0000000 0000000 openbve-1.4.0.9/openBVE/OpenBve/Audio/ 0000775 0000000 0000000 00000000000 11714575155 0017231 5 ustar 00root root 0000000 0000000 openbve-1.4.0.9/openBVE/OpenBve/Audio/Sounds.Convert.cs 0000664 0000000 0000000 00000015052 11714575155 0022455 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Audio/Sounds.SoundBuffer.cs 0000664 0000000 0000000 00000002701 11714575155 0023254 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Audio/Sounds.SoundOrigin.cs 0000664 0000000 0000000 00000012351 11714575155 0023274 0 ustar 00root root 0000000 0000000 #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.9/openBVE/OpenBve/Audio/Sounds.SoundSource.cs 0000664 0000000 0000000 00000006562 11714575155 0023314 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Audio/Sounds.Update.cs 0000664 0000000 0000000 00000031403 11714575155 0022255 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Audio/Sounds.cs 0000664 0000000 0000000 00000032501 11714575155 0021034 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Graphics/ 0000775 0000000 0000000 00000000000 11714575155 0017730 5 ustar 00root root 0000000 0000000 openbve-1.4.0.9/openBVE/OpenBve/Graphics/Camera.cs 0000664 0000000 0000000 00000004444 11714575155 0021455 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Graphics/Fonts.cs 0000664 0000000 0000000 00000020062 11714575155 0021350 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Graphics/Renderer.Loading.cs 0000664 0000000 0000000 00000003570 11714575155 0023406 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Graphics/Renderer.Primitives.cs 0000664 0000000 0000000 00000003540 11714575155 0024161 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Graphics/Renderer.Strings.cs 0000664 0000000 0000000 00000020147 11714575155 0023461 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Graphics/Renderer.cs 0000664 0000000 0000000 00000000431 11714575155 0022023 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Graphics/Screen.cs 0000664 0000000 0000000 00000013162 11714575155 0021501 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/Graphics/Textures.Texture.cs 0000664 0000000 0000000 00000011605 11714575155 0023544 0 ustar 00root root 0000000 0000000 #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.9/openBVE/OpenBve/Graphics/Textures.TextureOrigin.cs 0000664 0000000 0000000 00000020266 11714575155 0024717 0 ustar 00root root 0000000 0000000 #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.9/openBVE/OpenBve/Graphics/Textures.cs 0000664 0000000 0000000 00000040145 11714575155 0022106 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/OldCode/ 0000775 0000000 0000000 00000000000 11714575155 0017501 5 ustar 00root root 0000000 0000000 openbve-1.4.0.9/openBVE/OpenBve/OldCode/FunctionScripts.cs 0000664 0000000 0000000 00000306763 11714575155 0023204 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/OldCode/Game.cs 0000664 0000000 0000000 00000236177 11714575155 0020721 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/OldCode/Illustrations.cs 0000664 0000000 0000000 00000033666 11714575155 0022722 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/OldCode/Interface.cs 0000664 0000000 0000000 00000351125 11714575155 0021737 0 ustar 00root root 0000000 0000000 using System;
using System.Globalization;
using OpenBveApi.Colors;
using Tao.Sdl;
namespace OpenBve {
internal static class Interface {
// messages
internal enum MessageType {
Information = 1,
Warning = 2,
Error = 3,
Critical = 4
}
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.9/openBVE/OpenBve/OldCode/LegacyPlugin.cs 0000664 0000000 0000000 00000034421 11714575155 0022417 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/OldCode/Loading.cs 0000664 0000000 0000000 00000041171 11714575155 0021411 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/OldCode/MainLoop.cs 0000664 0000000 0000000 00000271330 11714575155 0021554 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/OldCode/NetPlugin.cs 0000664 0000000 0000000 00000017155 11714575155 0021746 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/OldCode/ObjectManager.cs 0000664 0000000 0000000 00000243710 11714575155 0022540 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/OldCode/PluginManager.cs 0000664 0000000 0000000 00000066074 11714575155 0022576 0 ustar 00root root 0000000 0000000 using 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.9/openBVE/OpenBve/OldCode/Program.cs 0000664 0000000 0000000 00000035552 11714575155 0021451 0 ustar 00root root 0000000 0000000 --- 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.9/openBVE/OpenBve/OldCode/Renderer.cs 0000664 0000000 0000000 00000461661 11714575155 0021614 0 ustar 00root root 0000000 0000000 using 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