16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-09-01 12:49:18 +02:00
Files
maszyna/vehicle/train/TrainCabSetup.cpp
2026-08-01 18:56:55 +02:00

2144 lines
86 KiB
C++

/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "vehicle/train/Train.h"
#include "utilities/Globals.h"
#include "simulation/simulation.h"
#include "world/Event.h"
#include "simulation/simulationtime.h"
#include "vehicle/Camera.h"
#include "utilities/Logs.h"
#include "model/MdlMngr.h"
#include "model/Model3d.h"
#include "utilities/Timer.h"
#include "vehicle/Driver.h"
#include "vehicle/DynObj.h"
#include "application/application.h"
#include "rendering/renderer.h"
#include <future>
#include <cmath>
#include <algorithm>
// cab movement update, fixed step part
void TTrain::UpdateCab()
{
// Ra: przesiadka, jeśli AI zmieniło kabinę (a człon?)...
if (DynamicObject->Mechanik // może nie być?
&& DynamicObject->Mechanik->AIControllFlag)
{
if (iCabn != ( // numer kabiny (-1: kabina B)
mvOccupied->CabOccupied == -1 ? 2 : mvOccupied->CabOccupied))
{
InitializeCab(mvOccupied->CabOccupied, mvOccupied->TypeName + ".mmd");
}
}
iCabn = mvOccupied->CabOccupied == -1 ? 2 : mvOccupied->CabOccupied;
}
void TTrain::update_screens(double dt)
{
for (auto &screen : m_screens)
{
if (screen.updatetimecounter >= 0)
screen.updatetimecounter += dt;
if (screen.updatetimecounter <= screen.updatetime)
continue;
screen.updatetimecounter = screen.updatetime > 0 ? 0 : -1;
auto state_dict = GetTrainState(screen.parameters);
state_dict->insert("touches", *screen.touch_list);
screen.touch_list->clear();
Application.request({screen.script, state_dict, screen.rt});
}
}
bool TTrain::CabChange(int iDirection)
{ // McZapkie-090902: zmiana kabiny 1->0->2 i z powrotem
if (DynamicObject->Mechanik == nullptr || true == DynamicObject->Mechanik->AIControllFlag)
{
// jeśli prowadzi AI albo jest w innym członie
// jak AI prowadzi, to nie można mu mieszać
if (std::abs(mvOccupied->CabOccupied + iDirection) > 1)
return false; // ewentualna zmiana pojazdu
mvOccupied->CabOccupied += iDirection;
}
else
{ // jeśli pojazd prowadzony ręcznie albo wcale (wagon)
mvOccupied->CabDeactivisationAuto();
if (mvOccupied->ChangeCab(iDirection))
{
if (InitializeCab(mvOccupied->CabOccupied, mvOccupied->TypeName + ".mmd"))
{
// zmiana kabiny w ramach tego samego pojazdu
mvOccupied->CabActivisationAuto(); // załączenie rozrządu (wirtualne kabiny)
DynamicObject->Mechanik->DirectionChange();
return true; // udało się zmienić kabinę
}
}
// aktywizacja poprzedniej, bo jeszcze nie wiadomo, czy jakiś pojazd jest
mvOccupied->CabActivisationAuto();
}
return false; // ewentualna zmiana pojazdu
}
// McZapkie-310302
// wczytywanie pliku z danymi multimedialnymi (dzwieki, kontrolki, kabiny)
bool TTrain::LoadMMediaFile(std::string const &asFileName)
{
// initialize sounds so potential entries from previous vehicle don't stick around
std::unordered_map<std::string, std::tuple<std::optional<sound_source> &, sound_placement, float, sound_type, int, double>> internalsounds = {
{"ctrl:", {dsbNastawnikJazdy, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"ctrlscnd:", {dsbNastawnikBocz, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"reverserkey:", {dsbReverserKey, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"buzzer:", {dsbBuzzer, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"buzzershp:", {dsbBuzzerShp, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"radiostop:", {m_radiostop, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"slipalarm:", {dsbSlipAlarm, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"distancecounter:", {m_distancecounterclear, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"tachoclock:", {dsbHasler, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"switch:", {dsbSwitch, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"pneumaticswitch:", {dsbPneumaticSwitch, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"airsound:", {rsHiss, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, sound_parameters::amplitude, 100.0}},
{"airsound2:", {rsHissU, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, sound_parameters::amplitude, 100.0}},
{"airsound3:", {rsHissE, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, sound_parameters::amplitude, 100.0}},
{"airsound4:", {rsHissX, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, sound_parameters::amplitude, 100.0}},
{"airsound5:", {rsHissT, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, sound_parameters::amplitude, 100.0}},
{"localbrakesound:", {rsSBHiss, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, sound_parameters::amplitude, 100.0}},
{"localbrakesound2:", {rsSBHissU, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, sound_parameters::amplitude, 100.0}},
{"brakesound:", {rsBrake, sound_placement::internal, -1, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, 100.0}},
{"fadesound:", {rsFadeSound, sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, sound_type::single, 0, 100.0}},
{"runningnoise:", {rsRunningNoise, sound_placement::internal, EU07_SOUND_GLOBALRANGE, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax}},
{"resonancenoise:", {rsResonanceNoise, sound_placement::internal, EU07_SOUND_GLOBALRANGE, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax}},
{"windsound:", {rsWindSound, sound_placement::internal, EU07_SOUND_GLOBALRANGE, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax}},
{"huntingnoise:", {rsHuntingNoise, sound_placement::internal, EU07_SOUND_GLOBALRANGE, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax}},
{"rainsound:", {m_rainsound, sound_placement::internal, -1, sound_type::single, 0, 100.0}},
};
for (auto &soundconfig : internalsounds)
{
std::get<std::optional<sound_source> &>(soundconfig.second).reset();
}
// NOTE: since radiosound is an incomplete template not using std::optional it gets a special treatment
m_radiosound.owner(DynamicObject);
CabSoundLocations.clear();
cParser parser(asFileName, cParser::buffer_FILE, DynamicObject->asBaseDir, true, std::vector<std::string>(), true);
// NOTE: yaml-style comments are disabled until conflict in use of # is resolved
// parser.addCommentStyle( "#", "\n" );
std::string token;
do
{
token = "";
parser.getTokens();
parser >> token;
} while (token != "" && token != "internaldata:");
if (token == "internaldata:")
{
do
{
token = "";
parser.getTokens();
parser >> token;
auto lookup{internalsounds.find(token)};
if (lookup == internalsounds.end())
{
continue;
}
auto const soundconfig{lookup->second};
sound_source sound{std::get<sound_placement>(soundconfig), std::get<float>(soundconfig)};
sound.deserialize(parser, std::get<sound_type>(soundconfig), std::get<int>(soundconfig), std::get<double>(soundconfig));
sound.owner(DynamicObject);
std::get<std::optional<sound_source> &>(soundconfig) = sound;
} while (token != "");
// assign default samples to sound emitters which weren't included in the config file
if (!m_rainsound)
{
sound_source rainsound;
rainsound.deserialize("rainsound_default", sound_type::single);
rainsound.owner(DynamicObject);
m_rainsound = rainsound;
}
if (!rsSBHiss)
{
// fallback for vehicles without defined local brake hiss sound
rsSBHiss = rsHiss;
}
if (!rsSBHissU)
{
// fallback for vehicles without defined local brake hiss sound
rsSBHissU = rsHissU;
}
if (rsBrake)
{
rsBrake->m_frequencyfactor /= 1 + mvOccupied->Vmax;
}
if (rsResonanceNoise)
{
rsResonanceNoise->m_frequencyfactor /= 1 + mvOccupied->Vmax;
}
if (rsWindSound)
{
rsWindSound->m_frequencyfactor /= 1 + mvOccupied->Vmax;
}
if (rsRunningNoise)
{
rsRunningNoise->m_frequencyfactor /= 1 + mvOccupied->Vmax;
}
if (rsHuntingNoise)
{
rsHuntingNoise->m_frequencyfactor /= 1 + mvOccupied->Vmax;
}
}
auto const nullvector{glm::vec3()};
std::vector<std::reference_wrapper<std::optional<sound_source>>> sounds = {
dsbReverserKey, dsbNastawnikJazdy, dsbNastawnikBocz, dsbSwitch, dsbPneumaticSwitch, rsHiss, rsHissU, rsHissE, rsHissX, rsHissT, rsSBHiss,
rsSBHissU, rsFadeSound, rsRunningNoise, rsResonanceNoise, rsWindSound, rsHuntingNoise, dsbHasler, dsbBuzzer, dsbBuzzerShp, dsbSlipAlarm, m_distancecounterclear,
m_rainsound, m_radiostop};
for (auto &sound : sounds)
{
if (sound.get())
{
CabSoundLocations.emplace_back(sound, sound.get()->offset());
}
}
return true;
}
bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
{
m_controlmapper.clear();
// clear python screens
m_screens.clear();
// reset sound positions
auto const nullvector{glm::vec3()};
std::vector<std::reference_wrapper<std::optional<sound_source>>> sounds = {
dsbReverserKey, dsbNastawnikJazdy, dsbNastawnikBocz, dsbSwitch, dsbPneumaticSwitch, rsHiss, rsHissU, rsHissE, rsHissX, rsHissT, rsSBHiss,
rsSBHissU, rsFadeSound, rsRunningNoise, rsResonanceNoise, rsWindSound, rsHuntingNoise, dsbHasler, dsbBuzzer, dsbBuzzerShp, dsbSlipAlarm, m_distancecounterclear,
m_rainsound, m_radiostop};
for (auto &sound : sounds)
{
if (sound.get())
{
sound.get()->offset(nullvector);
}
}
m_radiosound.offset(nullvector);
for (auto &sound : CabSoundLocations)
{
if (sound.first.get() && sound.first.get()->offset() == nullvector)
{
sound.first.get()->offset(sound.second);
}
}
// reset view angles
pMechViewAngle = {0.0, 0.0};
is_cab_initialized = true; // the attempt may fail, but it's the attempt that counts
bool parse = false;
int cabindex = 0;
DynamicObject->mdKabina = nullptr; // likwidacja wskaźnika na dotychczasową kabinę
switch (NewCabNo)
{ // ustalenie numeru kabiny do wczytania
case -1:
cabindex = 2;
break;
case 1:
cabindex = 1;
break;
case 0:
cabindex = 0;
break;
}
iCabn = cabindex;
std::string cabstr("cab" + std::to_string(cabindex) + "definition:");
cParser parser(asFileName, cParser::buffer_FILE, DynamicObject->asBaseDir, true, std::vector<std::string>(), true);
// NOTE: yaml-style comments are disabled until conflict in use of # is resolved
// parser.addCommentStyle( "#", "\n" );
std::string token;
do
{
// szukanie kabiny
token = "";
parser.getTokens();
parser >> token;
} while (token != "" && token != cabstr);
if (token == cabstr)
{
// jeśli znaleziony wpis kabiny
Cabine[cabindex].Load(parser);
// NOTE: the position and angle definitions depend on strict entry order
// TODO: refactor into more flexible arrangement
parser.getTokens();
parser >> token;
if (token == std::string("driver" + std::to_string(cabindex) + "angle:"))
{
// camera view angle
parser.getTokens(2, false);
// angle is specified in degrees but internally stored in radians
glm::vec2 viewangle;
parser >> viewangle.y // yaw first, then pitch
>> viewangle.x;
pMechViewAngle = glm::radians(viewangle);
Global.pCamera.Angle.x = pMechViewAngle.x;
Global.pCamera.Angle.y = pMechViewAngle.y;
parser.getTokens();
parser >> token;
}
if (token == std::string("driver" + std::to_string(cabindex) + "pos:"))
{
// pozycja poczatkowa maszynisty
parser.getTokens(3, false);
parser >> pMechOffset.x >> pMechOffset.y >> pMechOffset.z;
pMechSittingPosition = pMechOffset;
parser.getTokens();
parser >> token;
}
// ABu: pozycja siedzaca mechanika
if (token == std::string("driver" + std::to_string(cabindex) + "sitpos:"))
{
// ABu 180404 pozycja siedzaca maszynisty
parser.getTokens(3, false);
parser >> pMechSittingPosition.x >> pMechSittingPosition.y >> pMechSittingPosition.z;
parser.getTokens();
parser >> token;
}
// else parse=false;
do
{
if (parse == true)
{
token = "";
parser.getTokens();
parser >> token;
}
else
{
parse = true;
}
// inicjacja kabiny
// Ra 2014-08: zmieniamy zasady - zamiast przypisywać submodel do
// istniejących obiektów animujących
// będziemy teraz uaktywniać obiekty animujące z tablicy i podawać im
// submodel oraz wskaźnik na parametr
if (token == std::string("cab" + std::to_string(cabindex) + "model:"))
{
// model kabiny
parser.getTokens();
parser >> token;
std::replace(token.begin(), token.end(), '\\', '/');
if (token != "none")
{
// bieżąca sciezka do tekstur to dynamic/...
Global.asCurrentTexturePath = DynamicObject->asBaseDir;
// szukaj kabinę jako oddzielny model
// name can contain leading slash, erase it to avoid creation of double slashes when the name is combined with current directory
replace_slashes(token);
erase_leading_slashes(token);
if (token[0] == '/')
{
token.erase(0, 1);
}
TModel3d *kabina = TModelsManager::GetModel(DynamicObject->asBaseDir + token, true, true, Global.network_servers.empty() && !Global.network_client ? 0 : id());
// z powrotem defaultowa sciezka do tekstur
Global.asCurrentTexturePath = paths::textures;
// if (DynamicObject->mdKabina!=k)
if (kabina != nullptr)
{
DynamicObject->mdKabina = kabina; // nowa kabina
}
//(mdKabina) może zostać to samo po przejściu do innego członu bez
// zmiany kabiny, przy powrocie musi być wiązanie ponowne
// else
// break; //wyjście z pętli, bo model zostaje bez zmian
}
else if (cabindex == 1)
{
// model tylko, gdy nie ma kabiny 1
// McZapkie-170103: szukaj elementy kabiny w glownym modelu
DynamicObject->mdKabina = DynamicObject->mdModel;
}
clear_cab_controls();
}
/*
if (nullptr == DynamicObject->mdKabina)
{
// don't bother with other parts until the cab is initialised
continue;
}
*/
else if (true == initialize_gauge(parser, token, cabindex))
{
// matched the token, grab the next one
continue;
}
else if (true == initialize_button(parser, token, cabindex))
{
// matched the token, grab the next one
continue;
}
// TODO: add "pydestination:"
else if (token == "pyscreen:")
{
screen_entry screen;
screen.deserialize(parser);
if (false == screen.script.empty() && substr_path(screen.script).empty())
{
screen.script = DynamicObject->asBaseDir + screen.script;
}
ITexture *tex = nullptr;
TSubModel *submodel = nullptr;
if (screen.target != "none")
{
submodel = DynamicObject->mdKabina ? DynamicObject->mdKabina->GetFromName(screen.target) :
DynamicObject->mdLowPolyInt ? DynamicObject->mdLowPolyInt->GetFromName(screen.target) :
nullptr;
if (submodel == nullptr)
{
WriteLog("Python Screen: submodel " + screen.target + " not found - Ignoring screen");
continue;
}
auto const material{submodel->GetMaterial()};
if (material <= 0)
{
// sub model nie posiada tekstury lub tekstura wymienna - nie obslugiwana
WriteLog("Python Screen: invalid texture id " + std::to_string(material) + " - Ignoring screen");
continue;
}
tex = &GfxRenderer->Texture(GfxRenderer->Material(material)->GetTexture(0));
}
else
{
// TODO: fix leak
tex = ITexture::null_texture();
tex->make_stub();
}
tex->create(true); // make the surface static so it doesn't get destroyed by garbage
// collector if the user spends long time outside cab
// TBD, TODO: keep texture handles around, so we can undo the static switch when the
// user changes cabs?
auto rt = std::make_shared<python_rt>();
rt->shared_tex = tex;
// record renderer and material binding for future update requests
m_screens.emplace_back(screen);
m_screens.back().rt = rt;
m_screens.back().touch_list = std::make_shared<std::vector<glm::vec2>>();
if (submodel)
submodel->screen_touch_list = m_screens.back().touch_list;
if (Global.python_displaywindows)
m_screens.back().viewer = std::make_unique<python_screen_viewer>(rt, m_screens.back().touch_list, m_screens.back().script);
}
else if (token == "pyscreenupdatetime:")
{
parser.getTokens();
parser >> ScreenUpdateRate;
}
// btLampkaUnknown`"unknown",mdKabina,false);
} while (token != ""
// TODO: enable full per-cab deserialization when/if .mmd files get proper per-cab switch configuration
// && ( token != "cab1definition:" )
// && ( token != "cab2definition:" )
&& token != "cab0definition:");
for (auto &screen : m_screens)
{
if (screen.updatetime > 0)
{
screen.updatetime = std::max((int)screen.updatetime, Global.PythonScreenUpdateRate) * 0.001;
}
if (screen.updatetime == 0)
{
screen.updatetime = std::max(Global.PythonScreenUpdateRate, ScreenUpdateRate) * 0.001;
}
if (screen.updatetime < -1)
{
screen.updatetime = -screen.updatetime * 0.001;
}
}
}
else
{
return false;
}
/*
if (DynamicObject->mdKabina)
{
*/
// configure placement of sound emitters which aren't bound with any device model, and weren't placed manually
auto const caboffset{glm::dvec3{(Cabine[cabindex].CabPos1 + Cabine[cabindex].CabPos2) * 0.5f} + glm::dvec3{0, 1, 0}};
// NOTE: since radiosound is an incomplete template not using std::optional it gets a special treatment
if (m_radiosound.offset() == nullvector)
{
m_radiosound.offset(btLampkaRadio.model_offset());
}
if (m_radiosound.offset() == nullvector)
{
m_radiosound.offset(caboffset);
}
std::vector<std::pair<std::reference_wrapper<std::optional<sound_source>>, glm::vec3>> soundlocations = {
{dsbReverserKey, ggDirKey.model_offset()},
{dsbNastawnikJazdy, ggJointCtrl.model_offset()},
{dsbNastawnikJazdy, ggMainCtrl.model_offset()}, // NOTE: fallback for vehicles without universal controller
{dsbNastawnikBocz, ggScndCtrl.model_offset()},
{dsbSwitch, caboffset},
{dsbPneumaticSwitch, caboffset},
{rsHiss, ggBrakeCtrl.model_offset()},
{rsHissU, ggBrakeCtrl.model_offset()},
{rsHissE, ggBrakeCtrl.model_offset()},
{rsHissX, ggBrakeCtrl.model_offset()},
{rsHissT, ggBrakeCtrl.model_offset()},
{rsSBHiss, ggLocalBrake.model_offset()},
{rsSBHiss, ggBrakeCtrl.model_offset()}, // NOTE: fallback if the local brake model can't be located
{rsSBHissU, ggLocalBrake.model_offset()},
{rsSBHissU, ggBrakeCtrl.model_offset()}, // NOTE: fallback if the local brake model can't be located
{rsFadeSound, caboffset},
{rsRunningNoise, caboffset},
{rsResonanceNoise, caboffset},
{rsWindSound, caboffset},
{rsHuntingNoise, caboffset},
{dsbHasler, caboffset},
{dsbBuzzer, btLampkaCzuwaka.model_offset()},
{dsbBuzzerShp, btLampkaCzuwaka.model_offset()},
{dsbSlipAlarm, caboffset},
{m_distancecounterclear, btLampkaCzuwaka.model_offset()},
{m_rainsound, caboffset},
{m_radiostop, m_radiosound.offset()},
};
for (auto &sound : soundlocations)
{
if (sound.first.get() && sound.first.get()->offset() == nullvector)
{
sound.first.get()->offset(sound.second);
}
}
// second pass, in case some items received no positioning due to missing submodels etc
for (auto &sound : soundlocations)
{
if (sound.first.get() && sound.first.get()->offset() == nullvector)
{
sound.first.get()->offset(caboffset);
}
}
if (DynamicObject->mdKabina)
DynamicObject->mdKabina->Init(); // obrócenie modelu oraz optymalizacja, również zapisanie binarnego
set_cab_controls(NewCabNo < 0 ? 2 : NewCabNo);
// set pantograpths (hujhujhuj)
change_pantograph_selection(0);
/*
return true;
}
return (token == "none");
*/
return true;
}
glm::dvec3 TTrain::MirrorPosition(bool lewe)
{ // zwraca współrzędne widoku kamery z lusterka
auto const shiftdirection{(lewe ? -1 : 1) * (iCabn == 2 ? 1 : -1)};
return DynamicObject->mMatrix *
glm::dvec4(mvOccupied->Dim.W * (0.5 * shiftdirection) + 0.2 * shiftdirection, 1.5 + Cabine[iCabn].CabPos1.y, std::lerp(Cabine[iCabn].CabPos1.z, Cabine[iCabn].CabPos2.z, 0.5), 1.0);
};
void TTrain::DynamicSet(TDynamicObject *d)
{ // taka proteza: chcę podłączyć
// kabinę EN57 bezpośrednio z
// silnikowym, aby nie robić tego
// przez ukrotnienie
// drugi silnikowy i tak musi być ukrotniony, podobnie jak kolejna jednostka
// problem się robi ze światłami, które będą zapalane w silnikowym, ale muszą
// świecić się w rozrządczych
// dla EZT światła czołowe będą "zapalane w silnikowym", ale widziane z
// rozrządczych
// również wczytywanie MMD powinno dotyczyć aktualnego członu
// problematyczna może być kwestia wybranej kabiny (w silnikowym...)
// jeśli silnikowy będzie zapięty odwrotnie (tzn. -1), to i tak powinno
// jeździć dobrze
// również hamowanie wykonuje się zaworem w członie, a nie w silnikowym...
DynamicObject = d; // jedyne miejsce zmiany
mvOccupied = mvControlled = d ? DynamicObject->MoverParameters : nullptr; // albo silnikowy w EZT
if (DynamicObject == nullptr)
{
return;
}
mvControlled = DynamicObject->FindPowered()->MoverParameters;
mvSecond = nullptr; // gdyby się nic nie znalazło
if (mvOccupied->Power > 1.0) // dwuczłonowe lub ukrotnienia, żeby nie szukać każdorazowo
if (mvOccupied->Couplers[1].Connected ? mvOccupied->Couplers[1].AllowedFlag & coupling::control : false)
{ // gdy jest człon od sprzęgu 1, a sprzęg łączony
// warsztatowo (powiedzmy)
if (mvOccupied->Couplers[1].Connected->Power > 1.0) // ten drugi ma moc
mvSecond = (TMoverParameters *)mvOccupied->Couplers[1].Connected; // wskaźnik na drugiego
}
else if (mvOccupied->Couplers[0].Connected ? mvOccupied->Couplers[0].AllowedFlag & coupling::control : false)
{ // gdy jest człon od sprzęgu 0, a sprzęg łączony
// warsztatowo (powiedzmy)
if (mvOccupied->Couplers[0].Connected->Power > 1.0) // ale ten drugi ma moc
mvSecond = (TMoverParameters *)mvOccupied->Couplers[0].Connected; // wskaźnik na drugiego
}
// cache nearest unit equipped with pantographs
{
auto *lookup{DynamicObject->FindPantographCarrier()};
// HACK: set pointer to existing vehicle to avoid error checking all over the place
mvPantographUnit = lookup != nullptr ? lookup->MoverParameters : mvControlled;
}
};
void TTrain::MoveToVehicle(TDynamicObject *target)
{
// > Ra: to nie może być tak robione, to zbytnia proteza jest
// indeed, too much hacks...
// TODO: cleanup
TTrain *target_train = simulation::Trains.find(target->name());
if (target_train)
{
// let's try to destroy this TTrain and move to already existing one
if (!Dynamic()->Mechanik || !Dynamic()->Mechanik->AIControllFlag)
{
// tylko jeśli ręcznie prowadzony
// jeśli prowadzi AI, to mu nie robimy dywersji!
Occupied()->CabDeactivisation();
Occupied()->CabOccupied = 0;
Occupied()->BrakeLevelSet(Occupied()->Handle->GetPos(bh_NP)); // rozwala sterowanie hamulcem GF 04-2016
Occupied()->MainCtrlPos = Occupied()->MainCtrlNoPowerPos();
Occupied()->ScndCtrlPos = 0;
Dynamic()->MechInside = false;
Dynamic()->Controller = AIdriver;
Dynamic()->bDisplayCab = false;
Dynamic()->ABuSetModelShake({});
if (Dynamic()->Mechanik)
Dynamic()->Mechanik->MoveTo(target);
target_train->Occupied()->LimPipePress = target_train->Occupied()->PipePress;
target_train->Occupied()->CabActivisationAuto(true); // załączenie rozrządu (wirtualne kabiny)
target_train->Dynamic()->MechInside = true;
if (target_train->Dynamic()->Mechanik)
{
target_train->Dynamic()->Controller = target_train->Dynamic()->Mechanik->AIControllFlag;
target_train->Dynamic()->Mechanik->DirectionChange();
}
else
{
target_train->Dynamic()->Controller = Humandriver;
}
}
else
{
target_train->Dynamic()->bDisplayCab = false;
target_train->Dynamic()->ABuSetModelShake({});
}
target_train->Dynamic()->ABuSetModelShake({}); // zerowanie przesunięcia przed powrotem?
// potentially move player
if (simulation::Train == this)
{
simulation::Train = target_train;
// our local driver may potentially be in external view mode, in which case we shouldn't activate cab visualization
target_train->Dynamic()->bDisplayCab |= !FreeFlyModeFlag;
}
// delete this TTrain
pending_delete = true;
}
else
{
// move this TTrain to other dynamic
// remove TTrain from global list, we're going to change dynamic anyway
simulation::Trains.detach(Dynamic()->name());
if (!Dynamic()->Mechanik || !Dynamic()->Mechanik->AIControllFlag)
{
// tylko jeśli ręcznie prowadzony
// jeśli prowadzi AI, to mu nie robimy dywersji!
Occupied()->CabDeactivisation();
Occupied()->CabOccupied = 0;
Occupied()->BrakeLevelSet(Occupied()->Handle->GetPos(bh_NP)); // rozwala sterowanie hamulcem GF 04-2016
Occupied()->MainCtrlPos = Occupied()->MainCtrlNoPowerPos();
Occupied()->ScndCtrlPos = 0;
Dynamic()->MechInside = false;
Dynamic()->Controller = AIdriver;
Dynamic()->bDisplayCab = false;
Dynamic()->ABuSetModelShake({});
if (Dynamic()->Mechanik)
Dynamic()->Mechanik->MoveTo(target);
DynamicSet(target);
Dynamic()->MechInside = true;
if (Dynamic()->Mechanik)
{
Dynamic()->Controller = Dynamic()->Mechanik->AIControllFlag;
Dynamic()->Mechanik->DirectionChange();
}
else
{
Dynamic()->Controller = Humandriver;
}
Occupied()->LimPipePress = Occupied()->PipePress;
Occupied()->CabActivisationAuto(true); // załączenie rozrządu (wirtualne kabiny)
}
else
{
Dynamic()->bDisplayCab = false;
Dynamic()->ABuSetModelShake({});
DynamicSet(target);
}
{
auto const filename{Occupied()->TypeName + ".mmd"};
LoadMMediaFile(filename);
InitializeCab(Occupied()->CabActive, filename);
}
Dynamic()->ABuSetModelShake({}); // zerowanie przesunięcia przed powrotem?
if (simulation::Train == this)
{
// our local driver may potentially be in external view mode, in which case we shouldn't activate cab visualization
Dynamic()->bDisplayCab |= !FreeFlyModeFlag;
}
// add it back with updated dynamic name
simulation::Trains.insert(this);
}
}
// checks whether specified point is within boundaries of the active cab
bool TTrain::point_inside(glm::dvec3 const Point) const
{
return Point.x >= Cabine[iCabn].CabPos1.x && Point.x <= Cabine[iCabn].CabPos2.x && Point.y >= Cabine[iCabn].CabPos1.y + 0.5 && Point.y <= Cabine[iCabn].CabPos2.y + 1.8 &&
Point.z >= Cabine[iCabn].CabPos1.z && Point.z <= Cabine[iCabn].CabPos2.z;
}
glm::dvec3 TTrain::clamp_inside(glm::dvec3 const &Point) const
{
if (DebugModeFlag)
{
return Point;
}
return {std::clamp(Point.x, (double)Cabine[iCabn].CabPos1.x, (double)Cabine[iCabn].CabPos2.x), std::clamp(Point.y, (double)Cabine[iCabn].CabPos1.y + 0.5, (double)Cabine[iCabn].CabPos2.y + 1.8),
std::clamp(Point.z, (double)Cabine[iCabn].CabPos1.z, (double)Cabine[iCabn].CabPos2.z)};
}
const TTrain::screenentry_sequence &TTrain::get_screens()
{
update_screens(Timer::GetDeltaTime());
return m_screens;
}
// clears state of all cabin controls
void TTrain::clear_cab_controls()
{
// indicators exposed to custom control devices
btLampkaSHP.Clear(0);
btLampkaCzuwaka.Clear(1);
btLampkaOpory.Clear(2);
btLampkaWylSzybki.Clear(3);
btLampkaNadmSil.Clear(4);
btLampkaStyczn.Clear(5);
btLampkaPoslizg.Clear(6);
btLampkaNadmPrzetw.Clear((mvControlled->TrainType & dt_EZT) != 0 ? -1 : 7); // EN57 nie ma tej lampki
btLampkaPrzetwOff.Clear((mvControlled->TrainType & dt_EZT) != 0 ? 7 : -1); // za to ma tę
btLampkaNadmSpr.Clear(8);
btLampkaNadmWent.Clear(9);
btLampkaWysRozr.Clear((mvControlled->TrainType & dt_ET22) != 0 ? -1 : 10); // ET22 nie ma tej lampki
btLampkaOgrzewanieSkladu.Clear(11);
// overheat indicator lamps
btLampkaOilOverheat.Clear(-1);
btLampkaWaterOverheat.Clear(-1);
btLampkaWaterAuxOverheat.Clear(-1);
btLampkaEngineOverheat.Clear(-1);
btHaslerBrakes.Clear(12); // ciśnienie w cylindrach do odbijania na haslerze
btHaslerCurrent.Clear(13); // prąd na silnikach do odbijania na haslerze
// Numer 14 jest używany dla buczka SHP w update_sounds()
// Jeśli ustawiamy nową wartość dla PoKeys wolna jest 15
// other cab controls
// TODO: arrange in more readable manner, and eventually refactor
ggJointCtrl.Clear();
ggMainCtrl.Clear();
ggMainCtrlAct.Clear();
ggScndCtrl.Clear();
ggScndCtrlButton.Clear();
ggScndCtrlOffButton.Clear();
ggDistanceCounterButton.Clear();
ggDirKey.Clear();
ggDirForwardButton.Clear();
ggDirNeutralButton.Clear();
ggDirBackwardButton.Clear();
ggBrakeCtrl.Clear();
ggLocalBrake.Clear();
ggAlarmChain.Clear();
ggBrakeProfileCtrl.Clear();
ggBrakeProfileG.Clear();
ggBrakeProfileR.Clear();
ggBrakeOperationModeCtrl.Clear();
ggWiperSw.Clear();
ggMaxCurrentCtrl.Clear();
ggMainOffButton.Clear();
ggMainOnButton.Clear();
ggSecurityResetButton.Clear();
ggSHPResetButton.Clear();
ggReleaserButton.Clear();
ggSpringBrakeOnButton.Clear();
ggSpringBrakeOffButton.Clear();
ggUniveralBrakeButton1.Clear();
ggUniveralBrakeButton2.Clear();
ggUniveralBrakeButton3.Clear();
ggEPFuseButton.Clear();
ggSandButton.Clear();
ggAutoSandButton.Clear();
ggAntiSlipButton.Clear();
ggHornButton.Clear();
ggHornLowButton.Clear();
ggHornHighButton.Clear();
ggWhistleButton.Clear();
ggHelperButton.Clear();
ggNextCurrentButton.Clear();
ggSpeedControlIncreaseButton.Clear();
ggSpeedControlDecreaseButton.Clear();
ggSpeedControlPowerIncreaseButton.Clear();
ggSpeedControlPowerDecreaseButton.Clear();
for (auto &speedctrlbutton : ggSpeedCtrlButtons)
{
speedctrlbutton.Clear();
}
for (auto &universal : ggUniversals)
{
universal.Clear();
}
for (auto &item : ggInverterEnableButtons)
{
item.Clear();
}
for (auto &item : ggInverterDisableButtons)
{
item.Clear();
}
for (auto &item : ggInverterToggleButtons)
{
item.Clear();
}
for (auto &relayresetbutton : ggRelayResetButtons)
{
relayresetbutton.Clear();
}
ggInstrumentLightButton.Clear();
ggDashboardLightButton.Clear();
ggTimetableLightButton.Clear();
// hunter-091012
ggCabLightDimButton.Clear();
ggCompartmentLightsButton.Clear();
ggCompartmentLightsOnButton.Clear();
ggCompartmentLightsOffButton.Clear();
ggBatteryButton.Clear();
ggBatteryOnButton.Clear();
ggBatteryOffButton.Clear();
ggCabActivationButton.Clear();
//-------
ggFuseButton.Clear();
ggConverterFuseButton.Clear();
ggStLinOffButton.Clear();
ggRadioChannelSelector.Clear();
ggRadioChannelPrevious.Clear();
ggRadioChannelNext.Clear();
ggRadioStop.Clear();
ggRadioTest.Clear();
ggRadioCall1.Clear();
ggRadioCall3.Clear();
ggRadioVolumeSelector.Clear();
ggRadioVolumePrevious.Clear();
ggRadioVolumeNext.Clear();
ggDoorLeftPermitButton.Clear();
ggDoorRightPermitButton.Clear();
ggDoorPermitPresetButton.Clear();
ggDoorLeftButton.Clear();
ggDoorRightButton.Clear();
ggDoorLeftOnButton.Clear();
ggDoorRightOnButton.Clear();
ggDoorLeftOffButton.Clear();
ggDoorRightOffButton.Clear();
ggDoorAllOnButton.Clear();
ggDoorAllOffButton.Clear();
ggTrainHeatingButton.Clear();
ggSignallingButton.Clear();
ggDoorSignallingButton.Clear();
ggDoorStepButton.Clear();
ggDepartureSignalButton.Clear();
ggCompressorButton.Clear();
ggCompressorLocalButton.Clear();
ggConverterButton.Clear();
ggConverterOffButton.Clear();
ggConverterLocalButton.Clear();
ggMainButton.Clear();
/*
ggPantFrontButton.Clear();
ggPantRearButton.Clear();
ggPantFrontButtonOff.Clear();
ggPantRearButtonOff.Clear();
*/
ggPantAllDownButton.Clear();
ggPantSelectedButton.Clear();
ggPantSelectedDownButton.Clear();
ggPantValvesButton.Clear();
ggPantCompressorButton.Clear();
ggPantCompressorValve.Clear();
ggPantValvesOff.Clear();
ggPantValvesUpdate.Clear();
ggI1B.Clear();
ggI2B.Clear();
ggI3B.Clear();
ggItotalB.Clear();
ggOilPressB.Clear();
ggWater1TempB.Clear();
ggClockSInd.Clear();
ggClockMInd.Clear();
ggClockHInd.Clear();
ggEngineVoltage.Clear();
ggLVoltage.Clear();
ggMainGearStatus.Clear();
ggIgnitionKey.Clear();
ggWaterPumpBreakerButton.Clear();
ggWaterPumpButton.Clear();
ggWaterHeaterBreakerButton.Clear();
ggWaterHeaterButton.Clear();
ggWaterCircuitsLinkButton.Clear();
ggFuelPumpButton.Clear();
ggOilPumpButton.Clear();
ggMotorBlowersFrontButton.Clear();
ggMotorBlowersRearButton.Clear();
ggMotorBlowersAllOffButton.Clear();
btLampkaPrzetw.Clear();
btLampkaPrzetwB.Clear();
btLampkaPrzetwBOff.Clear();
btLampkaPrzekRozn.Clear();
btLampkaPrzekRoznPom.Clear();
btLampkaUkrotnienie.Clear();
btLampkaHamPosp.Clear();
btLampkaWylSzybkiOff.Clear();
btLampkaWylSzybkiB.Clear();
btLampkaWylSzybkiBOff.Clear();
btLampkaMainBreakerReady.Clear();
btLampkaMainBreakerBlinkingIfReady.Clear();
btLampkaBezoporowa.Clear();
btLampkaBezoporowaB.Clear();
btLampkaMaxSila.Clear();
btLampkaPrzekrMaxSila.Clear();
btLampkaRadio.Clear();
btLampkaRadioMessage.Clear();
btLampkaRadioStop.Clear();
btLampkaHamulecReczny.Clear();
btLampkaBlokadaDrzwi.Clear();
btLampkaDoorLockOff.Clear();
for (auto &universal : btUniversals)
{
universal.Clear();
}
btInstrumentLight.Clear();
btDashboardLight.Clear();
btTimetableLight.Clear();
btLampkaWentZaluzje.Clear();
btLampkaDoorLeft.Clear();
btLampkaDoorRight.Clear();
btLampkaDepartureSignal.Clear();
btLampkaRezerwa.Clear();
btLampkaBoczniki.Clear();
btLampkaBocznik1.Clear();
btLampkaBocznik2.Clear();
btLampkaBocznik3.Clear();
btLampkaBocznik4.Clear();
btLampkaGear1.Clear();
btLampkaGear2.Clear();
btLampkaHydroLockup.Clear();
btLampkaRadiotelefon.Clear();
btLampkaHamienie.Clear();
btLampkaBrakingOff.Clear();
btLampkaED.Clear();
btLampkaBrakeProfileG.Clear();
btLampkaBrakeProfileP.Clear();
btLampkaBrakeProfileR.Clear();
btLampkaSpringBrakeActive.Clear();
btLampkaSpringBrakeInactive.Clear();
btLampkaSprezarka.Clear();
btLampkaSprezarkaB.Clear();
btLampkaSprezarkaOff.Clear();
btLampkaSprezarkaBOff.Clear();
btLampkaFuelPumpOff.Clear();
btLampkaNapNastHam.Clear();
btLampkaOporyB.Clear();
btLampkaStycznB.Clear();
btLampkaHamowanie1zes.Clear();
btLampkaHamowanie2zes.Clear();
btLampkaNadmPrzetwB.Clear();
btLampkaHVoltageB.Clear();
btLampkaForward.Clear();
btLampkaBackward.Clear();
btLampkaNeutral.Clear();
// light indicators
btLampkaUpperLight.Clear();
btLampkaLeftLight.Clear();
btLampkaRightLight.Clear();
btLampkaLeftEndLight.Clear();
btLampkaRightEndLight.Clear();
btLampkaRearUpperLight.Clear();
btLampkaRearLeftLight.Clear();
btLampkaRearRightLight.Clear();
btLampkaRearLeftEndLight.Clear();
btLampkaRearRightEndLight.Clear();
// others
btLampkaMalfunction.Clear();
btLampkaMalfunctionB.Clear();
btLampkaMotorBlowers.Clear();
btLampkaCoolingFans.Clear();
btLampkaTempomat.Clear();
btLampkaDistanceCounter.Clear();
ggLeftLightButton.Clear();
ggRightLightButton.Clear();
ggUpperLightButton.Clear();
ggDimHeadlightsButton.Clear();
ggModernLightDimSw.Clear();
ggLeftEndLightButton.Clear();
ggRightEndLightButton.Clear();
ggLightsButton.Clear();
// hunter-230112
ggRearLeftLightButton.Clear();
ggRearRightLightButton.Clear();
ggRearUpperLightButton.Clear();
ggRearLeftEndLightButton.Clear();
ggRearRightEndLightButton.Clear();
}
// NOTE: we can get rid of this function once we have per-cab persistent state
void TTrain::set_cab_controls(int const Cab)
{
// switches
// battery
ggBatteryButton.PutValue(ggBatteryButton.type() == TGaugeType::push ? 0.5f : mvOccupied->Power24vIsAvailable ? 1.f : 0.f);
// activation
ggCabActivationButton.PutValue(ggCabActivationButton.type() == TGaugeType::push ? 0.5f : mvOccupied->IsCabMaster() ? 1.f : 0.f);
// line breaker
if (ggMainButton.SubModel != nullptr)
{ // instead of single main button there can be on/off pair
ggMainButton.PutValue(ggMainButton.type() == TGaugeType::push ? 0.5f : m_linebreakerstate > 0 ? 1.f : 0.f);
}
if (ggModernLightDimSw.SubModel != nullptr)
{
mvOccupied->modernDimmerPosition = mvOccupied->modernDimmerDefaultPosition;
ggModernLightDimSw.PutValue(mvOccupied->modernDimmerDefaultPosition);
}
// Init separate buttons
if (ggPantValvesUpdate.SubModel != nullptr)
{
ggPantValvesUpdate.PutValue(0.f);
}
if (ggPantValvesOff.SubModel != nullptr)
{
ggPantValvesOff.PutValue(0.f);
}
// motor connectors
ggStLinOffButton.PutValue(mvControlled->StLinSwitchOff ? 1.f : 0.f);
// radio
ggRadioChannelSelector.PutValue((Dynamic()->Mechanik ? Dynamic()->Mechanik->iRadioChannel : 1) - 1);
// pantographs
/*
if( mvOccupied->PantSwitchType != "impulse" ) {
if( ggPantFrontButton.SubModel ) {
ggPantFrontButton.PutValue(
( mvControlled->Pantographs[end::front].valve.is_enabled ?
1.f :
0.f ) );
}
if( ggPantFrontButtonOff.SubModel ) {
ggPantFrontButtonOff.PutValue(
( mvControlled->Pantographs[end::front].valve.is_disabled ?
1.f :
0.f ) );
}
}
if( mvOccupied->PantSwitchType != "impulse" ) {
if( ggPantRearButton.SubModel ) {
ggPantRearButton.PutValue(
( mvControlled->Pantographs[end::rear].valve.is_enabled ?
1.f :
0.f ) );
}
if( ggPantRearButtonOff.SubModel ) {
ggPantRearButtonOff.PutValue(
( mvControlled->Pantographs[end::rear].valve.is_disabled ?
1.f :
0.f ) );
}
}
*/
// front/end pantograph selection is relative to occupied cab
if (ggPantSelectedButton.type() == TGaugeType::toggle)
{
ggPantSelectedButton.PutValue(mvPantographUnit->PantsValve.is_enabled ? 1.f : 0.f);
}
else
{
if (false == m_controlmapper.contains("pantselectedoff_sw:"))
{
// single impulse switch arrangement, with neutral position mid-way
ggPantSelectedButton.PutValue(0.5f);
}
}
if (ggPantSelectedDownButton.type() == TGaugeType::toggle)
{
ggPantSelectedDownButton.PutValue(mvPantographUnit->PantsValve.is_disabled ? 1.f : 0.f);
}
ggPantValvesButton.PutValue(0.5f);
// auxiliary compressor
ggPantCompressorValve.PutValue(mvControlled->bPantKurek3 ? 0.f : // default setting is pantographs connected with primary tank
1.f);
ggPantCompressorButton.PutValue(mvPantographUnit->PantCompFlag ? 1.f : 0.f);
// converter
if (mvOccupied->ConvSwitchType != "impulse")
{
ggConverterButton.PutValue(mvControlled->ConverterAllow ? 1.f : 0.f);
}
ggConverterLocalButton.PutValue(mvControlled->ConverterAllowLocal ? 1.f : 0.f);
// compressor
ggCompressorButton.PutValue(mvControlled->CompressorAllow ? 1.f : 0.f);
ggCompressorLocalButton.PutValue(mvControlled->CompressorAllowLocal ? 1.f : 0.f);
ggCompressorListButton.PutValue(mvOccupied->CompressorListPos - 1);
// motor overload relay threshold / shunt mode
ggMaxCurrentCtrl.PutValue(true == mvControlled->ShuntModeAllow ? (true == mvControlled->ShuntMode ? 1.f : 0.f) : mvControlled->MotorOverloadRelayHighThreshold ? 1.f : 0.f);
// lights
ggLightsButton.PutValue(mvOccupied->LightsPos - 1);
auto const vehicleend{cab_to_end(Cab)};
if ((mvOccupied->iLights[vehicleend] & light::headlight_left) != 0)
{
ggLeftLightButton.PutValue(1.f);
}
if ((mvOccupied->iLights[vehicleend] & light::headlight_right) != 0)
{
ggRightLightButton.PutValue(1.f);
}
if ((mvOccupied->iLights[vehicleend] & light::headlight_upper) != 0)
{
ggUpperLightButton.PutValue(1.f);
}
if ((mvOccupied->iLights[vehicleend] & light::redmarker_left) != 0)
{
if (ggLeftEndLightButton.SubModel != nullptr)
{
ggLeftEndLightButton.PutValue(1.f);
}
else
{
ggLeftLightButton.PutValue(-1.f);
}
}
if ((mvOccupied->iLights[vehicleend] & light::redmarker_right) != 0)
{
if (ggRightEndLightButton.SubModel != nullptr)
{
ggRightEndLightButton.PutValue(1.f);
}
else
{
ggRightLightButton.PutValue(-1.f);
}
}
if (1 == DynamicObject->MoverParameters->modernDimmerPosition)
{
ggDimHeadlightsButton.PutValue(DynamicObject->MoverParameters->modernDimmerPosition);
}
// cab lights
if (true == Cabine[Cab].bLightDim)
{
ggCabLightDimButton.PutValue(1.f);
}
// compartment lights
ggCompartmentLightsButton.PutValue(ggCompartmentLightsButton.type() == TGaugeType::push ? 0.5f : mvOccupied->CompartmentLights.is_enabled ? 1.f : 0.f);
// instrument lights
ggInstrumentLightButton.PutValue(InstrumentLightActive ? 1.f : 0.f);
ggDashboardLightButton.PutValue(DashboardLightActive ? 1.f : 0.f);
ggTimetableLightButton.PutValue(TimetableLightActive ? 1.f : 0.f);
// doors permits
if (false == ggDoorLeftPermitButton.is_push())
{
ggDoorLeftPermitButton.PutValue(mvOccupied->Doors.instances[(cab_to_end() == end::front ? side::left : side::right)].open_permit ? 1.f : 0.f);
}
if (false == ggDoorRightPermitButton.is_push())
{
ggDoorRightPermitButton.PutValue(mvOccupied->Doors.instances[(cab_to_end() == end::front ? side::right : side::left)].open_permit ? 1.f : 0.f);
}
ggDoorPermitPresetButton.PutValue(mvOccupied->Doors.permit_preset);
// door controls
ggDoorLeftButton.PutValue(mvOccupied->Doors.instances[(cab_to_end() == end::front ? side::left : side::right)].is_closed ? 0.f : 1.f);
ggDoorRightButton.PutValue(mvOccupied->Doors.instances[(cab_to_end() == end::front ? side::right : side::left)].is_closed ? 0.f : 1.f);
// door lock
ggDoorSignallingButton.PutValue(mvOccupied->Doors.lock_enabled ? 1.f : 0.f);
// door step
if (false == ggDoorStepButton.is_push())
{
ggDoorStepButton.PutValue(mvOccupied->Doors.step_enabled ? 1.f : 0.f);
}
// heating
if (false == ggTrainHeatingButton.is_push())
{
ggTrainHeatingButton.PutValue(mvControlled->Heating ? 1.f : 0.f);
}
// brake acting time
if (ggBrakeProfileCtrl.SubModel != nullptr)
{
ggBrakeProfileCtrl.PutValue((mvOccupied->BrakeDelayFlag & bdelay_R) != 0 ? 2.f : mvOccupied->BrakeDelayFlag - 1);
}
if (ggBrakeProfileG.SubModel != nullptr)
{
ggBrakeProfileG.PutValue(mvOccupied->BrakeDelayFlag == bdelay_G ? 1.f : 0.f);
}
if (ggBrakeProfileR.SubModel != nullptr)
{
ggBrakeProfileR.PutValue((mvOccupied->BrakeDelayFlag & bdelay_R) != 0 ? 1.f : 0.f);
}
if (ggWiperSw.SubModel != nullptr)
{
ggWiperSw.PutValue(mvOccupied->wiperSwitchPos);
}
if (ggBrakeOperationModeCtrl.SubModel != nullptr)
{
ggBrakeOperationModeCtrl.PutValue(mvOccupied->BrakeOpModeFlag > 0 ? std::log2(mvOccupied->BrakeOpModeFlag) : 0);
}
// alarm chain
ggAlarmChain.PutValue(mvControlled->AlarmChainFlag ? 1.f : 0.f);
// brake signalling
ggSignallingButton.PutValue(mvControlled->Signalling ? 1.f : 0.f);
// multiple-unit current indicator source
ggNextCurrentButton.PutValue(ShowNextCurrent ? 1.f : 0.f);
// water pump
ggWaterPumpBreakerButton.PutValue(mvControlled->WaterPump.breaker ? 1.f : 0.f);
if (ggWaterPumpButton.type() != TGaugeType::push)
{
ggWaterPumpButton.PutValue(mvControlled->WaterPump.is_enabled ? 1.f : 0.f);
}
// water heater
ggWaterHeaterBreakerButton.PutValue(mvControlled->WaterHeater.breaker ? 1.f : 0.f);
ggWaterHeaterButton.PutValue(mvControlled->WaterHeater.is_enabled ? 1.f : 0.f);
ggWaterCircuitsLinkButton.PutValue(mvControlled->WaterCircuitsLink ? 1.f : 0.f);
// fuel pump
if (ggFuelPumpButton.type() != TGaugeType::push)
{
ggFuelPumpButton.PutValue(mvControlled->FuelPump.is_enabled ? 1.f : 0.f);
}
// oil pump
if (ggOilPumpButton.type() != TGaugeType::push)
{
ggOilPumpButton.PutValue(mvControlled->OilPump.is_enabled ? 1.f : 0.f);
}
// traction motor fans
if (ggMotorBlowersFrontButton.type() != TGaugeType::push)
{
ggMotorBlowersFrontButton.PutValue(mvControlled->MotorBlowers[end::front].is_enabled ? 1.f : 0.f);
}
if (ggMotorBlowersRearButton.type() != TGaugeType::push)
{
ggMotorBlowersRearButton.PutValue(mvControlled->MotorBlowers[end::rear].is_enabled ? 1.f : 0.f);
}
if (ggMotorBlowersAllOffButton.type() != TGaugeType::push)
{
ggMotorBlowersAllOffButton.PutValue(mvControlled->MotorBlowers[end::front].is_disabled || mvControlled->MotorBlowers[end::rear].is_disabled ? 1.f : 0.f);
}
// second controller
if (ggScndCtrl.is_push())
{
ggScndCtrl.PutValue(ggScndCtrl.is_toggle() ? 0.5f : // pushtoggle is two-way control with neutral position in the middle
0.f); // push is on/off control, active while held down, due to legacy use
}
// tempomat
if (false == ggScndCtrlButton.is_push())
{
ggScndCtrlButton.PutValue(mvControlled->ScndCtrlPos > 0 ? 1.f : 0.f);
}
// sandbox
if (ggAutoSandButton.type() != TGaugeType::push)
{
ggAutoSandButton.PutValue(mvControlled->SandDoseAutoAllow ? 1.f : 0.f);
}
// radio
ggRadioVolumeSelector.PutValue(m_radiovolume);
// finding each inverter - not so optimal, but action ins performed only during changing cabin
bool kier = DynamicObject->DirectionGet() * mvOccupied->CabOccupied > 0;
int flag = DynamicObject->MoverParameters->InverterControlCouplerFlag;
int itemstart = 0;
for (auto &item : ggInverterToggleButtons) // for each button
{
int itemindex = itemstart;
itemstart++;
TDynamicObject *p = DynamicObject->GetFirstDynamic(mvOccupied->CabOccupied < 0 ? end::rear : end::front, flag);
while (p)
{
if (p->MoverParameters->eimc[eimc_p_Pmax] > 1)
{
if (itemindex < p->MoverParameters->InvertersNo)
{
// visual feedback
ggInverterToggleButtons[itemstart - 1].PutValue(p->MoverParameters->Inverters[itemindex].Activate ? 1.0 : 0.0);
break;
}
else
{
itemindex -= p->MoverParameters->InvertersNo;
}
}
p = kier ? p->Next(flag) : p->Prev(flag);
}
}
// we reset all indicators, as they're set during the update pass
// TODO: when cleaning up break setting indicator state into a separate function, so we can reuse it
}
// initializes a button matching provided label. returns: true if the label was found, false otherwise
// TODO: refactor the cabin controls into some sensible structure
bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int const Cabindex)
{
std::unordered_map<std::string, TButton &> const lights = {
{"i-maxft:", btLampkaMaxSila},
{"i-maxftt:", btLampkaPrzekrMaxSila},
{"i-radio:", btLampkaRadio},
{"i-radiomessage:", btLampkaRadioMessage},
{"i-radiostop:", btLampkaRadioStop},
{"i-manual_brake:", btLampkaHamulecReczny},
{"i-door_blocked:", btLampkaBlokadaDrzwi},
{"i-door_blockedoff:", btLampkaDoorLockOff},
{"i-slippery:", btLampkaPoslizg},
{"i-contactors:", btLampkaStyczn},
{"i-conv_ovld:", btLampkaNadmPrzetw},
{"i-converter:", btLampkaPrzetw},
{"i-converteroff:", btLampkaPrzetwOff},
{"i-converterb:", btLampkaPrzetwB},
{"i-converterboff:", btLampkaPrzetwBOff},
{"i-diff_relay:", btLampkaPrzekRozn},
{"i-diff_relay2:", btLampkaPrzekRoznPom},
{"i-motor_ovld:", btLampkaNadmSil},
{"i-train_controll:", btLampkaUkrotnienie},
{"i-brake_delay_r:", btLampkaHamPosp},
{"i-mainbreaker:", btLampkaWylSzybki},
{"i-mainbreakerb:", btLampkaWylSzybkiB},
{"i-mainbreakeroff:", btLampkaWylSzybkiOff},
{"i-mainbreakerboff:", btLampkaWylSzybkiBOff},
{"i-mainbreakerready:", btLampkaMainBreakerReady},
{"i-mainbreakerblinking:", btLampkaMainBreakerBlinkingIfReady},
{"i-vent_ovld:", btLampkaNadmWent},
{"i-comp_ovld:", btLampkaNadmSpr},
// overheat indicator lamps
{"i-oil_overheat:", btLampkaOilOverheat},
{"i-water_overheat:", btLampkaWaterOverheat},
{"i-wateraux_overheat:", btLampkaWaterAuxOverheat},
{"i-engine_overheat:", btLampkaEngineOverheat},
{"i-resistors:", btLampkaOpory},
{"i-no_resistors:", btLampkaBezoporowa},
{"i-no_resistors_b:", btLampkaBezoporowaB},
{"i-highcurrent:", btLampkaWysRozr},
{"i-vent_trim:", btLampkaWentZaluzje},
{"i-motorblowers:", btLampkaMotorBlowers},
{"i-coolingfans:", btLampkaCoolingFans},
{"i-tempomat:", btLampkaTempomat},
{"i-distancecounter:", btLampkaDistanceCounter},
{"i-trainheating:", btLampkaOgrzewanieSkladu},
{"i-security_aware:", btLampkaCzuwaka},
{"i-security_cabsignal:", btLampkaSHP},
{"i-security_aware_cabsignal:", btLampkaCzuwakaSHP},
{"i-door_left:", btLampkaDoorLeft},
{"i-door_right:", btLampkaDoorRight},
{"i-departure_signal:", btLampkaDepartureSignal},
{"i-reserve:", btLampkaRezerwa},
{"i-scnd:", btLampkaBoczniki},
{"i-scnd1:", btLampkaBocznik1},
{"i-scnd2:", btLampkaBocznik2},
{"i-scnd3:", btLampkaBocznik3},
{"i-scnd4:", btLampkaBocznik4},
{"i-gear1:", btLampkaGear1},
{"i-gear2:", btLampkaGear2},
{"i-hydrolockup:", btLampkaHydroLockup},
{"i-braking:", btLampkaHamienie},
{"i-brakingoff:", btLampkaBrakingOff},
{"i-dynamicbrake:", btLampkaED},
{"i-brakeprofileg:", btLampkaBrakeProfileG},
{"i-brakeprofilep:", btLampkaBrakeProfileP},
{"i-brakeprofiler:", btLampkaBrakeProfileR},
{"i-springbrakeactive:", btLampkaSpringBrakeActive},
{"i-springbrakeinactive:", btLampkaSpringBrakeInactive},
{"i-braking-ezt:", btLampkaHamowanie1zes},
{"i-braking-ezt2:", btLampkaHamowanie2zes},
{"i-compressor:", btLampkaSprezarka},
{"i-compressorb:", btLampkaSprezarkaB},
{"i-compressoroff:", btLampkaSprezarkaOff},
{"i-compressorboff:", btLampkaSprezarkaBOff},
{"i-fuelpumpoff:", btLampkaFuelPumpOff},
{"i-voltbrake:", btLampkaNapNastHam},
{"i-resistorsb:", btLampkaOporyB},
{"i-contactorsb:", btLampkaStycznB},
{"i-conv_ovldb:", btLampkaNadmPrzetwB},
{"i-hvoltageb:", btLampkaHVoltageB},
{"i-malfunction:", btLampkaMalfunction},
{"i-malfunctionb:", btLampkaMalfunctionB},
{"i-forward:", btLampkaForward},
{"i-backward:", btLampkaBackward},
{"i-neutral:", btLampkaNeutral},
{"i-upperlight:", btLampkaUpperLight},
{"i-leftlight:", btLampkaLeftLight},
{"i-rightlight:", btLampkaRightLight},
{"i-leftend:", btLampkaLeftEndLight},
{"i-rightend:", btLampkaRightEndLight},
{"i-rearupperlight:", btLampkaRearUpperLight},
{"i-rearleftlight:", btLampkaRearLeftLight},
{"i-rearrightlight:", btLampkaRearRightLight},
{"i-rearleftend:", btLampkaRearLeftEndLight},
{"i-rearrightend:", btLampkaRearRightEndLight},
{"i-dashboardlight:", btDashboardLight},
{"i-timetablelight:", btTimetableLight},
{"i-universal0:", btUniversals[0]},
{"i-universal1:", btUniversals[1]},
{"i-universal2:", btUniversals[2]},
{"i-universal3:", btUniversals[3]},
{"i-universal4:", btUniversals[4]},
{"i-universal5:", btUniversals[5]},
{"i-universal6:", btUniversals[6]},
{"i-universal7:", btUniversals[7]},
{"i-universal8:", btUniversals[8]},
{"i-universal9:", btUniversals[9]},
{"i-cabactived:", btCabActived},
{"i-aklvents:", btAKLVents},
{"i-compressorany:", btCompressors},
{"i-edenabled", btEDenabled},
};
{
auto lookup = lights.find(Label);
if (lookup != lights.end())
{
lookup->second.Load(Parser, DynamicObject);
return true;
}
}
// TODO: move viable dedicated lights to the automatic light array
std::unordered_map<std::string, bool const *> const autolights = {
{"i-doors:", &m_doors},
{"i-doorpermit_left:", &m_doorspermitleft},
{"i-doorpermit_right:", &m_doorspermitright},
{"i-doorpermit_any:", &m_doorpermits},
{"i-doorstep:", &mvOccupied->Doors.step_enabled},
{"i-mainpipelock:", &mvOccupied->LockPipe},
{"i-battery:", &mvOccupied->Power24vIsAvailable},
{"i-cablight:", &Cabine[iCabn].bLight},
};
{
auto lookup = autolights.find(Label);
if (lookup != autolights.end())
{
auto &button = Cabine[Cabindex].Button(-1); // pierwsza wolna lampka
button.Load(Parser, DynamicObject);
button.AssignBool(lookup->second);
return true;
}
}
// custom lights
if (Label == "i-instrumentlight:")
{
btInstrumentLight.Load(Parser, DynamicObject);
InstrumentLightType = 0;
}
else if (Label == "i-instrumentlight_m:")
{
btInstrumentLight.Load(Parser, DynamicObject);
InstrumentLightType = 1;
}
else if (Label == "i-instrumentlight_c:")
{
btInstrumentLight.Load(Parser, DynamicObject);
InstrumentLightType = 2;
}
else if (Label == "i-instrumentlight_a:")
{
btInstrumentLight.Load(Parser, DynamicObject);
InstrumentLightType = 3;
}
else if (Label == "i-instrumentlight_l:")
{
btInstrumentLight.Load(Parser, DynamicObject);
InstrumentLightType = 4;
}
else if (Label == "i-doors:")
{
int i = Parser.getToken<int>() - 1;
auto &button = Cabine[Cabindex].Button(-1); // pierwsza wolna lampka
button.Load(Parser, DynamicObject);
button.AssignBool(bDoors[0] + 3 * i);
}
else
{
// failed to match the label
return false;
}
return true;
}
// initializes a gauge matching provided label. returns: true if the label was found, false otherwise
// TODO: refactor the cabin controls into some sensible structure
bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int const Cabindex)
{
std::unordered_map<std::string, TGauge &> const gauges = {{"jointctrl:", ggJointCtrl},
{"mainctrl:", ggMainCtrl},
{"scndctrl:", ggScndCtrl},
{"dirkey:", ggDirKey},
{"brakectrl:", ggBrakeCtrl},
{"localbrake:", ggLocalBrake},
{"alarmchain:", ggAlarmChain},
{"brakeprofile_sw:", ggBrakeProfileCtrl},
{"brakeprofileg_sw:", ggBrakeProfileG},
{"brakeprofiler_sw:", ggBrakeProfileR},
{"brakeopmode_sw:", ggBrakeOperationModeCtrl},
{"maxcurrent_sw:", ggMaxCurrentCtrl},
{"main_off_bt:", ggMainOffButton},
{"main_on_bt:", ggMainOnButton},
{"security_reset_bt:", ggSecurityResetButton},
{"shp_reset_bt:", ggSHPResetButton},
{"releaser_bt:", ggReleaserButton},
{"springbrakeon_bt:", ggSpringBrakeOnButton},
{"springbrakeoff_bt:", ggSpringBrakeOffButton},
{"universalbrake1_bt:", ggUniveralBrakeButton1},
{"universalbrake2_bt:", ggUniveralBrakeButton2},
{"universalbrake3_bt:", ggUniveralBrakeButton3},
{"epbrake_bt:", ggEPFuseButton},
{"sand_bt:", ggSandButton},
{"autosandallow_sw:", ggAutoSandButton},
{"antislip_bt:", ggAntiSlipButton},
{"horn_bt:", ggHornButton},
{"hornlow_bt:", ggHornLowButton},
{"hornhigh_bt:", ggHornHighButton},
{"whistle_bt:", ggWhistleButton},
{"helper_bt:", ggHelperButton},
{"fuse_bt:", ggFuseButton},
{"converterfuse_bt:", ggConverterFuseButton},
{"stlinoff_bt:", ggStLinOffButton},
{"doorpermitpreset_sw:", ggDoorPermitPresetButton},
{"door_left_sw:", ggDoorLeftButton},
{"door_right_sw:", ggDoorRightButton},
{"doorlefton_sw:", ggDoorLeftOnButton},
{"doorrighton_sw:", ggDoorRightOnButton},
{"doorleftoff_sw:", ggDoorLeftOffButton},
{"doorrightoff_sw:", ggDoorRightOffButton},
{"doorallon_sw:", ggDoorAllOnButton},
{"departure_signal_bt:", ggDepartureSignalButton},
{"upperlight_sw:", ggUpperLightButton},
{"leftlight_sw:", ggLeftLightButton},
{"rightlight_sw:", ggRightLightButton},
{"dimheadlights_sw:", ggDimHeadlightsButton},
{"leftend_sw:", ggLeftEndLightButton},
{"rightend_sw:", ggRightEndLightButton},
{"lights_sw:", ggLightsButton},
{"moderndimmer_sw:", ggModernLightDimSw},
{"rearupperlight_sw:", ggRearUpperLightButton},
{"rearleftlight_sw:", ggRearLeftLightButton},
{"rearrightlight_sw:", ggRearRightLightButton},
{"rearleftend_sw:", ggRearLeftEndLightButton},
{"rearrightend_sw:", ggRearRightEndLightButton},
{"compressor_sw:", ggCompressorButton},
{"compressorlocal_sw:", ggCompressorLocalButton},
{"compressorlist_sw:", ggCompressorListButton},
{"converter_sw:", ggConverterButton},
{"converterlocal_sw:", ggConverterLocalButton},
{"converteroff_sw:", ggConverterOffButton},
{"main_sw:", ggMainButton},
{"waterpumpbreaker_sw:", ggWaterPumpBreakerButton},
{"waterpump_sw:", ggWaterPumpButton},
{"waterheaterbreaker_sw:", ggWaterHeaterBreakerButton},
{"waterheater_sw:", ggWaterHeaterButton},
{"water1tempb:", ggWater1TempB},
{"watercircuitslink_sw:", ggWaterCircuitsLinkButton},
{"fuelpump_sw:", ggFuelPumpButton},
{"oilpump_sw:", ggOilPumpButton},
{"oilpressb:", ggOilPressB},
{"motorblowersfront_sw:", ggMotorBlowersFrontButton},
{"motorblowersrear_sw:", ggMotorBlowersRearButton},
{"motorblowersalloff_sw:", ggMotorBlowersAllOffButton},
{"radiochannel_sw:", ggRadioChannelSelector},
{"radiochannelprev_sw:", ggRadioChannelPrevious},
{"radiochannelnext_sw:", ggRadioChannelNext},
{"radiostop_sw:", ggRadioStop},
{"radiotest_sw:", ggRadioTest},
{"radiocall1_sw:", ggRadioCall1},
{"radiocall3_sw:", ggRadioCall3},
{"radiovolume_sw:", ggRadioVolumeSelector},
{"radiovolumeprev_sw:", ggRadioVolumePrevious},
{"radiovolumenext_sw:", ggRadioVolumeNext},
/*
{ "pantfront_sw:", ggPantFrontButton },
{ "pantrear_sw:", ggPantRearButton },
{ "pantfrontoff_sw:", ggPantFrontButtonOff },
{ "pantrearoff_sw:", ggPantRearButtonOff },
*/
{"pantalloff_sw:", ggPantAllDownButton},
{"pantselected_sw:", ggPantSelectedButton},
{"pantselectedoff_sw:", ggPantSelectedDownButton},
{"pantvalves_sw:", ggPantValvesButton},
{"pantcompressor_sw:", ggPantCompressorButton},
{"pantcompressorvalve_sw:", ggPantCompressorValve},
{"trainheating_sw:", ggTrainHeatingButton},
{"signalling_sw:", ggSignallingButton},
{"door_signalling_sw:", ggDoorSignallingButton},
{"nextcurrent_sw:", ggNextCurrentButton},
{"instrumentlight_sw:", ggInstrumentLightButton},
{"dashboardlight_sw:", ggDashboardLightButton},
{"timetablelight_sw:", ggTimetableLightButton},
{"cablightdim_sw:", ggCabLightDimButton},
{"compartmentlights_sw:", ggCompartmentLightsButton},
{"compartmentlightson_sw:", ggCompartmentLightsOnButton},
{"compartmentlightsoff_sw:", ggCompartmentLightsOffButton},
{"battery_sw:", ggBatteryButton},
{"batteryon_sw:", ggBatteryOnButton},
{"batteryoff_sw:", ggBatteryOffButton},
{"cabactivation_sw:", ggCabActivationButton},
{"distancecounter_sw:", ggDistanceCounterButton},
{"relayreset1_bt:", ggRelayResetButtons[0]},
{"relayreset2_bt:", ggRelayResetButtons[1]},
{"relayreset3_bt:", ggRelayResetButtons[2]},
{"universal0:", ggUniversals[0]},
{"universal1:", ggUniversals[1]},
{"universal2:", ggUniversals[2]},
{"universal3:", ggUniversals[3]},
{"universal4:", ggUniversals[4]},
{"universal5:", ggUniversals[5]},
{"universal6:", ggUniversals[6]},
{"universal7:", ggUniversals[7]},
{"universal8:", ggUniversals[8]},
{"universal9:", ggUniversals[9]},
{"universal10:", ggUniversals[10]},
{"universal11:", ggUniversals[11]},
{"universal12:", ggUniversals[12]},
{"universal13:", ggUniversals[13]},
{"universal14:", ggUniversals[14]},
{"universal15:", ggUniversals[15]},
{"universal16:", ggUniversals[16]},
{"universal17:", ggUniversals[17]},
{"universal18:", ggUniversals[18]},
{"universal19:", ggUniversals[19]},
{"universal20:", ggUniversals[20]},
{"universal21:", ggUniversals[21]},
{"universal22:", ggUniversals[22]},
{"universal23:", ggUniversals[23]},
{"universal24:", ggUniversals[24]},
{"universal25:", ggUniversals[25]},
{"universal26:", ggUniversals[26]},
{"universal27:", ggUniversals[27]},
{"universal28:", ggUniversals[28]},
{"universal29:", ggUniversals[29]},
{"inverterenable1_bt:", ggInverterEnableButtons[0]},
{"inverterenable2_bt:", ggInverterEnableButtons[1]},
{"inverterenable3_bt:", ggInverterEnableButtons[2]},
{"inverterenable4_bt:", ggInverterEnableButtons[3]},
{"inverterenable5_bt:", ggInverterEnableButtons[4]},
{"inverterenable6_bt:", ggInverterEnableButtons[5]},
{"inverterenable7_bt:", ggInverterEnableButtons[6]},
{"inverterenable8_bt:", ggInverterEnableButtons[7]},
{"inverterenable9_bt:", ggInverterEnableButtons[8]},
{"inverterenable10_bt:", ggInverterEnableButtons[9]},
{"inverterenable11_bt:", ggInverterEnableButtons[10]},
{"inverterenable12_bt:", ggInverterEnableButtons[11]},
{"inverterdisable1_bt:", ggInverterDisableButtons[0]},
{"inverterdisable2_bt:", ggInverterDisableButtons[1]},
{"inverterdisable3_bt:", ggInverterDisableButtons[2]},
{"inverterdisable4_bt:", ggInverterDisableButtons[3]},
{"inverterdisable5_bt:", ggInverterDisableButtons[4]},
{"inverterdisable6_bt:", ggInverterDisableButtons[5]},
{"inverterdisable7_bt:", ggInverterDisableButtons[6]},
{"inverterdisable8_bt:", ggInverterDisableButtons[7]},
{"inverterdisable9_bt:", ggInverterDisableButtons[8]},
{"inverterdisable10_bt:", ggInverterDisableButtons[9]},
{"inverterdisable11_bt:", ggInverterDisableButtons[10]},
{"inverterdisable12_bt:", ggInverterDisableButtons[11]},
{"invertertoggle1_bt:", ggInverterToggleButtons[0]},
{"invertertoggle2_bt:", ggInverterToggleButtons[1]},
{"invertertoggle3_bt:", ggInverterToggleButtons[2]},
{"invertertoggle4_bt:", ggInverterToggleButtons[3]},
{"invertertoggle5_bt:", ggInverterToggleButtons[4]},
{"invertertoggle6_bt:", ggInverterToggleButtons[5]},
{"invertertoggle7_bt:", ggInverterToggleButtons[6]},
{"invertertoggle8_bt:", ggInverterToggleButtons[7]},
{"invertertoggle9_bt:", ggInverterToggleButtons[8]},
{"invertertoggle10_bt:", ggInverterToggleButtons[9]},
{"invertertoggle11_bt:", ggInverterToggleButtons[10]},
{"invertertoggle12_bt:", ggInverterToggleButtons[11]},
{"pantvalvesupdate_bt:", ggPantValvesUpdate},
{"pantvalvesoff_bt:", ggPantValvesOff},
{"wipers_sw:", ggWiperSw}};
{
auto const lookup{gauges.find(Label)};
if (lookup != gauges.end())
{
lookup->second.Load(Parser, DynamicObject);
m_controlmapper.insert(lookup->second, lookup->first);
return true;
}
}
// dedicated gauges with state-driven optional submodel
// TODO: move viable gauges here
// TODO: convert dedicated gauges to auto-allocated ones, replace dedicated references in command handlers to mapper lookups
std::unordered_map<std::string, std::tuple<TGauge &, bool const *>> const stategauges = {
{"tempomat_sw:", {ggScndCtrlButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
{"tempomatoff_sw:", {ggScndCtrlOffButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedinc_bt:", {ggSpeedControlIncreaseButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speeddec_bt:", {ggSpeedControlDecreaseButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedctrlpowerinc_bt:", {ggSpeedControlPowerIncreaseButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedctrlpowerdec_bt:", {ggSpeedControlPowerDecreaseButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedbutton0:", {ggSpeedCtrlButtons[0], &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedbutton1:", {ggSpeedCtrlButtons[1], &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedbutton2:", {ggSpeedCtrlButtons[2], &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedbutton3:", {ggSpeedCtrlButtons[3], &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedbutton4:", {ggSpeedCtrlButtons[4], &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedbutton5:", {ggSpeedCtrlButtons[5], &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedbutton6:", {ggSpeedCtrlButtons[6], &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedbutton7:", {ggSpeedCtrlButtons[7], &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedbutton8:", {ggSpeedCtrlButtons[8], &mvOccupied->SpeedCtrlUnit.IsActive}},
{"speedbutton9:", {ggSpeedCtrlButtons[9], &mvOccupied->SpeedCtrlUnit.IsActive}},
{"doorleftpermit_sw:", {ggDoorLeftPermitButton, &m_doorspermitleft}},
{"doorrightpermit_sw:", {ggDoorRightPermitButton, &m_doorspermitright}},
{"dooralloff_sw:", {ggDoorAllOffButton, &m_doors}},
{"doorstep_sw:", {ggDoorStepButton, &mvOccupied->Doors.step_enabled}},
{"dirforward_bt:", {ggDirForwardButton, &m_dirforward}},
{"dirneutral_bt:", {ggDirNeutralButton, &m_dirneutral}},
{"dirbackward_bt:", {ggDirBackwardButton, &m_dirbackward}},
};
{
auto const lookup{stategauges.find(Label)};
if (lookup != stategauges.end())
{
auto &gauge{std::get<TGauge &>(lookup->second)};
gauge.Load(Parser, DynamicObject);
gauge.AssignState(std::get<bool const *>(lookup->second));
m_controlmapper.insert(gauge, lookup->first);
return true;
}
}
// TODO: move viable dedicated gauges to the automatic array
std::unordered_map<std::string, bool *> const autoboolgauges = {
{"doormode_sw:", &mvOccupied->Doors.remote_only},
{"coolingfans_sw:", &mvControlled->RVentForceOn},
{"pantfront_sw:", &mvPantographUnit->Pantographs[end::front].valve.is_enabled},
{"pantrear_sw:", &mvPantographUnit->Pantographs[end::rear].valve.is_enabled},
{"pantfrontoff_sw:", &mvPantographUnit->Pantographs[end::front].valve.is_disabled},
{"pantrearoff_sw:", &mvPantographUnit->Pantographs[end::rear].valve.is_disabled},
{"radio_sw:", &mvOccupied->Radio},
{"cablight_sw:", &Cabine[iCabn].bLight},
{"springbraketoggle_bt:", &mvOccupied->SpringBrake.Activate},
{"couplingdisconnect_sw:", &m_couplingdisconnect},
{"couplingdisconnectback_sw:", &m_couplingdisconnectback},
{"mirrors_sw:", &mvOccupied->MirrorForbidden},
};
{
auto lookup = autoboolgauges.find(Label);
if (lookup != autoboolgauges.end())
{
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna lampka
gauge.Load(Parser, DynamicObject);
gauge.AssignBool(lookup->second);
m_controlmapper.insert(gauge, lookup->first);
return true;
}
}
// TODO: move viable dedicated gauges to the automatic array
std::unordered_map<std::string, int *> const autointgauges = {
{"manualbrake:", &mvOccupied->ManualBrakePos},
{"pantselect_sw:", &mvOccupied->PantsPreset.second[cab_to_end()]},
};
{
auto lookup = autointgauges.find(Label);
if (lookup != autointgauges.end())
{
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna lampka
gauge.Load(Parser, DynamicObject);
gauge.AssignInt(lookup->second);
m_controlmapper.insert(gauge, lookup->first);
return true;
}
}
// ABu 090305: uniwersalne przyciski lub inne rzeczy
if (Label == "mainctrlact:")
{
ggMainCtrlAct.Load(Parser, DynamicObject);
}
// SEKCJA WSKAZNIKOW
else if (Label == "tachometer:" || Label == "tachometerb:")
{
// predkosciomierz wskaźnikowy z szarpaniem
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(&fTachoVelocityJump);
// bind tachometer sound location to the meter
if (dsbHasler && dsbHasler->offset() == glm::vec3())
{
dsbHasler->offset(gauge.model_offset());
}
}
else if (Label == "tachometern:")
{
// predkosciomierz wskaźnikowy bez szarpania
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(&fTachoVelocity);
// bind tachometer sound location to the meter
if (dsbHasler && dsbHasler->offset() == glm::vec3())
{
dsbHasler->offset(gauge.model_offset());
}
}
else if (Label == "tachometerd:")
{
// predkosciomierz cyfrowy
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(&fTachoVelocity);
// bind tachometer sound location to the meter
if (dsbHasler && dsbHasler->offset() == glm::vec3())
{
dsbHasler->offset(gauge.model_offset());
}
}
else if (Label == "hvcurrent1:" || Label == "hvcurrent1b:")
{
// 1szy amperomierz
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(fHCurrent + 1);
}
else if (Label == "hvcurrent2:" || Label == "hvcurrent2b:")
{
// 2gi amperomierz
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(fHCurrent + 2);
}
else if (Label == "hvcurrent3:" || Label == "hvcurrent3b:")
{
// 3ci amperomierz
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałska
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(fHCurrent + 3);
}
else if (Label == "hvcurrent:" || Label == "hvcurrentb:")
{
// amperomierz calkowitego pradu
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(fHCurrent);
}
else if (Label == "eimscreen:")
{
// amperomierz calkowitego pradu
int i, j;
Parser.getTokens(2, false);
Parser >> i >> j;
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(&fEIMParams[i][j]);
}
else if (Label == "brakes:")
{
// specified pipe pressure of specified consist vehicle
int i, j;
Parser.getTokens(2, false);
Parser >> i >> j;
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject, 0.1);
gauge.AssignFloat(&fPress[std::clamp(i, 1, 20) - 1][std::clamp(j, 0, 3)]);
}
else if (Label == "brakepress:" || Label == "brakepressb:")
{
// manometr cylindrow hamulcowych
// Ra 2014-08: przeniesione do TCab
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject, 0.1);
gauge.AssignDouble(&mvOccupied->BrakePress);
}
else if (Label == "pipepress:" || Label == "pipepressb:")
{
// manometr przewodu hamulcowego
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject, 0.1);
gauge.AssignDouble(&mvOccupied->PipePress);
}
else if (Label == "scndpress:")
{
// manometr przewodu hamulcowego
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject, 0.1);
gauge.AssignDouble(&mvOccupied->ScndPipePress);
}
else if (Label == "limpipepress:")
{
// manometr zbiornika sterujacego zaworu maszynisty
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject, 0.1);
gauge.AssignDouble(&m_brakehandlecp);
}
else if (Label == "cntrlpress:")
{
// manometr zbiornika kontrolnego/rorz�du
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject, 0.1);
gauge.AssignDouble(&mvPantographUnit->PantPress);
}
else if (Label == "springbrakepress:")
{
// manometr cylindra hamulca sprężynowego
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject, 0.1);
gauge.AssignDouble(&mvOccupied->SpringBrake.SBP);
}
else if (Label == "epctrlvalue:")
{
// wskazowka sterowania sila hamulca ep
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject, 0.1);
gauge.AssignDouble(&mvOccupied->EpForce);
}
else if (Label == "compressor:" || Label == "compressorb:")
{
// manometr sprezarki/zbiornika glownego
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject, 0.1);
gauge.AssignDouble(&mvOccupied->Compressor);
}
else if (Label == "oilpress:")
{
// oil pressure
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(&mvControlled->OilPump.pressure);
}
else if (Label == "oiltemp:")
{
// oil temperature
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(&mvControlled->dizel_heat.To);
}
else if (Label == "water1temp:")
{
// main circuit water temperature
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(&mvControlled->dizel_heat.temperatura1);
}
else if (Label == "water2temp:")
{
// auxiliary circuit water temperature
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(&mvControlled->dizel_heat.temperatura2);
}
else if (Label == "pantpress:")
{
// pantograph tank pressure
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject, 0.1);
gauge.AssignDouble(&mvPantographUnit->PantPress);
}
// yB - dla drugiej sekcji
else if (Label == "hvbcurrent1:")
{
// 1szy amperomierz
ggI1B.Load(Parser, DynamicObject);
}
else if (Label == "hvbcurrent2:")
{
// 2gi amperomierz
ggI2B.Load(Parser, DynamicObject);
}
else if (Label == "hvbcurrent3:")
{
// 3ci amperomierz
ggI3B.Load(Parser, DynamicObject);
}
else if (Label == "hvbcurrent:")
{
// amperomierz calkowitego pradu
ggItotalB.Load(Parser, DynamicObject);
}
//*************************************************************
else if (Label == "clock:")
{
// zegar analogowy
if (Parser.getToken<std::string>() == "analog")
{
if (DynamicObject->mdKabina)
{
// McZapkie-300302: zegarek
ggClockSInd.Init(DynamicObject->mdKabina->GetFromName("ClockShand"), nullptr, TGaugeAnimation::gt_Rotate, 1.0 / 60.0);
ggClockMInd.Init(DynamicObject->mdKabina->GetFromName("ClockMhand"), nullptr, TGaugeAnimation::gt_Rotate, 1.0 / 60.0);
ggClockHInd.Init(DynamicObject->mdKabina->GetFromName("ClockHhand"), nullptr, TGaugeAnimation::gt_Rotate, 1.0 / 12.0);
}
}
}
else if (Label == "clock_seconds:")
{
ggClockSInd.Load(Parser, DynamicObject);
}
else if (Label == "evoltage:")
{
// woltomierz napiecia silnikow
ggEngineVoltage.Load(Parser, DynamicObject);
}
else if (Label == "hvoltage:")
{
// woltomierz wysokiego napiecia
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(&fHVoltage);
}
else if (Label == "lvoltage:")
{
// woltomierz niskiego napiecia
ggLVoltage.Load(Parser, DynamicObject);
}
else if (Label == "enrot1m:")
{
// obrotomierz
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(fEngine + 1);
} // ggEnrot1m.Load(Parser,DynamicObject->mdKabina);
else if (Label == "enrot2m:")
{
// obrotomierz
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(fEngine + 2);
} // ggEnrot2m.Load(Parser,DynamicObject->mdKabina);
else if (Label == "enrot3m:")
{ // obrotomierz
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignFloat(fEngine + 3);
} // ggEnrot3m.Load(Parser,DynamicObject->mdKabina);
else if (Label == "engageratio:")
{
// np. ciśnienie sterownika sprzęgła
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignDouble(&mvControlled->dizel_engage);
} // ggEngageRatio.Load(Parser,DynamicObject->mdKabina);
else if (Label == "maingearstatus:")
{
// np. ciśnienie sterownika skrzyni biegów
ggMainGearStatus.Load(Parser, DynamicObject);
}
else if (Label == "ignitionkey:")
{
ggIgnitionKey.Load(Parser, DynamicObject);
}
else if (Label == "distcounter:")
{
// Ra 2014-07: licznik kilometrów
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignDouble(&mvControlled->DistCounter);
}
else if (Label == "shuntmodepower:")
{
// shunt mode power slider
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignDouble(&mvControlled->AnPos);
m_controlmapper.insert(gauge, "shuntmodepower:");
}
else if (Label == "heatingvoltage:")
{
if (mvControlled->HeatingPowerSource.SourceType == TPowerSource::Generator)
{
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignDouble(&mvControlled->HeatingPowerSource.EngineGenerator.voltage);
}
}
else if (Label == "heatingcurrent:")
{
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
gauge.Load(Parser, DynamicObject);
gauge.AssignDouble(&mvControlled->TotalCurrent);
}
else
{
// failed to match the label
return false;
}
return true;
}