/* 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 #include #include // 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 &, 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 &>(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(), 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(soundconfig), std::get(soundconfig)}; sound.deserialize(parser, std::get(soundconfig), std::get(soundconfig), std::get(soundconfig)); sound.owner(DynamicObject); std::get &>(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>> 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>> 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(), 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(); 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>(); if (submodel) submodel->screen_touch_list = m_screens.back().touch_list; if (Global.python_displaywindows) m_screens.back().viewer = std::make_unique(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>, 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 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 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() - 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 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> 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(lookup->second)}; gauge.Load(Parser, DynamicObject); gauge.AssignState(std::get(lookup->second)); m_controlmapper.insert(gauge, lookup->first); return true; } } // TODO: move viable dedicated gauges to the automatic array std::unordered_map 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 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() == "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; }