/* 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/. */ /* MaSzyna EU07 locomotive simulator Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others */ #include "stdafx.h" #include "vehicle/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 "world/mtable.h" #include "Console.h" #include "application/application.h" #include "rendering/renderer.h" #include #include #include /* namespace input { extern user_command command; } */ void control_mapper::clear() { *this = control_mapper(); } void control_mapper::insert(TGauge const &Gauge, std::string const &Label) { if (Gauge.SubModel != nullptr) { m_controlnames.emplace(Gauge.SubModel, Label); } if (Gauge.SubModelOn != nullptr) { m_controlnames.emplace(Gauge.SubModelOn, Label); } m_names.emplace(Label); } std::string control_mapper::find(TSubModel const *Control) const { auto const lookup = m_controlnames.find(Control); if (lookup != m_controlnames.end()) { return lookup->second; } else { return ""; } } bool control_mapper::contains(std::string const Control) const { return (m_names.find(Control) != m_names.end()); } void TTrain::screen_entry::deserialize(cParser &Input) { while (true == deserialize_mapping(Input)) { ; // all work done by while() } } bool TTrain::screen_entry::deserialize_mapping(cParser &Input) { // token can be a key or block end auto const key{Input.getToken(true, "\n\r\t ,;[]")}; if ((true == key.empty()) || (key == "}")) { return false; } if (key == "{") { script = Input.getToken(); } else if (key == "target:") { target = Input.getToken(); } else if (key == "updatetime:") { updatetime = Input.getToken(); } else if (key == "parameters:") { parameters = dictionary_source(Input.getToken()); } else { // HACK: we expect this to be true only if the screen entry doesn't start with a { which means legacy configuration format target = key; script = Input.getToken(); return false; } return true; } void TCab::Load(cParser &Parser) { // NOTE: clearing control tables here is bit of a crutch, imposed by current scheme of loading compartments anew on each cab change ggList.clear(); btList.clear(); std::string token; Parser.getTokens(); Parser >> token; if (token == "cablight:") { Parser.getTokens(9, false); /* Parser >> dimm.r >> dimm.g >> dimm.b >> intlit.r >> intlit.g >> intlit.b >> intlitlow.r >> intlitlow.g >> intlitlow.b; */ Parser.getTokens(); Parser >> token; } CabPos1.x = std::stod(token); Parser.getTokens(5, false); Parser >> CabPos1.y >> CabPos1.z >> CabPos2.x >> CabPos2.y >> CabPos2.z; bEnabled = true; bOccupied = true; } TGauge &TCab::Gauge(int n) { // pobranie adresu obiektu aniomowanego ruchem /* if (n < 0) { // rezerwacja wolnego ggList[iGauges].Clear(); return ggList + iGauges++; } else if (n < iGauges) return ggList + n; return NULL; */ if (n < 0) { ggList.emplace_back(); return ggList.back(); } else { return ggList[n]; } }; TButton &TCab::Button(int n) { // pobranie adresu obiektu animowanego wyborem 1 z 2 /* if (n < 0) { // rezerwacja wolnego return btList + iButtons++; } else if (n < iButtons) return btList + n; return NULL; */ if (n < 0) { btList.emplace_back(); return btList.back(); } else { return btList[n]; } }; void TCab::Update(bool const Power) { // odczyt parametrów i ustawienie animacji submodelom for (auto &gauge : ggList) { // animacje izometryczne gauge.UpdateValue(); // odczyt parametru i przeliczenie na kąt gauge.Update(); // ustawienie animacji } for (auto &button : btList) { // animacje dwustanowe button.Update(Power); // odczyt parametru i wybór submodelu } }; // NOTE: we're currently using universal handlers and static handler map but it may be beneficial to have these implemented on individual class instance basis // TBD, TODO: consider this approach if we ever want to have customized consist behaviour to received commands, based on the consist/vehicle type or whatever TTrain::commandhandler_map const TTrain::m_commandhandlers = { {user_command::aidriverenable, &TTrain::OnCommand_aidriverenable}, {user_command::aidriverdisable, &TTrain::OnCommand_aidriverdisable}, {user_command::jointcontrollerset, &TTrain::OnCommand_jointcontrollerset}, {user_command::mastercontrollerincrease, &TTrain::OnCommand_mastercontrollerincrease}, {user_command::mastercontrollerincreasefast, &TTrain::OnCommand_mastercontrollerincreasefast}, {user_command::mastercontrollerdecrease, &TTrain::OnCommand_mastercontrollerdecrease}, {user_command::mastercontrollerdecreasefast, &TTrain::OnCommand_mastercontrollerdecreasefast}, {user_command::mastercontrollerset, &TTrain::OnCommand_mastercontrollerset}, {user_command::secondcontrollerincrease, &TTrain::OnCommand_secondcontrollerincrease}, {user_command::secondcontrollerincreasefast, &TTrain::OnCommand_secondcontrollerincreasefast}, {user_command::secondcontrollerdecrease, &TTrain::OnCommand_secondcontrollerdecrease}, {user_command::secondcontrollerdecreasefast, &TTrain::OnCommand_secondcontrollerdecreasefast}, {user_command::secondcontrollerset, &TTrain::OnCommand_secondcontrollerset}, {user_command::dynamicbrakecontrollerincrease, &TTrain::OnCommand_DynamicBrakeControllerIncrease}, {user_command::dynamicbrakecontrollerincreasefast, &TTrain::OnCommand_DynamicBrakeControllerIncreaseFast}, {user_command::dynamicbrakecontrollerdecrease, &TTrain::OnCommand_DynamicBrakeControllerDecrease}, {user_command::dynamicbrakecontrollerdecreasefast, &TTrain::OnCommand_DynamicBrakeControllerDecreaseFast}, {user_command::dynamicbrakecontrollerset, &TTrain::OnCommand_DynamicBrakeControllerSet}, {user_command::notchingrelaytoggle, &TTrain::OnCommand_notchingrelaytoggle}, {user_command::tempomattoggle, &TTrain::OnCommand_tempomattoggle}, {user_command::mucurrentindicatorothersourceactivate, &TTrain::OnCommand_mucurrentindicatorothersourceactivate}, {user_command::independentbrakeincrease, &TTrain::OnCommand_independentbrakeincrease}, {user_command::independentbrakeincreasefast, &TTrain::OnCommand_independentbrakeincreasefast}, {user_command::independentbrakedecrease, &TTrain::OnCommand_independentbrakedecrease}, {user_command::independentbrakedecreasefast, &TTrain::OnCommand_independentbrakedecreasefast}, {user_command::independentbrakeset, &TTrain::OnCommand_independentbrakeset}, {user_command::independentbrakebailoff, &TTrain::OnCommand_independentbrakebailoff}, {user_command::universalbrakebutton1, &TTrain::OnCommand_universalbrakebutton1}, {user_command::universalbrakebutton2, &TTrain::OnCommand_universalbrakebutton2}, {user_command::universalbrakebutton3, &TTrain::OnCommand_universalbrakebutton3}, {user_command::trainbrakeincrease, &TTrain::OnCommand_trainbrakeincrease}, {user_command::trainbrakedecrease, &TTrain::OnCommand_trainbrakedecrease}, {user_command::trainbrakeset, &TTrain::OnCommand_trainbrakeset}, {user_command::trainbrakecharging, &TTrain::OnCommand_trainbrakecharging}, {user_command::trainbrakerelease, &TTrain::OnCommand_trainbrakerelease}, {user_command::trainbrakefirstservice, &TTrain::OnCommand_trainbrakefirstservice}, {user_command::trainbrakeservice, &TTrain::OnCommand_trainbrakeservice}, {user_command::trainbrakefullservice, &TTrain::OnCommand_trainbrakefullservice}, {user_command::trainbrakehandleoff, &TTrain::OnCommand_trainbrakehandleoff}, {user_command::trainbrakeemergency, &TTrain::OnCommand_trainbrakeemergency}, {user_command::trainbrakebasepressureincrease, &TTrain::OnCommand_trainbrakebasepressureincrease}, {user_command::trainbrakebasepressuredecrease, &TTrain::OnCommand_trainbrakebasepressuredecrease}, {user_command::trainbrakebasepressurereset, &TTrain::OnCommand_trainbrakebasepressurereset}, {user_command::trainbrakeoperationtoggle, &TTrain::OnCommand_trainbrakeoperationtoggle}, {user_command::manualbrakeincrease, &TTrain::OnCommand_manualbrakeincrease}, {user_command::manualbrakedecrease, &TTrain::OnCommand_manualbrakedecrease}, {user_command::alarmchaintoggle, &TTrain::OnCommand_alarmchaintoggle}, {user_command::alarmchainenable, &TTrain::OnCommand_alarmchainenable}, {user_command::alarmchaindisable, &TTrain::OnCommand_alarmchaindisable}, {user_command::wheelspinbrakeactivate, &TTrain::OnCommand_wheelspinbrakeactivate}, {user_command::sandboxactivate, &TTrain::OnCommand_sandboxactivate}, {user_command::autosandboxtoggle, &TTrain::OnCommand_autosandboxtoggle}, {user_command::autosandboxactivate, &TTrain::OnCommand_autosandboxactivate}, {user_command::autosandboxdeactivate, &TTrain::OnCommand_autosandboxdeactivate}, {user_command::epbrakecontroltoggle, &TTrain::OnCommand_epbrakecontroltoggle}, {user_command::epbrakecontrolenable, &TTrain::OnCommand_epbrakecontrolenable}, {user_command::epbrakecontroldisable, &TTrain::OnCommand_epbrakecontroldisable}, {user_command::trainbrakeoperationmodeincrease, &TTrain::OnCommand_trainbrakeoperationmodeincrease}, {user_command::trainbrakeoperationmodedecrease, &TTrain::OnCommand_trainbrakeoperationmodedecrease}, {user_command::brakeactingspeedincrease, &TTrain::OnCommand_brakeactingspeedincrease}, {user_command::brakeactingspeeddecrease, &TTrain::OnCommand_brakeactingspeeddecrease}, {user_command::brakeactingspeedsetcargo, &TTrain::OnCommand_brakeactingspeedsetcargo}, {user_command::brakeactingspeedsetpassenger, &TTrain::OnCommand_brakeactingspeedsetpassenger}, {user_command::brakeactingspeedsetrapid, &TTrain::OnCommand_brakeactingspeedsetrapid}, {user_command::brakeloadcompensationincrease, &TTrain::OnCommand_brakeloadcompensationincrease}, {user_command::brakeloadcompensationdecrease, &TTrain::OnCommand_brakeloadcompensationdecrease}, {user_command::mubrakingindicatortoggle, &TTrain::OnCommand_mubrakingindicatortoggle}, {user_command::reverserincrease, &TTrain::OnCommand_reverserincrease}, {user_command::reverserdecrease, &TTrain::OnCommand_reverserdecrease}, {user_command::reverserforwardhigh, &TTrain::OnCommand_reverserforwardhigh}, {user_command::reverserforward, &TTrain::OnCommand_reverserforward}, {user_command::reverserneutral, &TTrain::OnCommand_reverserneutral}, {user_command::reverserbackward, &TTrain::OnCommand_reverserbackward}, {user_command::alerteracknowledge, &TTrain::OnCommand_alerteracknowledge}, {user_command::cabsignalacknowledge, &TTrain::OnCommand_cabsignalacknowledge}, {user_command::batterytoggle, &TTrain::OnCommand_batterytoggle}, {user_command::batteryenable, &TTrain::OnCommand_batteryenable}, {user_command::batterydisable, &TTrain::OnCommand_batterydisable}, {user_command::cabactivationtoggle, &TTrain::OnCommand_cabactivationtoggle}, {user_command::cabactivationenable, &TTrain::OnCommand_cabactivationenable}, {user_command::cabactivationdisable, &TTrain::OnCommand_cabactivationdisable}, {user_command::pantographcompressorvalvetoggle, &TTrain::OnCommand_pantographcompressorvalvetoggle}, {user_command::pantographcompressorvalveenable, &TTrain::OnCommand_pantographcompressorvalveenable}, {user_command::pantographcompressorvalvedisable, &TTrain::OnCommand_pantographcompressorvalvedisable}, {user_command::pantographcompressoractivate, &TTrain::OnCommand_pantographcompressoractivate}, {user_command::pantographtogglefront, &TTrain::OnCommand_pantographtogglefront}, {user_command::pantographtogglerear, &TTrain::OnCommand_pantographtogglerear}, {user_command::pantographraisefront, &TTrain::OnCommand_pantographraisefront}, {user_command::pantographraiserear, &TTrain::OnCommand_pantographraiserear}, {user_command::pantographlowerfront, &TTrain::OnCommand_pantographlowerfront}, {user_command::pantographlowerrear, &TTrain::OnCommand_pantographlowerrear}, {user_command::wiperswitchincrease, &TTrain::OnCommand_wiperswitchincrease}, {user_command::wiperswitchdecrease, &TTrain::OnCommand_wiperswitchdecrease}, {user_command::lightsset, &TTrain::OnCommand_lightsset}, {user_command::pantographlowerall, &TTrain::OnCommand_pantographlowerall}, {user_command::pantographselectnext, &TTrain::OnCommand_pantographselectnext}, {user_command::pantographselectprevious, &TTrain::OnCommand_pantographselectprevious}, {user_command::pantographtoggleselected, &TTrain::OnCommand_pantographtoggleselected}, {user_command::pantographraiseselected, &TTrain::OnCommand_pantographraiseselected}, {user_command::pantographlowerselected, &TTrain::OnCommand_pantographlowerselected}, {user_command::pantographvalvesupdate, &TTrain::OnCommand_pantographvalvesupdate}, {user_command::pantographvalvesoff, &TTrain::OnCommand_pantographvalvesoff}, {user_command::linebreakertoggle, &TTrain::OnCommand_linebreakertoggle}, {user_command::linebreakeropen, &TTrain::OnCommand_linebreakeropen}, {user_command::linebreakerclose, &TTrain::OnCommand_linebreakerclose}, {user_command::fuelpumptoggle, &TTrain::OnCommand_fuelpumptoggle}, {user_command::fuelpumpenable, &TTrain::OnCommand_fuelpumpenable}, {user_command::fuelpumpdisable, &TTrain::OnCommand_fuelpumpdisable}, {user_command::oilpumptoggle, &TTrain::OnCommand_oilpumptoggle}, {user_command::oilpumpenable, &TTrain::OnCommand_oilpumpenable}, {user_command::oilpumpdisable, &TTrain::OnCommand_oilpumpdisable}, {user_command::waterheaterbreakertoggle, &TTrain::OnCommand_waterheaterbreakertoggle}, {user_command::waterheaterbreakerclose, &TTrain::OnCommand_waterheaterbreakerclose}, {user_command::waterheaterbreakeropen, &TTrain::OnCommand_waterheaterbreakeropen}, {user_command::waterheatertoggle, &TTrain::OnCommand_waterheatertoggle}, {user_command::waterheaterenable, &TTrain::OnCommand_waterheaterenable}, {user_command::waterheaterdisable, &TTrain::OnCommand_waterheaterdisable}, {user_command::waterpumpbreakertoggle, &TTrain::OnCommand_waterpumpbreakertoggle}, {user_command::waterpumpbreakerclose, &TTrain::OnCommand_waterpumpbreakerclose}, {user_command::waterpumpbreakeropen, &TTrain::OnCommand_waterpumpbreakeropen}, {user_command::waterpumptoggle, &TTrain::OnCommand_waterpumptoggle}, {user_command::waterpumpenable, &TTrain::OnCommand_waterpumpenable}, {user_command::waterpumpdisable, &TTrain::OnCommand_waterpumpdisable}, {user_command::watercircuitslinktoggle, &TTrain::OnCommand_watercircuitslinktoggle}, {user_command::watercircuitslinkenable, &TTrain::OnCommand_watercircuitslinkenable}, {user_command::watercircuitslinkdisable, &TTrain::OnCommand_watercircuitslinkdisable}, {user_command::convertertoggle, &TTrain::OnCommand_convertertoggle}, {user_command::converterenable, &TTrain::OnCommand_converterenable}, {user_command::converterdisable, &TTrain::OnCommand_converterdisable}, {user_command::convertertogglelocal, &TTrain::OnCommand_convertertogglelocal}, {user_command::converteroverloadrelayreset, &TTrain::OnCommand_converteroverloadrelayreset}, {user_command::compressortoggle, &TTrain::OnCommand_compressortoggle}, {user_command::compressorenable, &TTrain::OnCommand_compressorenable}, {user_command::compressordisable, &TTrain::OnCommand_compressordisable}, {user_command::compressortogglelocal, &TTrain::OnCommand_compressortogglelocal}, {user_command::compressorpresetactivatenext, &TTrain::OnCommand_compressorpresetactivatenext}, {user_command::compressorpresetactivateprevious, &TTrain::OnCommand_compressorpresetactivateprevious}, {user_command::compressorpresetactivatedefault, &TTrain::OnCommand_compressorpresetactivatedefault}, {user_command::motorblowerstogglefront, &TTrain::OnCommand_motorblowerstogglefront}, {user_command::motorblowerstogglerear, &TTrain::OnCommand_motorblowerstogglerear}, {user_command::motorblowersdisableall, &TTrain::OnCommand_motorblowersdisableall}, {user_command::coolingfanstoggle, &TTrain::OnCommand_coolingfanstoggle}, {user_command::motorconnectorsopen, &TTrain::OnCommand_motorconnectorsopen}, {user_command::motorconnectorsclose, &TTrain::OnCommand_motorconnectorsclose}, {user_command::motordisconnect, &TTrain::OnCommand_motordisconnect}, {user_command::motoroverloadrelaythresholdtoggle, &TTrain::OnCommand_motoroverloadrelaythresholdtoggle}, {user_command::motoroverloadrelaythresholdsetlow, &TTrain::OnCommand_motoroverloadrelaythresholdsetlow}, {user_command::motoroverloadrelaythresholdsethigh, &TTrain::OnCommand_motoroverloadrelaythresholdsethigh}, {user_command::motoroverloadrelayreset, &TTrain::OnCommand_motoroverloadrelayreset}, {user_command::universalrelayreset1, &TTrain::OnCommand_universalrelayreset}, {user_command::universalrelayreset2, &TTrain::OnCommand_universalrelayreset}, {user_command::universalrelayreset3, &TTrain::OnCommand_universalrelayreset}, {user_command::heatingtoggle, &TTrain::OnCommand_heatingtoggle}, {user_command::heatingenable, &TTrain::OnCommand_heatingenable}, {user_command::heatingdisable, &TTrain::OnCommand_heatingdisable}, {user_command::lightspresetactivatenext, &TTrain::OnCommand_lightspresetactivatenext}, {user_command::lightspresetactivateprevious, &TTrain::OnCommand_lightspresetactivateprevious}, {user_command::headlighttoggleleft, &TTrain::OnCommand_headlighttoggleleft}, {user_command::headlightenableleft, &TTrain::OnCommand_headlightenableleft}, {user_command::headlightdisableleft, &TTrain::OnCommand_headlightdisableleft}, {user_command::headlighttoggleright, &TTrain::OnCommand_headlighttoggleright}, {user_command::headlightenableright, &TTrain::OnCommand_headlightenableright}, {user_command::headlightdisableright, &TTrain::OnCommand_headlightdisableright}, {user_command::headlighttoggleupper, &TTrain::OnCommand_headlighttoggleupper}, {user_command::headlightenableupper, &TTrain::OnCommand_headlightenableupper}, {user_command::headlightdisableupper, &TTrain::OnCommand_headlightdisableupper}, {user_command::redmarkertoggleleft, &TTrain::OnCommand_redmarkertoggleleft}, {user_command::redmarkerenableleft, &TTrain::OnCommand_redmarkerenableleft}, {user_command::redmarkerdisableleft, &TTrain::OnCommand_redmarkerdisableleft}, {user_command::redmarkertoggleright, &TTrain::OnCommand_redmarkertoggleright}, {user_command::redmarkerenableright, &TTrain::OnCommand_redmarkerenableright}, {user_command::redmarkerdisableright, &TTrain::OnCommand_redmarkerdisableright}, {user_command::headlighttogglerearleft, &TTrain::OnCommand_headlighttogglerearleft}, {user_command::headlightenablerearleft, &TTrain::OnCommand_headlightenablerearleft}, {user_command::headlightdisablerearleft, &TTrain::OnCommand_headlightdisablerearleft}, {user_command::headlighttogglerearright, &TTrain::OnCommand_headlighttogglerearright}, {user_command::headlightenablerearright, &TTrain::OnCommand_headlightenablerearright}, {user_command::headlightdisablerearright, &TTrain::OnCommand_headlightdisablerearright}, {user_command::headlighttogglerearupper, &TTrain::OnCommand_headlighttogglerearupper}, {user_command::headlightenablerearupper, &TTrain::OnCommand_headlightenablerearupper}, {user_command::headlightdisablerearupper, &TTrain::OnCommand_headlightdisablerearupper}, {user_command::modernlightdimmerdecrease, &TTrain::OnCommand_modernlightdimmerdecrease}, {user_command::modernlightdimmerincrease, &TTrain::OnCommand_modernlightdimmerincrease}, {user_command::redmarkertogglerearleft, &TTrain::OnCommand_redmarkertogglerearleft}, {user_command::redmarkerenablerearleft, &TTrain::OnCommand_redmarkerenablerearleft}, {user_command::redmarkerdisablerearleft, &TTrain::OnCommand_redmarkerdisablerearleft}, {user_command::redmarkertogglerearright, &TTrain::OnCommand_redmarkertogglerearright}, {user_command::redmarkerenablerearright, &TTrain::OnCommand_redmarkerenablerearright}, {user_command::redmarkerdisablerearright, &TTrain::OnCommand_redmarkerdisablerearright}, {user_command::redmarkerstoggle, &TTrain::OnCommand_redmarkerstoggle}, {user_command::endsignalstoggle, &TTrain::OnCommand_endsignalstoggle}, {user_command::headlightsdimtoggle, &TTrain::OnCommand_headlightsdimtoggle}, {user_command::headlightsdimenable, &TTrain::OnCommand_headlightsdimenable}, {user_command::headlightsdimdisable, &TTrain::OnCommand_headlightsdimdisable}, {user_command::interiorlighttoggle, &TTrain::OnCommand_interiorlighttoggle}, {user_command::interiorlightenable, &TTrain::OnCommand_interiorlightenable}, {user_command::interiorlightdisable, &TTrain::OnCommand_interiorlightdisable}, {user_command::interiorlightdimtoggle, &TTrain::OnCommand_interiorlightdimtoggle}, {user_command::interiorlightdimenable, &TTrain::OnCommand_interiorlightdimenable}, {user_command::interiorlightdimdisable, &TTrain::OnCommand_interiorlightdimdisable}, {user_command::compartmentlightstoggle, &TTrain::OnCommand_compartmentlightstoggle}, {user_command::compartmentlightsenable, &TTrain::OnCommand_compartmentlightsenable}, {user_command::compartmentlightsdisable, &TTrain::OnCommand_compartmentlightsdisable}, {user_command::instrumentlighttoggle, &TTrain::OnCommand_instrumentlighttoggle}, {user_command::instrumentlightenable, &TTrain::OnCommand_instrumentlightenable}, {user_command::instrumentlightdisable, &TTrain::OnCommand_instrumentlightdisable}, {user_command::dashboardlighttoggle, &TTrain::OnCommand_dashboardlighttoggle}, {user_command::dashboardlightenable, &TTrain::OnCommand_dashboardlightenable}, {user_command::dashboardlightdisable, &TTrain::OnCommand_dashboardlightdisable}, {user_command::timetablelighttoggle, &TTrain::OnCommand_timetablelighttoggle}, {user_command::timetablelightenable, &TTrain::OnCommand_timetablelightenable}, {user_command::timetablelightdisable, &TTrain::OnCommand_timetablelightdisable}, {user_command::doorlocktoggle, &TTrain::OnCommand_doorlocktoggle}, {user_command::doortoggleleft, &TTrain::OnCommand_doortoggleleft}, {user_command::doortoggleright, &TTrain::OnCommand_doortoggleright}, {user_command::doorpermitleft, &TTrain::OnCommand_doorpermitleft}, {user_command::doorpermitright, &TTrain::OnCommand_doorpermitright}, {user_command::doorpermitpresetactivatenext, &TTrain::OnCommand_doorpermitpresetactivatenext}, {user_command::doorpermitpresetactivateprevious, &TTrain::OnCommand_doorpermitpresetactivateprevious}, {user_command::dooropenleft, &TTrain::OnCommand_dooropenleft}, {user_command::dooropenright, &TTrain::OnCommand_dooropenright}, {user_command::doorcloseleft, &TTrain::OnCommand_doorcloseleft}, {user_command::doorcloseright, &TTrain::OnCommand_doorcloseright}, {user_command::dooropenall, &TTrain::OnCommand_dooropenall}, {user_command::doorcloseall, &TTrain::OnCommand_doorcloseall}, {user_command::doorsteptoggle, &TTrain::OnCommand_doorsteptoggle}, {user_command::doormodetoggle, &TTrain::OnCommand_doormodetoggle}, {user_command::mirrorstoggle, &TTrain::OnCommand_mirrorstoggle}, {user_command::nearestcarcouplingincrease, &TTrain::OnCommand_nearestcarcouplingincrease}, {user_command::nearestcarcouplingdisconnect, &TTrain::OnCommand_nearestcarcouplingdisconnect}, {user_command::nearestcarcoupleradapterattach, &TTrain::OnCommand_nearestcarcoupleradapterattach}, {user_command::nearestcarcoupleradapterremove, &TTrain::OnCommand_nearestcarcoupleradapterremove}, {user_command::occupiedcarcouplingdisconnect, &TTrain::OnCommand_occupiedcarcouplingdisconnect}, {user_command::departureannounce, &TTrain::OnCommand_departureannounce}, {user_command::hornlowactivate, &TTrain::OnCommand_hornlowactivate}, {user_command::hornhighactivate, &TTrain::OnCommand_hornhighactivate}, {user_command::whistleactivate, &TTrain::OnCommand_whistleactivate}, {user_command::radiotoggle, &TTrain::OnCommand_radiotoggle}, {user_command::radioenable, &TTrain::OnCommand_radioenable}, {user_command::radiodisable, &TTrain::OnCommand_radiodisable}, {user_command::radiochannelincrease, &TTrain::OnCommand_radiochannelincrease}, {user_command::radiochanneldecrease, &TTrain::OnCommand_radiochanneldecrease}, {user_command::radiochannelset, &TTrain::OnCommand_radiochannelset}, {user_command::radiostopsend, &TTrain::OnCommand_radiostopsend}, {user_command::radiostopenable, &TTrain::OnCommand_radiostopenable}, {user_command::radiostopdisable, &TTrain::OnCommand_radiostopdisable}, {user_command::radiostoptest, &TTrain::OnCommand_radiostoptest}, {user_command::radiocall1send, &TTrain::OnCommand_radiocall1send}, {user_command::radiocall3send, &TTrain::OnCommand_radiocall3send}, {user_command::radiovolumeincrease, &TTrain::OnCommand_radiovolumeincrease}, {user_command::radiovolumedecrease, &TTrain::OnCommand_radiovolumedecrease}, {user_command::radiovolumeset, &TTrain::OnCommand_radiovolumeset}, {user_command::cabchangeforward, &TTrain::OnCommand_cabchangeforward}, {user_command::cabchangebackward, &TTrain::OnCommand_cabchangebackward}, {user_command::generictoggle0, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle1, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle2, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle3, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle4, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle5, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle6, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle7, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle8, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle9, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle10, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle11, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle12, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle13, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle14, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle15, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle16, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle17, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle18, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle19, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle20, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle21, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle22, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle23, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle24, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle25, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle26, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle27, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle28, &TTrain::OnCommand_generictoggle}, {user_command::generictoggle29, &TTrain::OnCommand_generictoggle}, {user_command::vehiclemoveforwards, &TTrain::OnCommand_vehiclemoveforwards}, {user_command::vehiclemovebackwards, &TTrain::OnCommand_vehiclemovebackwards}, {user_command::vehicleboost, &TTrain::OnCommand_vehicleboost}, {user_command::springbraketoggle, &TTrain::OnCommand_springbraketoggle}, {user_command::springbrakeenable, &TTrain::OnCommand_springbrakeenable}, {user_command::springbrakedisable, &TTrain::OnCommand_springbrakedisable}, {user_command::springbrakeshutofftoggle, &TTrain::OnCommand_springbrakeshutofftoggle}, {user_command::springbrakeshutoffenable, &TTrain::OnCommand_springbrakeshutoffenable}, {user_command::springbrakeshutoffdisable, &TTrain::OnCommand_springbrakeshutoffdisable}, {user_command::springbrakerelease, &TTrain::OnCommand_springbrakerelease}, {user_command::distancecounteractivate, &TTrain::OnCommand_distancecounteractivate}, {user_command::speedcontrolincrease, &TTrain::OnCommand_speedcontrolincrease}, {user_command::speedcontroldecrease, &TTrain::OnCommand_speedcontroldecrease}, {user_command::speedcontrolpowerincrease, &TTrain::OnCommand_speedcontrolpowerincrease}, {user_command::speedcontrolpowerdecrease, &TTrain::OnCommand_speedcontrolpowerdecrease}, {user_command::speedcontrolbutton0, &TTrain::OnCommand_speedcontrolbutton}, {user_command::speedcontrolbutton1, &TTrain::OnCommand_speedcontrolbutton}, {user_command::speedcontrolbutton2, &TTrain::OnCommand_speedcontrolbutton}, {user_command::speedcontrolbutton3, &TTrain::OnCommand_speedcontrolbutton}, {user_command::speedcontrolbutton4, &TTrain::OnCommand_speedcontrolbutton}, {user_command::speedcontrolbutton5, &TTrain::OnCommand_speedcontrolbutton}, {user_command::speedcontrolbutton6, &TTrain::OnCommand_speedcontrolbutton}, {user_command::speedcontrolbutton7, &TTrain::OnCommand_speedcontrolbutton}, {user_command::speedcontrolbutton8, &TTrain::OnCommand_speedcontrolbutton}, {user_command::speedcontrolbutton9, &TTrain::OnCommand_speedcontrolbutton}, {user_command::inverterenable1, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable2, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable3, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable4, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable5, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable6, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable7, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable8, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable9, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable10, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable11, &TTrain::OnCommand_inverterenable}, {user_command::inverterenable12, &TTrain::OnCommand_inverterenable}, {user_command::inverterdisable1, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable2, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable3, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable4, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable5, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable6, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable7, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable8, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable9, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable10, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable11, &TTrain::OnCommand_inverterdisable}, {user_command::inverterdisable12, &TTrain::OnCommand_inverterdisable}, {user_command::invertertoggle1, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle2, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle3, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle4, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle5, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle6, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle7, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle8, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle9, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle10, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle11, &TTrain::OnCommand_invertertoggle}, {user_command::invertertoggle12, &TTrain::OnCommand_invertertoggle}, }; std::vector const TTrain::fPress_labels = { "ch1: ", "ch2: ", "ch3: ", "ch4: ", "ch5: ", "ch6: ", "ch7: ", "ch8: ", "ch9: ", "ch0: "}; TTrain::TTrain() { ShowNextCurrent = false; // McZapkie-240302 - przyda sie do tachometru fTachoVelocity = 0; fTachoCount = 0; fPPress = fNPress = 0; // asMessage=""; pMechOffset = glm::dvec3(0, 0, 0); fBlinkTimer = 0; fHaslerTimer = 0; DynamicSet(nullptr); // ustawia wszystkie mv* //----- pMechSittingPosition = glm::dvec3(0, 0, 0); // ABu: 180404 fTachoTimer = 0.0; // włączenie skoków wskazań prędkościomierza // for (int i = 0; i < 8; i++) { bMains[i] = false; fCntVol[i] = 0.0f; bPants[i][0] = false; bPants[i][1] = false; bFuse[i] = false; bBatt[i] = false; bConv[i] = false; bComp[i][0] = false; bComp[i][1] = false; // bComp[ i ][ 2 ] = false; // bComp[ i ][ 3 ] = false; bHeat[i] = false; } bCompressors.clear(); for (int i = 0; i < 9; ++i) for (int j = 0; j < 10; ++j) { fEIMParams[i][j] = 0.0; fDieselParams[i][j] = 0.0; } for (int i = 0; i < 20; ++i) { for (int j = 0; j < 7; ++j) fPress[i][j] = 0.0; bBrakes[i][0] = bBrakes[i][1] = false; } } TTrain::~TTrain() {} bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) { // powiązanie ręcznego sterowania kabiną z pojazdem if (NewDynamicObject->Mechanik == nullptr) { /* ErrorLog( "Bad config: can't take control of inactive vehicle \"" + NewDynamicObject->asName + "\"" ); return false; */ auto const activecab{(NewDynamicObject->MoverParameters->CabOccupied > 0 ? "1" : NewDynamicObject->MoverParameters->CabOccupied < 0 ? "2" : "p")}; NewDynamicObject->create_controller(activecab, NewDynamicObject->ctOwner != nullptr); } DynamicSet(NewDynamicObject); if (!e3d) if (DynamicObject->Mechanik == nullptr) return false; DynamicObject->MechInside = true; fMainRelayTimer = 0; // Hunter, do k...y nędzy, ustawiaj wartości początkowe zmiennych! iCabn = (mvOccupied->CabOccupied > 0 ? 1 : mvOccupied->CabOccupied < 0 ? 2 : 0); { Global.CurrentMaxTextureSize = Global.iMaxCabTextureSize; auto const filename{mvOccupied->TypeName + ".mmd"}; LoadMMediaFile(filename); InitializeCab(mvOccupied->CabOccupied, filename); Global.CurrentMaxTextureSize = Global.iMaxTextureSize; if (DynamicObject->Controller == Humandriver) { // McZapkie-030303: mozliwosc wyswietlania kabiny, w przyszlosci dac opcje w mmd DynamicObject->bDisplayCab = true; } } // Ra: taka proteza - przesłanie kierunku do członów connected /* if (mvControlled->DirActive > 0) { // było do przodu mvControlled->DirectionBackward(); mvControlled->DirectionForward(); } else if (mvControlled->DirActive < 0) { mvControlled->DirectionForward(); mvControlled->DirectionBackward(); } */ if (false == DynamicObject->Mechanik->AIControllFlag) { DynamicObject->Mechanik->sync_consist_reversers(); } return true; } std::shared_ptr TTrain::GetTrainState(dictionary_source const &Extraparameters) { if ((mvOccupied == nullptr) || (mvControlled == nullptr)) { return nullptr; } auto dict = std::make_shared(Extraparameters); if (dict == nullptr) { return nullptr; } dict->insert("name", DynamicObject->asName); dict->insert("cab", mvOccupied->CabOccupied); dict->insert("cabactive", mvOccupied->CabActive); dict->insert("master", mvOccupied->CabMaster); // basic systems state data dict->insert("battery", mvOccupied->Power24vIsAvailable); dict->insert("linebreaker", mvControlled->Mains); dict->insert("main_init", (mvControlled->MainsInitTimeCountdown < mvControlled->MainsInitTime) && (mvControlled->MainsInitTimeCountdown > 0.0)); dict->insert("main_ready", (false == mvControlled->Mains) && (fHVoltage > 0.0) && (mvControlled->MainsInitTimeCountdown <= 0.0)); dict->insert("converter", mvOccupied->Power110vIsAvailable); dict->insert("converter_overload", mvControlled->ConvOvldFlag); dict->insert("compress", mvControlled->CompressorFlag); dict->insert("pant_compressor", mvPantographUnit->PantCompFlag); dict->insert("lights_front", mvOccupied->iLights[end::front]); dict->insert("lights_rear", mvOccupied->iLights[end::rear]); dict->insert("off_from_dimmer", mvOccupied->dimPositions[mvOccupied->modernDimmerPosition].isOff); dict->insert("lights_compartments", mvOccupied->CompartmentLights.is_active || mvOccupied->CompartmentLights.is_disabled); if (Dynamic()->Mechanik) { auto const *controller{Dynamic()->Mechanik}; auto const cabmodifier{cab_to_end() == end::front ? 1 : -1}; auto const traindirection{controller->Direction() * cabmodifier}; auto const *frontvehicle{controller->Vehicle(traindirection >= 0 ? end::front : end::rear)}; auto const *rearvehicle{controller->Vehicle(traindirection >= 0 ? end::rear : end::front)}; auto const frontvehicledirection{(frontvehicle->DirectionGet() == controller->Vehicle()->DirectionGet() ? 1 : -1)}; auto const rearvehicledirection{(rearvehicle->DirectionGet() == controller->Vehicle()->DirectionGet() ? 1 : -1)}; auto const fronttrainlights{frontvehicle->MoverParameters->iLights[frontvehicledirection * cabmodifier >= 0 ? end::front : end::rear]}; auto const reartrainlights{rearvehicle->MoverParameters->iLights[rearvehicledirection * cabmodifier >= 0 ? end::rear : end::front]}; dict->insert("lights_train_front", fronttrainlights); dict->insert("lights_train_rear", reartrainlights); } else { // fallback, in the unlikely case we lose the controller dict->insert("lights_train_front", mvOccupied->iLights[end::front]); dict->insert("lights_train_rear", mvOccupied->iLights[end::rear]); } // reverser dict->insert("direction", mvOccupied->DirActive); // throttle dict->insert("mainctrl_pos", mvControlled->MainCtrlPos); dict->insert("mainctrl_pos_count", mvControlled->MainCtrlPosNo); dict->insert("main_ctrl_actual_pos", mvControlled->MainCtrlActualPos); dict->insert("scndctrl_pos", mvControlled->ScndCtrlPos); dict->insert("scnd_ctrl_actual_pos", mvControlled->ScndCtrlActualPos); dict->insert("brakectrl_pos", mvControlled->fBrakeCtrlPos); dict->insert("localbrake_pos", mvControlled->LocalBrakePosA); dict->insert("new_speed", mvOccupied->NewSpeed); dict->insert("speedctrl", mvOccupied->SpeedCtrlValue); dict->insert("speedctrlpower", mvOccupied->SpeedCtrlUnit.DesiredPower); dict->insert("speedctrlactive", mvOccupied->SpeedCtrlUnit.IsActive); dict->insert("speedctrlstandby", mvOccupied->SpeedCtrlUnit.Standby); // brakes dict->insert("manual_brake", (mvOccupied->ManualBrakePos > 0)); bool const bEP = (mvControlled->LocHandle->GetCP() > 0.2) || (fEIMParams[0][5] > 0.01); dict->insert("dir_brake", bEP); bool bPN{false}; if ((typeid(*mvOccupied->Hamulec) == typeid(TLSt)) || (typeid(*mvOccupied->Hamulec) == typeid(TEStED))) { TBrake *temp_ham = mvOccupied->Hamulec.get(); bPN = (static_cast(temp_ham)->GetEDBCP() > 0.2); } dict->insert("indir_brake", bPN); dict->insert("emergency_brake", mvOccupied->AlarmChainFlag); dict->insert("brake_delay_flag", mvOccupied->BrakeDelayFlag); dict->insert("brake_op_mode_flag", mvOccupied->BrakeOpModeFlag); dict->insert("pipelock", mvOccupied->LockPipe); // other controls dict->insert("ca", mvOccupied->SecuritySystem.is_vigilance_blinking()); dict->insert("shp", mvOccupied->SecuritySystem.is_cabsignal_blinking()); dict->insert("distance_counter", m_distancecounter); dict->insert("pantpress", std::abs(mvPantographUnit->PantPress)); dict->insert("universal3", InstrumentLightActive); for (auto idx = 0; idx < ggUniversals.size(); idx++) { if (idx != 3) { dict->insert("universal" + std::to_string(idx), (ggUniversals[idx].GetValue() > 0.5)); } } dict->insert("radio", mvOccupied->Radio); dict->insert("radio_channel", RadioChannel()); dict->insert("radio_volume", m_radiovolume); dict->insert("door_lock", mvOccupied->Doors.lock_enabled); dict->insert("door_step", mvOccupied->Doors.step_enabled); dict->insert("door_permit_left", mvOccupied->Doors.instances[side::left].open_permit); dict->insert("door_permit_right", mvOccupied->Doors.instances[side::right].open_permit); // movement data dict->insert("velocity", std::abs(mvOccupied->Vel)); dict->insert("tractionforce", std::abs(mvOccupied->Ft)); dict->insert("slipping_wheels", mvOccupied->SlippingWheels); dict->insert("sanding", mvOccupied->SandDose); dict->insert("odometer", mvOccupied->DistCounter); // electric current data dict->insert("traction_voltage", std::abs(mvPantographUnit->PantographVoltage)); dict->insert("voltage", std::abs(mvControlled->EngineVoltage)); dict->insert("im", std::abs(mvControlled->Im)); dict->insert("fuse", mvControlled->FuseFlag); dict->insert("epfuse", mvOccupied->EpFuse); dict->insert("power_drawn", mvOccupied->EnergyMeter.first); dict->insert("power_returned", mvOccupied->EnergyMeter.second); // induction motor state data char const *TXTT[10] = {"fd", "fdt", "fdb", "pd", "pdt", "pdb", "itothv", "1", "2", "3"}; char const *TXTC[10] = {"fr", "frt", "frb", "pr", "prt", "prb", "im", "vm", "ihv", "uhv"}; char const *TXTD[10] = {"enrot", "nrot", "fill_des", "fill_real", "clutch_des", "clutch_real", "water_temp", "oil_press", "engine_temp", "retarder_fill"}; char const *TXTP[7] = {"bc", "bp", "sp", "cp", "rp", "mass", "spring"}; char const *TXTB[2] = {"spring_active", "spring_shutoff"}; for (int j = 0; j < 10; ++j) dict->insert(("eimp_t_" + std::string(TXTT[j])), fEIMParams[0][j]); for (int i = 0; i < 8; ++i) { auto const idx{std::to_string(i + 1)}; for (int j = 0; j < 10; ++j) dict->insert(("eimp_c" + idx + "_" + std::string(TXTC[j])), fEIMParams[i + 1][j]); for (int j = 0; j < 10; ++j) dict->insert(("diesel_param_" + idx + "_" + std::string(TXTD[j])), fDieselParams[i + 1][j]); dict->insert(("eimp_c" + idx + "_ms"), bMains[i]); dict->insert(("eimp_c" + idx + "_cv"), fCntVol[i]); dict->insert(("eimp_c" + idx + "_fuse"), bFuse[i]); dict->insert(("eimp_c" + idx + "_batt"), bBatt[i]); dict->insert(("eimp_c" + idx + "_conv"), bConv[i]); dict->insert(("eimp_c" + idx + "_heat"), bHeat[i]); dict->insert(("eimp_u" + idx + "_pf"), bPants[i][0]); dict->insert(("eimp_u" + idx + "_pr"), bPants[i][1]); dict->insert(("eimp_u" + idx + "_comp_a"), bComp[i][0]); dict->insert(("eimp_u" + idx + "_comp_w"), bComp[i][1]); } dict->insert("compressors_no", (int)bCompressors.size()); for (int i = 0; i < bCompressors.size(); i++) { auto const idx{std::to_string(i + 1)}; dict->insert("compressors_" + idx + "_allow", std::get<0>(bCompressors[i])); dict->insert("compressors_" + idx + "_work", std::get<1>(bCompressors[i])); dict->insert("compressors_" + idx + "_car_no", std::get<2>(bCompressors[i])); } bool kier = (DynamicObject->DirectionGet() * mvOccupied->CabOccupied > 0); TDynamicObject *p = DynamicObject->GetFirstDynamic(mvOccupied->CabOccupied < 0 ? end::rear : end::front, 4); int in = 0; while (p && in < 8) { if (p->MoverParameters->eimc[eimc_p_Pmax] > 1) { in++; dict->insert(("eimp_c" + std::to_string(in) + "_invno"), p->MoverParameters->InvertersNo); for (int j = 0; j < p->MoverParameters->InvertersNo; j++) { dict->insert(("eimp_c" + std::to_string(in) + "_inv" + std::to_string(j + 1) + "_act"), p->MoverParameters->Inverters[j].IsActive); dict->insert(("eimp_c" + std::to_string(in) + "_inv" + std::to_string(j + 1) + "_error"), p->MoverParameters->Inverters[j].Error); dict->insert(("eimp_c" + std::to_string(in) + "_inv" + std::to_string(j + 1) + "_allow"), p->MoverParameters->Inverters[j].Activate); } } p = (kier ? p->Next(4) : p->Prev(4)); } for (int i = 0; i < 20; ++i) { for (int j = 0; j < 7; ++j) { dict->insert(("eimp_pn" + std::to_string(i + 1) + "_" + TXTP[j]), fPress[i][j]); } for (int j = 0; j < 2; ++j) { dict->insert(("brakes_" + std::to_string(i + 1) + "_" + TXTB[j]), bBrakes[i][j]); } } // multi-unit state data dict->insert("car_no", iCarNo); dict->insert("power_no", iPowerNo); dict->insert("unit_no", iUnitNo); for (int i = 0; i < 20; i++) { auto const caridx{std::to_string(i + 1)}; dict->insert(("doors_" + caridx), bDoors[i][0]); dict->insert(("doors_l_" + caridx), bDoors[i][1]); dict->insert(("doors_r_" + caridx), bDoors[i][2]); dict->insert(("doorstep_l_" + caridx), bDoors[i][3]); dict->insert(("doorstep_r_" + caridx), bDoors[i][4]); dict->insert(("doors_no_" + caridx), iDoorNo[i]); dict->insert(("code_" + caridx), (std::to_string(iUnits[i]) + cCode[i])); dict->insert(("car_name" + caridx), asCarName[i]); dict->insert(("slip_" + caridx), bSlip[i]); } // ai state data auto const *driver{(DynamicObject->ctOwner != nullptr ? DynamicObject->ctOwner : DynamicObject->Mechanik)}; dict->insert("velocity_desired", driver->VelDesired); dict->insert("velroad", driver->VelRoad); dict->insert("vellimitlast", driver->VelLimitLast); dict->insert("velsignallast", driver->VelSignalLast); dict->insert("velsignalnext", driver->VelSignalNext); dict->insert("velnext", driver->VelNext); dict->insert("actualproximitydist", driver->ActualProximityDist); // train data driver->TrainTimetable().serialize(dict.get()); dict->insert("train_atpassengerstop", driver->IsAtPassengerStop); dict->insert("train_length", driver->fLength); // world state data dict->insert("scenario", Global.SceneryFile); dict->insert("hours", static_cast(simulation::Time.data().wHour)); dict->insert("minutes", static_cast(simulation::Time.data().wMinute)); dict->insert("seconds", static_cast(simulation::Time.second())); dict->insert("air_temperature", Global.AirTemperature); dict->insert("light_level", Global.fLuminance - std::max(0.f, Global.Overcast - 1.f)); return dict; } TTrain::state_t TTrain::get_state() const { return { btLampkaSHP.GetValue(), btLampkaCzuwaka.GetValue(), btLampkaRadioStop.GetValue(), btLampkaOpory.GetValue(), btLampkaWylSzybki.GetValue(), btLampkaPrzekRozn.GetValue(), btLampkaNadmSil.GetValue(), btLampkaStyczn.GetValue(), btLampkaPoslizg.GetValue(), btLampkaNadmPrzetw.GetValue(), btLampkaPrzetwOff.GetValue(), btLampkaNadmSpr.GetValue(), btLampkaNadmWent.GetValue(), btLampkaWysRozr.GetValue(), btLampkaOgrzewanieSkladu.GetValue(), static_cast(iCabn), btHaslerBrakes.GetValue(), btHaslerCurrent.GetValue(), mvOccupied->SecuritySystem.is_beeping(), btLampkaHVoltageB.GetValue(), fTachoVelocity, static_cast(mvOccupied->Compressor), static_cast(mvOccupied->PipePress), static_cast(mvOccupied->BrakePress), static_cast(mvPantographUnit->PantPress), fHVoltage, {fHCurrent[(mvControlled->TrainType & dt_EZT) ? 0 : 1], fHCurrent[2], fHCurrent[3]}, ggLVoltage.GetValue(), mvOccupied->DistCounter, static_cast(RadioChannel()), btLampkaSpringBrakeActive.GetValue(), btLampkaNapNastHam.GetValue(), mvOccupied->DirActive > 0, mvOccupied->DirActive < 0, mvOccupied->Doors.instances[mvOccupied->CabOccupied < 0 ? side::right : side::left].open_permit, mvOccupied->Doors.instances[mvOccupied->CabOccupied < 0 ? side::right : side::left].is_open, mvOccupied->Doors.instances[mvOccupied->CabOccupied < 0 ? side::left : side::right].open_permit, mvOccupied->Doors.instances[mvOccupied->CabOccupied < 0 ? side::left : side::right].is_open, mvOccupied->Doors.step_enabled, mvOccupied->Power24vIsAvailable, 0, mvOccupied->LockPipe, btLampkaRadioMessage.GetValue(), }; } bool TTrain::is_eztoer() const { return ((mvControlled->TrainType == dt_EZT) && (mvOccupied->BrakeSubsystem == TBrakeSubSystem::ss_ESt) && (mvControlled->Power24vIsAvailable == true) && (mvControlled->EpFuse == true) && (mvControlled->DirActive != 0)); // od yB } // mover master controller to specified position void TTrain::set_master_controller(double const Position) { auto positionchange{std::min(Position, (mvControlled->CoupledCtrl ? mvControlled->MainCtrlPosNo + mvControlled->ScndCtrlPosNo : mvControlled->MainCtrlPosNo)) - (mvControlled->CoupledCtrl ? mvControlled->MainCtrlPos + mvControlled->ScndCtrlPos : mvControlled->MainCtrlPos)}; while ((positionchange < 0) && (true == mvControlled->DecMainCtrl(1))) { ++positionchange; } while ((positionchange > 0) && (true == mvControlled->IncMainCtrl(1))) { --positionchange; } } // moves train brake lever to specified position, potentially emits switch sound if conditions are met void TTrain::set_train_brake(double const Position) { auto const originalbrakeposition{static_cast(100.0 * mvOccupied->fBrakeCtrlPos)}; mvOccupied->BrakeLevelSet(Position); if (static_cast(100.0 * mvOccupied->fBrakeCtrlPos) == originalbrakeposition) { return; } if ((true == is_eztoer()) && (false == (((originalbrakeposition / 100 == 0) || (originalbrakeposition / 100 >= 5)) && ((mvOccupied->BrakeCtrlPos == 0) || (mvOccupied->BrakeCtrlPos >= 5))))) { // sound feedback if the lever movement activates one of the switches if (dsbPneumaticSwitch) { dsbPneumaticSwitch->play(); } } } void TTrain::zero_charging_train_brake() { if ((mvOccupied->BrakeCtrlPos == -1) && (DynamicObject->Controller != AIdriver) && (Global.iFeedbackMode < 3) && ((mvOccupied->BrakeHandle == TBrakeHandle::FVel6) || (mvOccupied->BrakeHandle == TBrakeHandle::MHZ_EN57) || (mvOccupied->BrakeHandle == TBrakeHandle::MHZ_K8P))) { // Odskakiwanie hamulce EP set_train_brake(0); } } void TTrain::set_train_brake_speed(TDynamicObject *Vehicle, int const Speed) { if (true == Vehicle->MoverParameters->BrakeDelaySwitch(Speed)) { // visual feedback // TODO: add setting indicator to vehicle class, for external lever/indicator if (Vehicle == DynamicObject) { if (ggBrakeProfileCtrl.SubModel != nullptr) { ggBrakeProfileCtrl.UpdateValue(((mvOccupied->BrakeDelayFlag & bdelay_R) != 0 ? 2.0 : mvOccupied->BrakeDelayFlag - 1), dsbSwitch); } if (ggBrakeProfileG.SubModel != nullptr) { ggBrakeProfileG.UpdateValue((mvOccupied->BrakeDelayFlag == bdelay_G ? 1.0 : 0.0), dsbSwitch); } if (ggBrakeProfileR.SubModel != nullptr) { ggBrakeProfileR.UpdateValue(((mvOccupied->BrakeDelayFlag & bdelay_R) != 0 ? 1.0 : 0.0), dsbSwitch); } } } } void TTrain::set_paired_open_motor_connectors_button(bool const State) { if ((mvControlled->TrainType == dt_ET41) || (mvControlled->TrainType == dt_ET42)) { // crude implementation of the button affecting entire unit for multi-unit engines // TODO: rework it into part of standard command propagation system if ((mvControlled->Couplers[end::front].Connected != nullptr) && (true == TestFlag(mvControlled->Couplers[end::front].CouplingFlag, coupling::permanent))) { mvControlled->Couplers[end::front].Connected->StLinSwitchOff = State; } if ((mvControlled->Couplers[end::rear].Connected != nullptr) && (true == TestFlag(mvControlled->Couplers[end::rear].CouplingFlag, coupling::permanent))) { mvControlled->Couplers[end::rear].Connected->StLinSwitchOff = State; } } } // locates nearest vehicle belonging to the consist TDynamicObject *TTrain::find_nearest_consist_vehicle(bool freefly, glm::vec3 pos) const { if (!freefly) return DynamicObject; auto coupler{-2}; // scan for vehicle, not any specific coupler auto *vehicle{DynamicObject->ABuScanNearestObject(pos, DynamicObject->GetTrack(), 1, 1500, coupler)}; if (vehicle == nullptr) vehicle = DynamicObject->ABuScanNearestObject(pos, DynamicObject->GetTrack(), -1, 1500, coupler); // TBD, TODO: perform owner test for the located vehicle return vehicle; } // command handlers void TTrain::OnCommand_aidriverenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // on press if (Train->DynamicObject->Mechanik == nullptr) { return; } if (true == Train->DynamicObject->Mechanik->AIControllFlag) { // żeby nie trzeba było rozłączać dla zresetowania Train->DynamicObject->Mechanik->TakeControl(false); } Train->DynamicObject->Mechanik->TakeControl(true); } } void TTrain::OnCommand_aidriverdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // on press if (Train->DynamicObject->Mechanik) Train->DynamicObject->Mechanik->TakeControl(false); } } auto const EU07_CONTROLLER_BASERETURNDELAY{0.5f}; auto const EU07_CONTROLLER_KEYBOARDETURNDELAY{1.5f}; void TTrain::OnCommand_jointcontrollerset(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // on press or hold // value controls brake in range 0-0.5, master controller in range 0.5-1.0 if (Command.param1 >= 0.5) { Train->set_master_controller((Command.param1 * 2 - 1) * (Train->mvControlled->CoupledCtrl ? Train->mvControlled->MainCtrlPosNo + Train->mvControlled->ScndCtrlPosNo : Train->mvControlled->MainCtrlPosNo)); Train->m_mastercontrollerinuse = true; // when SplitEDPneumaticBrake is active the joint controller's negative range // commands the dedicated dynamic-brake lever instead of the local pneumatic brake if (Train->mvControlled->SplitEDPneumaticBrake) { Train->mvControlled->DynamicBrakeLevelSet(0.0); } else { Train->mvOccupied->LocalBrakePosA = 0; } } else { auto const negativeRange{std::clamp(1.0 - (Command.param1 * 2), 0.0, 1.0)}; if (Train->mvControlled->SplitEDPneumaticBrake) { // negative range of jointctrl drives only ED braking, local pneumatic brake stays untouched. // snap to DBPN discrete stops so the lever animates step-by-step auto const stepCount{std::max(1, Train->mvControlled->DynamicBrakeCtrlPosNo)}; auto const snapped{std::round(negativeRange * stepCount) / stepCount}; Train->mvControlled->DynamicBrakeLevelSet(snapped); } else { Train->mvOccupied->LocalBrakePosA = negativeRange; } if (Train->mvControlled->MainCtrlPowerPos() > 0) { Train->set_master_controller(Train->mvControlled->MainCtrlNoPowerPos()); } } } else { // release Train->m_mastercontrollerinuse = false; Train->m_mastercontrollerreturndelay = EU07_CONTROLLER_BASERETURNDELAY; // NOTE: keyboard return delay is omitted for other input sources } } void TTrain::OnCommand_mastercontrollerincrease(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // on press or hold auto const splitMode{Train->mvControlled->SplitEDPneumaticBrake}; // when SplitEDPneumaticBrake is true the joint controller's negative range maps // to the dedicated dynamic-brake lever (DynamicBrakeCtrl) rather than to LocalBrake auto const negativeRangeActive{splitMode ? (Train->mvControlled->DynamicBrakeCtrlPos > 0.0) : (Train->mvOccupied->LocalBrakePosA > 0.0)}; if ((Train->ggJointCtrl.SubModel != nullptr) && negativeRangeActive) { if (splitMode) { OnCommand_DynamicBrakeControllerDecrease(Train, Command); Train->m_mastercontrollerinuse = true; } else { OnCommand_independentbrakedecrease(Train, Command); } } else { Train->mvControlled->IncMainCtrl(1); Train->m_mastercontrollerinuse = true; } } else if (Command.action == GLFW_RELEASE) { // release Train->m_mastercontrollerinuse = false; Train->m_mastercontrollerreturndelay = EU07_CONTROLLER_KEYBOARDETURNDELAY + EU07_CONTROLLER_BASERETURNDELAY; } } void TTrain::OnCommand_mastercontrollerincreasefast(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // on press or hold auto const splitMode{Train->mvControlled->SplitEDPneumaticBrake}; auto const negativeRangeActive{splitMode ? (Train->mvControlled->DynamicBrakeCtrlPos > 0.0) : (Train->mvOccupied->LocalBrakePosA > 0.0)}; if ((Train->ggJointCtrl.SubModel != nullptr) && negativeRangeActive) { if (splitMode) { OnCommand_DynamicBrakeControllerDecreaseFast(Train, Command); Train->m_mastercontrollerinuse = true; } else { OnCommand_independentbrakedecreasefast(Train, Command); } } else { Train->mvControlled->IncMainCtrl(Train->mvControlled->MainCtrlPosNo); Train->m_mastercontrollerinuse = true; } } else { // release Train->m_mastercontrollerinuse = false; Train->m_mastercontrollerreturndelay = EU07_CONTROLLER_KEYBOARDETURNDELAY + EU07_CONTROLLER_BASERETURNDELAY; } } void TTrain::OnCommand_mastercontrollerdecrease(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // on press or hold auto const splitMode{Train->mvControlled->SplitEDPneumaticBrake}; if ((Train->ggJointCtrl.SubModel != nullptr) && (Train->mvControlled->IsMainCtrlNoPowerPos())) { // negative range of jointctrl: ED brake when split, otherwise pneumatic local brake if (splitMode) { OnCommand_DynamicBrakeControllerIncrease(Train, Command); Train->m_mastercontrollerinuse = true; } else { OnCommand_independentbrakeincrease(Train, Command); } } else { Train->mvControlled->DecMainCtrl(1); Train->m_mastercontrollerinuse = true; } } else if (Command.action == GLFW_RELEASE) { // release Train->m_mastercontrollerinuse = false; Train->m_mastercontrollerreturndelay = EU07_CONTROLLER_KEYBOARDETURNDELAY + EU07_CONTROLLER_BASERETURNDELAY; } } void TTrain::OnCommand_mastercontrollerdecreasefast(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // on press or hold auto const splitMode{Train->mvControlled->SplitEDPneumaticBrake}; if ((Train->ggJointCtrl.SubModel != nullptr) && (Train->mvControlled->IsMainCtrlNoPowerPos())) { if (splitMode) { OnCommand_DynamicBrakeControllerIncreaseFast(Train, Command); Train->m_mastercontrollerinuse = true; } else { OnCommand_independentbrakeincreasefast(Train, Command); } } else { Train->mvControlled->DecMainCtrl(Train->mvControlled->MainCtrlPowerPos()); Train->m_mastercontrollerinuse = true; } } else { // release Train->m_mastercontrollerinuse = false; Train->m_mastercontrollerreturndelay = EU07_CONTROLLER_KEYBOARDETURNDELAY + EU07_CONTROLLER_BASERETURNDELAY; } } void TTrain::OnCommand_mastercontrollerset(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // on press or hold Train->set_master_controller(Command.param1); Train->m_mastercontrollerinuse = true; } else { // release Train->m_mastercontrollerinuse = false; Train->m_mastercontrollerreturndelay = EU07_CONTROLLER_BASERETURNDELAY; // NOTE: keyboard return delay is omitted for other input sources } } void TTrain::OnCommand_DynamicBrakeControllerIncrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_RELEASE) { return; } if (false == Train->mvControlled->SplitEDPneumaticBrake) { return; } // step exactly one stop (1 / DBPN) per command, matching the discrete behaviour of jointctrl Train->mvControlled->IncDynamicBrakeLevel(1.0f); } void TTrain::OnCommand_DynamicBrakeControllerIncreaseFast(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_PRESS) { return; } if (false == Train->mvControlled->SplitEDPneumaticBrake) { return; } Train->mvControlled->IncDynamicBrakeLevel(static_cast(Train->mvControlled->DynamicBrakeCtrlPosNo)); } void TTrain::OnCommand_DynamicBrakeControllerDecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_RELEASE) { return; } if (false == Train->mvControlled->SplitEDPneumaticBrake) { return; } // step exactly one stop (1 / DBPN) per command, matching the discrete behaviour of jointctrl Train->mvControlled->DecDynamicBrakeLevel(1.0f); } void TTrain::OnCommand_DynamicBrakeControllerDecreaseFast(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_PRESS) { return; } if (false == Train->mvControlled->SplitEDPneumaticBrake) { return; } Train->mvControlled->DecDynamicBrakeLevel(static_cast(Train->mvControlled->DynamicBrakeCtrlPosNo)); } void TTrain::OnCommand_DynamicBrakeControllerSet(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_RELEASE) { return; } if (false == Train->mvControlled->SplitEDPneumaticBrake) { return; } // when input source uses raw 0..1 value, snap to nearest DBPN step auto const target{std::clamp(Command.param1, 0.0, 1.0)}; auto const stepCount{std::max(1, Train->mvControlled->DynamicBrakeCtrlPosNo)}; auto const snapped{std::round(target * stepCount) / stepCount}; Train->mvControlled->DynamicBrakeLevelSet(snapped); } void TTrain::OnCommand_secondcontrollerincrease(TTrain *Train, command_data const &Command) { if ((Train->mvControlled->EngineType == TEngineType::DieselElectric) && (true == Train->mvControlled->ShuntModeAllow) && (true == Train->mvControlled->ShuntMode)) { if (Command.action != GLFW_RELEASE) { Train->mvControlled->AnPos = std::clamp(Train->mvControlled->AnPos + 0.025, 0.0, 1.0); } } else { // regular mode // push or pushtoggle control type if (Train->ggScndCtrl.is_push()) { if (Command.action == GLFW_PRESS) { // activate on press Train->mvControlled->IncScndCtrl(1); } } // toggle control type else { if (Command.action != GLFW_RELEASE) { Train->mvControlled->IncScndCtrl(1); } } // HACK: potentially animate push or pushtoggle control if (Train->ggScndCtrl.is_push()) { auto const activeposition{Train->ggScndCtrl.is_toggle() ? 1.f : 1.f}; auto const neutralposition{Train->ggScndCtrl.is_toggle() ? 0.5f : 0.f}; Train->ggScndCtrl.UpdateValue(((Command.action == GLFW_RELEASE) ? neutralposition : activeposition), Train->dsbSwitch); } // potentially animate tempomat button if ((Train->ggScndCtrlButton.is_push()) && (Train->mvControlled->ScndCtrlPos <= 1)) { Train->ggScndCtrlButton.UpdateValue(((Command.action == GLFW_RELEASE) ? 0.f : 1.f), Train->dsbSwitch); } } } void TTrain::OnCommand_secondcontrollerincreasefast(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // on press or hold if ((Train->mvControlled->EngineType == TEngineType::DieselElectric) && (true == Train->mvControlled->ShuntMode)) { Train->mvControlled->AnPos = 1.0; } else { Train->mvControlled->IncScndCtrl(2); } } } void TTrain::OnCommand_notchingrelaytoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->mvOccupied->AutoRelayFlag) { // turn on Train->mvOccupied->AutoRelaySwitch(true); } else { // turn off Train->mvOccupied->AutoRelaySwitch(false); } } } void TTrain::OnCommand_tempomattoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggScndCtrlButton.is_push()) { // impulse switch if (Command.action == GLFW_RELEASE) { // just move the button(s) back to default position // visual feedback Train->ggScndCtrlButton.UpdateValue(0.0, Train->dsbSwitch); Train->ggScndCtrlOffButton.UpdateValue(0.0, Train->dsbSwitch); return; } // glfw_press if (Train->mvControlled->ScndCtrlPos == 0) { // turn on if it's not active Train->mvControlled->IncScndCtrl(1); // visual feedback Train->ggScndCtrlButton.UpdateValue(1.0, Train->dsbSwitch); } else { // otherwise turn off Train->mvControlled->DecScndCtrl(2); // visual feedback if (Train->m_controlmapper.contains("tempomatoff_sw:")) { Train->ggScndCtrlOffButton.UpdateValue(1.0, Train->dsbSwitch); } else { Train->ggScndCtrlButton.UpdateValue(1.0, Train->dsbSwitch); } } } else { // two-state switch if (Command.action == GLFW_RELEASE) { return; } if (Train->mvControlled->ScndCtrlPos == 0) { // turn on Train->mvControlled->IncScndCtrl(1); // visual feedback Train->ggScndCtrlButton.UpdateValue(1.0, Train->dsbSwitch); } else { // turn off Train->mvControlled->DecScndCtrl(2); // visual feedback Train->ggScndCtrlButton.UpdateValue(0.0, Train->dsbSwitch); } } } void TTrain::OnCommand_distancecounteractivate(TTrain *Train, command_data const &Command) { // NOTE: distance meter activation button is presumed to be of impulse type if (Command.action == GLFW_PRESS) { // visual feedback Train->ggDistanceCounterButton.UpdateValue(1.0, Train->dsbSwitch); // activate or start anew if (Train->mvOccupied->isDoubleClickForMeasureNeeded) { // handler tempomatu dla podwojnego kliku if (Train->trainLenghtMeasureTimer >= 0.f) // jesli zdazylismy w czasie sekundy Train->m_distancecounter = 0.f; // rozpoczynamy pomiar else Train->trainLenghtMeasureTimer = Train->mvOccupied->DistanceCounterDoublePressPeriod; // odpalamy zegarek od nowa } else { // dla pojedynczego kliku Train->m_distancecounter = 0.f; } } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggDistanceCounterButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_mucurrentindicatorothersourceactivate(TTrain *Train, command_data const &Command) { if (Train->ggNextCurrentButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Current Indicator Source switch is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { // turn on Train->ShowNextCurrent = true; // visual feedback Train->ggNextCurrentButton.UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // turn off Train->ShowNextCurrent = false; // visual feedback Train->ggNextCurrentButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_secondcontrollerdecrease(TTrain *Train, command_data const &Command) { if ((Train->mvControlled->EngineType == TEngineType::DieselElectric) && (true == Train->mvControlled->ShuntMode)) { if (Command.action != GLFW_RELEASE) { Train->mvControlled->AnPos = std::clamp(Train->mvControlled->AnPos - 0.025, 0.0, 1.0); } } else { // regular mode // push or pushtoggle control type if (Train->ggScndCtrl.is_push()) { // basic push control can't decrease state, but pushtoggle can if (true == Train->ggScndCtrl.is_toggle()) { if (Command.action == GLFW_PRESS) { // activate on press Train->mvControlled->DecScndCtrl(1); } } } // toggle control type else { if (Command.action != GLFW_RELEASE) { Train->mvControlled->DecScndCtrl(1); } } // HACK: potentially animate push or pushtoggle control if (Train->ggScndCtrl.is_push()) { auto const activeposition{Train->ggScndCtrl.is_toggle() ? 0.f : 1.f}; auto const neutralposition{Train->ggScndCtrl.is_toggle() ? 0.5f : 0.f}; Train->ggScndCtrl.UpdateValue(((Command.action == GLFW_RELEASE) ? neutralposition : activeposition), Train->dsbSwitch); } // potentially animate tempomat button if ((Train->ggScndCtrlButton.is_push()) && (Train->mvControlled->ScndCtrlPos <= 1)) { if (Train->m_controlmapper.contains("tempomatoff_sw:")) { Train->ggScndCtrlOffButton.UpdateValue(((Command.action == GLFW_RELEASE) ? 0.f : 1.f), Train->dsbSwitch); } else { Train->ggScndCtrlButton.UpdateValue(((Command.action == GLFW_RELEASE) ? 0.f : 1.f), Train->dsbSwitch); } } } } void TTrain::OnCommand_secondcontrollerdecreasefast(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // on press or hold if ((Train->mvControlled->EngineType == TEngineType::DieselElectric) && (true == Train->mvControlled->ShuntMode)) { Train->mvControlled->AnPos = 0.0; } else { Train->mvControlled->DecScndCtrl(2); } } } void TTrain::OnCommand_secondcontrollerset(TTrain *Train, command_data const &Command) { auto const targetposition{std::min(Command.param1, Train->mvControlled->ScndCtrlPosNo)}; // HACK: potentially animate push or pushtoggle control if (Train->ggScndCtrl.is_push()) { auto const activeposition{Train->ggScndCtrl.is_toggle() ? (targetposition < Train->mvControlled->ScndCtrlPos ? 0.f : targetposition > Train->mvControlled->ScndCtrlPos ? 1.f : Train->ggScndCtrl.GetDesiredValue()) : // leave the control in its current position if it hits the limit (targetposition == 0 ? 0.f : 1.f)}; auto const neutralposition{Train->ggScndCtrl.is_toggle() ? 0.5f : 0.f}; Train->ggScndCtrl.UpdateValue(((Command.action == GLFW_RELEASE) ? neutralposition : activeposition), Train->dsbSwitch); } // update control value if (Command.action != GLFW_RELEASE) { // on press or hold while ((targetposition < Train->mvControlled->GetVirtualScndPos()) && (true == Train->mvControlled->DecScndCtrl(1))) { // all work is done in the header ; } while ((targetposition > Train->mvControlled->GetVirtualScndPos()) && (true == Train->mvControlled->IncScndCtrl(1))) { // all work is done in the header ; } } } void TTrain::OnCommand_independentbrakeincrease(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // when SplitEDPneumaticBrake is active the local brake key always operates // the pneumatic local brake directly, never piggy-backing on the joint controller auto const splitMode{Train->mvControlled->SplitEDPneumaticBrake}; auto const useStepped{(Train->ggJointCtrl.SubModel != nullptr) && !splitMode}; if (Train->mvOccupied->LocalBrake != TLocalBrake::ManualBrake) { if ((false == splitMode) && (Train->ggJointCtrl.SubModel != nullptr) && (Train->mvOccupied->MainCtrlPos > 0)) { OnCommand_mastercontrollerdecrease(Train, Command); } else { Train->mvOccupied->IncLocalBrakeLevel(useStepped ? 1 : (Global.brake_speed * Command.time_delta * LocalBrakePosNo)); if (useStepped) { Train->m_mastercontrollerinuse = true; } } } } } void TTrain::OnCommand_independentbrakeincreasefast(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { auto const splitMode{Train->mvControlled->SplitEDPneumaticBrake}; auto const useJointAnim{(Train->ggJointCtrl.SubModel != nullptr) && !splitMode}; if (Train->mvOccupied->LocalBrake != TLocalBrake::ManualBrake) { if ((false == splitMode) && (Train->ggJointCtrl.SubModel != nullptr) && (Train->mvOccupied->MainCtrlPos > 0)) { OnCommand_mastercontrollerdecreasefast(Train, Command); } else { Train->mvOccupied->IncLocalBrakeLevel(LocalBrakePosNo); if (useJointAnim) { Train->m_mastercontrollerinuse = true; } } } } } void TTrain::OnCommand_independentbrakedecrease(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { auto const splitMode{Train->mvControlled->SplitEDPneumaticBrake}; auto const useStepped{(Train->ggJointCtrl.SubModel != nullptr) && !splitMode}; if ((Train->mvOccupied->LocalBrake != TLocalBrake::ManualBrake) // Ra 1014-06: AI potrafi zahamować pomocniczym mimo jego braku - odhamować jakoś trzeba // TODO: sort AI out so it doesn't do things it doesn't have equipment for || (Train->mvOccupied->LocalBrakePosA > 0)) { if ((false == splitMode) && (Train->ggJointCtrl.SubModel != nullptr) && (Train->mvOccupied->LocalBrakePosA == 0.0)) { OnCommand_mastercontrollerincrease(Train, Command); } else { Train->mvOccupied->DecLocalBrakeLevel(useStepped ? 1 : (Global.brake_speed * Command.time_delta * LocalBrakePosNo)); if (useStepped) { Train->m_mastercontrollerinuse = true; } } } } } void TTrain::OnCommand_independentbrakedecreasefast(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { auto const splitMode{Train->mvControlled->SplitEDPneumaticBrake}; auto const useJointAnim{(Train->ggJointCtrl.SubModel != nullptr) && !splitMode}; if ((Train->mvOccupied->LocalBrake != TLocalBrake::ManualBrake) // Ra 1014-06: AI potrafi zahamować pomocniczym mimo jego braku - odhamować jakoś trzeba // TODO: sort AI out so it doesn't do things it doesn't have equipment for || (Train->mvOccupied->LocalBrakePosA > 0)) { if ((false == splitMode) && (Train->ggJointCtrl.SubModel != nullptr) && (Train->mvOccupied->LocalBrakePosA == 0.0)) { OnCommand_mastercontrollerincreasefast(Train, Command); } else { Train->mvOccupied->DecLocalBrakeLevel(LocalBrakePosNo); if (useJointAnim) { Train->m_mastercontrollerinuse = true; } } } } } void TTrain::OnCommand_independentbrakeset(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { Train->mvOccupied->LocalBrakePosA = (std::clamp(Command.param1, 0.0, 1.0)); } /* Train->mvControlled->LocalBrakePos = ( std::round( interpolate( 0.0, LocalBrakePosNo, clamp( Command.param1, 0.0, 1.0 ) ) ) ); */ } void TTrain::OnCommand_independentbrakebailoff(TTrain *Train, command_data const &Command) { if (false == Command.freefly) { // TODO: check if this set of conditions can be simplified. // it'd be more flexible to have an attribute indicating whether bail off position is supported if ((Train->mvControlled->TrainType != dt_EZT) && ((Train->mvControlled->EngineType == TEngineType::ElectricSeriesMotor) || (Train->mvControlled->EngineType == TEngineType::DieselElectric) || (Train->mvControlled->EngineType == TEngineType::ElectricInductionMotor)) && (Train->mvOccupied->BrakeCtrlPosNo > 0)) { if (Command.action == GLFW_PRESS) { // press or hold // visual feedback Train->ggReleaserButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvOccupied->BrakeReleaser(1); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggReleaserButton.UpdateValue(0.0, Train->dsbSwitch); Train->mvOccupied->BrakeReleaser(0); } } } else { // car brake handling, while in walk mode auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle != nullptr) { if (Command.action == GLFW_PRESS) { // press or hold vehicle->MoverParameters->BrakeReleaser(1); } else if (Command.action == GLFW_RELEASE) { // release vehicle->MoverParameters->BrakeReleaser(0); } } } } void TTrain::OnCommand_universalbrakebutton1(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // press or hold // visual feedback Train->ggUniveralBrakeButton1.UpdateValue(1.0, Train->dsbSwitch); Train->mvOccupied->UniversalBrakeButton(0, 1); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggUniveralBrakeButton1.UpdateValue(0.0, Train->dsbSwitch); Train->mvOccupied->UniversalBrakeButton(0, 0); } } void TTrain::OnCommand_universalbrakebutton2(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // press or hold // visual feedback Train->ggUniveralBrakeButton2.UpdateValue(1.0, Train->dsbSwitch); Train->mvOccupied->UniversalBrakeButton(1, 1); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggUniveralBrakeButton2.UpdateValue(0.0, Train->dsbSwitch); Train->mvOccupied->UniversalBrakeButton(1, 0); } } void TTrain::OnCommand_universalbrakebutton3(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // press or hold // visual feedback Train->ggUniveralBrakeButton3.UpdateValue(1.0, Train->dsbSwitch); Train->mvOccupied->UniversalBrakeButton(2, 1); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggUniveralBrakeButton3.UpdateValue(0.0, Train->dsbSwitch); Train->mvOccupied->UniversalBrakeButton(2, 0); } } void TTrain::OnCommand_trainbrakeincrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT && Train->mvOccupied->BrakeHandle == TBrakeHandle::FV4a) Train->mvOccupied->BrakeLevelAdd(Global.brake_speed * Command.time_delta * Train->mvOccupied->BrakeCtrlPosNo); else if (Command.action == GLFW_PRESS && Train->mvOccupied->BrakeHandle != TBrakeHandle::FV4a) Train->set_train_brake(Train->mvOccupied->fBrakeCtrlPos + Global.fBrakeStep); } void TTrain::OnCommand_trainbrakedecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT && Train->mvOccupied->BrakeHandle == TBrakeHandle::FV4a) Train->mvOccupied->BrakeLevelAdd(-Global.brake_speed * Command.time_delta * Train->mvOccupied->BrakeCtrlPosNo); else if (Command.action == GLFW_PRESS && Train->mvOccupied->BrakeHandle != TBrakeHandle::FV4a) Train->set_train_brake(Train->mvOccupied->fBrakeCtrlPos - Global.fBrakeStep); else if (Command.action == GLFW_RELEASE) { // release Train->zero_charging_train_brake(); } } void TTrain::OnCommand_trainbrakeset(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // press or hold Train->mvOccupied->BrakeLevelSet(std::lerp(Train->mvOccupied->Handle->GetPos(bh_MIN), Train->mvOccupied->Handle->GetPos(bh_MAX), std::clamp(Command.param1, 0.0, 1.0))); } else { // release Train->zero_charging_train_brake(); } } void TTrain::OnCommand_trainbrakecharging(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // press or hold Train->set_train_brake(-1); } else { // release Train->zero_charging_train_brake(); } } void TTrain::OnCommand_trainbrakerelease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->set_train_brake(0); } } void TTrain::OnCommand_trainbrakefirstservice(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->set_train_brake(1); } } void TTrain::OnCommand_trainbrakeservice(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->set_train_brake((Train->mvOccupied->BrakeCtrlPosNo / 2 + (Train->mvOccupied->BrakeHandle == TBrakeHandle::FV4a ? 1 : 0))); } } void TTrain::OnCommand_trainbrakefullservice(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->set_train_brake(Train->mvOccupied->BrakeCtrlPosNo - 1); } } void TTrain::OnCommand_trainbrakehandleoff(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->set_train_brake(Train->mvOccupied->Handle->GetPos(bh_NP)); } } void TTrain::OnCommand_trainbrakeemergency(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->set_train_brake(Train->mvOccupied->Handle->GetPos(bh_EB)); /* if( Train->mvOccupied->BrakeCtrlPosNo <= 0.1 ) { // hamulec bezpieczeństwa dla wagonów Train->mvOccupied->RadioStopFlag = true; } */ } } void TTrain::OnCommand_trainbrakebasepressureincrease(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { switch (Train->mvOccupied->BrakeHandle) { case TBrakeHandle::FV4a: { Train->mvOccupied->BrakeCtrlPos2 = std::clamp(Train->mvOccupied->BrakeCtrlPos2 - 0.01, -1.5, 2.0); break; } default: { Train->mvOccupied->BrakeLevelAdd(0.01); break; } } } } void TTrain::OnCommand_trainbrakebasepressuredecrease(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { switch (Train->mvOccupied->BrakeHandle) { case TBrakeHandle::FV4a: { Train->mvOccupied->BrakeCtrlPos2 = std::clamp(Train->mvOccupied->BrakeCtrlPos2 + 0.01, -1.5, 2.0); break; } default: { Train->mvOccupied->BrakeLevelAdd(-0.01); break; } } } } void TTrain::OnCommand_trainbrakebasepressurereset(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->mvOccupied->BrakeCtrlPos2 = 0; } } void TTrain::OnCommand_trainbrakeoperationtoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle == nullptr) { return; } vehicle->MoverParameters->Hamulec->SetBrakeStatus(vehicle->MoverParameters->Hamulec->GetBrakeStatus() ^ b_dmg); } } void TTrain::OnCommand_manualbrakeincrease(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle == nullptr) { return; } if ((vehicle->MoverParameters->LocalBrake == TLocalBrake::ManualBrake) || (vehicle->MoverParameters->MBrake == true)) { vehicle->MoverParameters->IncManualBrakeLevel(1); } } } void TTrain::OnCommand_manualbrakedecrease(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle == nullptr) { return; } if ((vehicle->MoverParameters->LocalBrake == TLocalBrake::ManualBrake) || (vehicle->MoverParameters->MBrake == true)) { vehicle->MoverParameters->DecManualBrakeLevel(1); } } } void TTrain::OnCommand_alarmchaintoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { if (false == Train->mvOccupied->AlarmChainFlag) { OnCommand_alarmchainenable(Train, Command); } else { OnCommand_alarmchaindisable(Train, Command); } } } void TTrain::OnCommand_alarmchainenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // pull Train->mvOccupied->AlarmChainSwitch(true); // visual feedback Train->ggAlarmChain.UpdateValue(1.0); } } void TTrain::OnCommand_alarmchaindisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // release Train->mvOccupied->AlarmChainSwitch(false); // visual feedback Train->ggAlarmChain.UpdateValue(0.0); } } void TTrain::OnCommand_wheelspinbrakeactivate(TTrain *Train, command_data const &Command) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels if (Train->ggAntiSlipButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Wheelspin Brake button is missing, or wasn't defined"); } return; } if (Train->mvOccupied->BrakeSystem != TBrakeSystem::ElectroPneumatic) { // standard behaviour if (Command.action == GLFW_PRESS) { // visual feedback Train->ggAntiSlipButton.UpdateValue(1.0, Train->dsbSwitch); // NOTE: system activation is (repeatedly) done in the train update routine } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggAntiSlipButton.UpdateValue(0.0); } } else { // electro-pneumatic, custom case if (Command.action == GLFW_PRESS) { // visual feedback Train->ggAntiSlipButton.UpdateValue(1.0, Train->dsbPneumaticSwitch); if ((Train->mvOccupied->BrakeHandle == TBrakeHandle::St113) && (Train->mvControlled->EpFuse == true)) { Train->mvOccupied->SwitchEPBrake(1); } } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggAntiSlipButton.UpdateValue(0.0); Train->mvOccupied->SwitchEPBrake(0); } } } void TTrain::OnCommand_sandboxactivate(TTrain *Train, command_data const &Command) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels if (Train->ggSandButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Sandbox activation button is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { // visual feedback Train->ggSandButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvControlled->SandboxManual(true); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggSandButton.UpdateValue(0.0); Train->mvControlled->SandboxManual(false); } } void TTrain::OnCommand_autosandboxtoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->mvOccupied->SandDoseAutoAllow) { // turn on OnCommand_autosandboxactivate(Train, Command); } else { // turn off OnCommand_autosandboxdeactivate(Train, Command); } } }; void TTrain::OnCommand_autosandboxactivate(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SandboxAutoAllow(true); Train->ggAutoSandButton.UpdateValue(1.0, Train->dsbSwitch); } }; void TTrain::OnCommand_autosandboxdeactivate(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SandboxAutoAllow(false); Train->ggAutoSandButton.UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_epbrakecontrolenable(TTrain *Train, command_data const &Command) { auto const istoggle{(static_cast(Train->ggEPFuseButton.type()) & static_cast(TGaugeType::toggle)) != 0}; if (Command.action == GLFW_PRESS) { // command only works for bistable switch if (istoggle) { if (Train->mvOccupied->EpFuseSwitch(true)) { // audio feedback if (Train->dsbPneumaticSwitch) { Train->dsbPneumaticSwitch->play(); } Train->ggEPFuseButton.UpdateValue(1.0f, Train->dsbSwitch); }; } } } void TTrain::OnCommand_epbrakecontroldisable(TTrain *Train, command_data const &Command) { auto const istoggle{(static_cast(Train->ggEPFuseButton.type()) & static_cast(TGaugeType::toggle)) != 0}; if (Command.action == GLFW_PRESS) { // command only works for bistable switch if (istoggle) { if (Train->mvOccupied->EpFuseSwitch(false)) { Train->ggEPFuseButton.UpdateValue(0.0f, Train->dsbSwitch); }; } } } void TTrain::OnCommand_epbrakecontroltoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } auto const ispush{(static_cast(Train->ggEPFuseButton.type()) & static_cast(TGaugeType::push)) != 0}; auto const istoggle{(static_cast(Train->ggEPFuseButton.type()) & static_cast(TGaugeType::toggle)) != 0}; if (Command.action == GLFW_PRESS) { if (istoggle) { // switch state if (false == Train->mvOccupied->EpFuse) { // turn on if (Train->mvOccupied->EpFuseSwitch(true)) { // audio feedback if (Train->dsbPneumaticSwitch) { Train->dsbPneumaticSwitch->play(); } }; } else { // turn off Train->mvOccupied->EpFuseSwitch(false); } } else if (ispush) { // potentially turn on if (Train->mvOccupied->EpFuseSwitch(true)) { // audio feedback if (Train->dsbPneumaticSwitch) { Train->dsbPneumaticSwitch->play(); } }; } // visual feedback Train->ggEPFuseButton.UpdateValue((ispush ? 1.0f : // push or pushtoggle Train->mvOccupied->EpFuse ? 1.0f : 0.0f), // toggle Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { if (ispush) { // return the switch to neutral position Train->ggEPFuseButton.UpdateValue(0.0f, Train->dsbSwitch); } } } void TTrain::OnCommand_trainbrakeoperationmodeincrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (((Train->mvOccupied->BrakeOpModeFlag << 1) & Train->mvOccupied->BrakeOpModes) != 0) { // next mode Train->mvOccupied->BrakeOpModeFlag <<= 1; // visual feedback Train->ggBrakeOperationModeCtrl.UpdateValue(Train->mvOccupied->BrakeOpModeFlag > 0 ? std::log2(Train->mvOccupied->BrakeOpModeFlag) : 0); // audio fallback } } } void TTrain::OnCommand_trainbrakeoperationmodedecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (((Train->mvOccupied->BrakeOpModeFlag >> 1) & Train->mvOccupied->BrakeOpModes) != 0) { // previous mode Train->mvOccupied->BrakeOpModeFlag >>= 1; // visual feedback Train->ggBrakeOperationModeCtrl.UpdateValue(Train->mvOccupied->BrakeOpModeFlag > 0 ? std::log2(Train->mvOccupied->BrakeOpModeFlag) : 0); } } } void TTrain::OnCommand_brakeactingspeedincrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle == nullptr) { return; } if ((vehicle->MoverParameters->BrakeDelayFlag & bdelay_M) != 0) { // can't speed it up any more than this return; } auto const fasterbrakesetting = (vehicle->MoverParameters->BrakeDelayFlag < bdelay_R ? vehicle->MoverParameters->BrakeDelayFlag << 1 : vehicle->MoverParameters->BrakeDelayFlag | bdelay_M); Train->set_train_brake_speed(vehicle, fasterbrakesetting); } } void TTrain::OnCommand_brakeactingspeeddecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle == nullptr) { return; } if (vehicle->MoverParameters->BrakeDelayFlag == bdelay_G) { // can't slow it down any more than this return; } auto const slowerbrakesetting = (vehicle->MoverParameters->BrakeDelayFlag < bdelay_M ? vehicle->MoverParameters->BrakeDelayFlag >> 1 : vehicle->MoverParameters->BrakeDelayFlag ^ bdelay_M); Train->set_train_brake_speed(vehicle, slowerbrakesetting); } } void TTrain::OnCommand_brakeactingspeedsetcargo(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle == nullptr) { return; } Train->set_train_brake_speed(vehicle, bdelay_G); } } void TTrain::OnCommand_brakeactingspeedsetpassenger(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle == nullptr) { return; } Train->set_train_brake_speed(vehicle, bdelay_P); } } void TTrain::OnCommand_brakeactingspeedsetrapid(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle == nullptr) { return; } Train->set_train_brake_speed(vehicle, bdelay_R); } } void TTrain::OnCommand_brakeloadcompensationincrease(TTrain *Train, command_data const &Command) { if ((true == Command.freefly) && (Command.action == GLFW_PRESS)) { auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle != nullptr) { vehicle->MoverParameters->IncBrakeMult(); } } } void TTrain::OnCommand_brakeloadcompensationdecrease(TTrain *Train, command_data const &Command) { if ((true == Command.freefly) && (Command.action == GLFW_PRESS)) { auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle != nullptr) { vehicle->MoverParameters->DecBrakeMult(); } } } void TTrain::OnCommand_mubrakingindicatortoggle(TTrain *Train, command_data const &Command) { if (Train->ggSignallingButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Braking Indicator switch is missing, or wasn't defined"); } return; } if (Train->mvControlled->TrainType != dt_EZT) { // return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->mvControlled->Signalling) { // turn on Train->mvControlled->Signalling = true; // visual feedback Train->ggSignallingButton.UpdateValue(1.0, Train->dsbSwitch); } else { // turn off Train->mvControlled->Signalling = false; // visual feedback Train->ggSignallingButton.UpdateValue(0.0, Train->dsbSwitch); } } } void TTrain::OnCommand_wiperswitchincrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->mvOccupied->wiperSwitchPos++; if (Train->mvOccupied->wiperSwitchPos > Train->mvOccupied->WiperListSize - 1) Train->mvOccupied->wiperSwitchPos = Train->mvOccupied->WiperListSize - 1; // Visual feedback Train->ggWiperSw.UpdateValue(Train->mvOccupied->wiperSwitchPos, Train->dsbSwitch); } } void TTrain::OnCommand_wiperswitchdecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->mvOccupied->wiperSwitchPos--; if (Train->mvOccupied->wiperSwitchPos < 0) Train->mvOccupied->wiperSwitchPos = 0; // visual feedback Train->ggWiperSw.UpdateValue(Train->mvOccupied->wiperSwitchPos, Train->dsbSwitch); } } void TTrain::OnCommand_reverserincrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // HACK: master controller position isn't set in occupied vehicle in E(D)MUs // so we do a manual check in relevant vehicle here if (false == Train->mvControlled->EIMDirectionChangeAllow()) { return; } if (Train->mvOccupied->DirectionForward()) { // aktualizacja skrajnych pojazdów w składzie if ((Train->mvOccupied->DirActive) && (Train->DynamicObject->Mechanik)) { Train->DynamicObject->Mechanik->DirectionChange(); } } } } void TTrain::OnCommand_reverserdecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // HACK: master controller position isn't set in occupied vehicle in E(D)MUs // so we do a manual check in relevant vehicle here if (false == Train->mvControlled->EIMDirectionChangeAllow()) { return; } if (Train->mvOccupied->DirectionBackward()) { // aktualizacja skrajnych pojazdów w składzie if ((Train->mvOccupied->DirActive) && (Train->DynamicObject->Mechanik)) { Train->DynamicObject->Mechanik->DirectionChange(); ; } } } } void TTrain::OnCommand_reverserforwardhigh(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // HACK: master controller position isn't set in occupied vehicle in E(D)MUs // so we do a manual check in relevant vehicle here if (false == Train->mvControlled->EIMDirectionChangeAllow()) { return; } // HACK: try to move the reverser one position back, in case it's set to "high forward" OnCommand_reverserdecrease(Train, Command); if (Train->mvOccupied->DirActive < 1) { while ((Train->mvOccupied->DirActive < 1) && (true == Train->mvOccupied->DirectionForward())) { // all work is done in the header } // aktualizacja skrajnych pojazdów w składzie if ((Train->mvOccupied->DirActive == 1) && (Train->DynamicObject->Mechanik)) { Train->DynamicObject->Mechanik->DirectionChange(); } } OnCommand_reverserincrease(Train, Command); } } void TTrain::OnCommand_reverserforward(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // HACK: master controller position isn't set in occupied vehicle in E(D)MUs // so we do a manual check in relevant vehicle here if (false == Train->mvControlled->EIMDirectionChangeAllow()) { return; } // HACK: try to move the reverser one position back, in case it's set to "high forward" // OnCommand_reverserdecrease( Train, Command ); // visual feedback Train->ggDirForwardButton.UpdateValue(1.0, Train->dsbSwitch); if (Train->mvOccupied->DirActive == 0) { while ((Train->mvOccupied->DirActive < 1) && (true == Train->mvOccupied->DirectionForward())) { // all work is done in the header } // aktualizacja skrajnych pojazdów w składzie if ((Train->mvOccupied->DirActive == 1) && (Train->DynamicObject->Mechanik)) { Train->DynamicObject->Mechanik->DirectionChange(); } } } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggDirForwardButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_reverserneutral(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // HACK: master controller position isn't set in occupied vehicle in E(D)MUs // so we do a manual check in relevant vehicle here if (false == Train->mvControlled->EIMDirectionChangeAllow()) { return; } // visual feedback Train->ggDirNeutralButton.UpdateValue(1.0, Train->dsbSwitch); while ((Train->mvOccupied->DirActive < 0) && (true == Train->mvOccupied->DirectionForward())) { // all work is done in the header } while ((Train->mvOccupied->DirActive > 0) && (true == Train->mvOccupied->DirectionBackward())) { // all work is done in the header } } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggDirNeutralButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_reverserbackward(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // HACK: master controller position isn't set in occupied vehicle in E(D)MUs // so we do a manual check in relevant vehicle here if (false == Train->mvControlled->EIMDirectionChangeAllow()) { return; } Train->ggDirBackwardButton.UpdateValue(1.0, Train->dsbSwitch); if (Train->mvOccupied->DirActive == 0) { while ((Train->mvOccupied->DirActive > -1) && (true == Train->mvOccupied->DirectionBackward())) { // all work is done in the header } // aktualizacja skrajnych pojazdów w składzie if ((Train->mvOccupied->DirActive == -1) && (Train->DynamicObject->Mechanik)) { Train->DynamicObject->Mechanik->DirectionChange(); } } } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggDirBackwardButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_alerteracknowledge(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggSecurityResetButton.UpdateValue(1.0, Train->dsbSwitch); if (Train->mvOccupied->TrainType == dt_EZT || Train->mvOccupied->DirActive != 0) Train->mvOccupied->SecuritySystem.acknowledge_press(); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggSecurityResetButton.UpdateValue(0.0); if (Train->mvOccupied->TrainType == dt_EZT || Train->mvOccupied->DirActive != 0) Train->mvOccupied->SecuritySystem.acknowledge_release(); } } void TTrain::OnCommand_cabsignalacknowledge(TTrain *Train, command_data const &Command) { // TODO: visual feedback if (Command.action == GLFW_PRESS) { if (Train->mvOccupied->SecuritySystem.has_separate_acknowledge()) { Train->mvOccupied->SecuritySystem.cabsignal_reset(); Train->ggSHPResetButton.UpdateValue(1.0, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { Train->ggSHPResetButton.UpdateValue(0.0); } } void TTrain::OnCommand_batterytoggle(TTrain *Train, command_data const &Command) { if (Train->allowBatteryToggle || Command.action != GLFW_REPEAT) { // keep the switch from flipping back and forth if key is held down if (false == Train->mvOccupied->Power24vIsAvailable) { // turn on OnCommand_batteryenable(Train, Command); } else { // turn off OnCommand_batterydisable(Train, Command); } } } void TTrain::OnCommand_batteryenable(TTrain *Train, command_data const &Command) { if (!Train->mvOccupied->isBatteryButtonImpulse) { // regular button behavior if (Command.action == GLFW_PRESS) { // visual feedback Train->ggBatteryButton.UpdateValue(1.0f, Train->dsbSwitch); Train->ggBatteryOnButton.UpdateValue(1.0f, Train->dsbSwitch); Train->mvOccupied->BatterySwitch(true); Train->allowBatteryToggle = false; // side-effects if (Train->mvOccupied->LightsPosNo > 0) { Train->Dynamic()->SetLights(); } } else if (Command.action == GLFW_RELEASE) { if (Train->ggBatteryButton.type() == TGaugeType::push) { // return the switch to neutral position Train->ggBatteryButton.UpdateValue(0.5f); } Train->ggBatteryOnButton.UpdateValue(0.0f, Train->dsbSwitch); Train->allowBatteryToggle = true; } } else // impulse button behavior { if (Command.action == GLFW_PRESS) { if (Train->mvOccupied->shouldHoldBatteryButton) { // jesli przycisk trzeba przytrzymac Train->ggBatteryButton.UpdateValue(1.0f, Train->dsbSwitch); Train->ggBatteryOnButton.UpdateValue(1.0f, Train->dsbSwitch); Train->fBatteryTimer = Train->mvOccupied->BatteryButtonHoldTime; // start timer } else { // jesli przycisk dziala od razu Train->mvOccupied->BatterySwitch(true); Train->allowBatteryToggle = false; // side-effects if (Train->mvOccupied->LightsPosNo > 0) { Train->Dynamic()->SetLights(); } // visual feedback Train->ggBatteryButton.UpdateValue(1.0f, Train->dsbSwitch); Train->ggBatteryOnButton.UpdateValue(1.0f, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggBatteryButton.UpdateValue(0.0f, Train->dsbSwitch); Train->ggBatteryOnButton.UpdateValue(0.0f, Train->dsbSwitch); Train->fBatteryTimer = -1.f; // Train->allowBatteryToggle = true; Train->mvOccupied->batterySwAlreadyFired = false; } else if (Command.action == GLFW_REPEAT && Train->mvOccupied->shouldHoldBatteryButton) { // trzymamy przycisk if (Train->fBatteryTimer <= 0.0 && Train->mvOccupied->Battery == false && !Train->mvOccupied->batterySwAlreadyFired) { Train->mvOccupied->BatterySwitch(true); Train->mvOccupied->batterySwAlreadyFired = true; // side-effects if (Train->mvOccupied->LightsPosNo > 0) { Train->Dynamic()->SetLights(); } Train->allowBatteryToggle = false; } } } } void TTrain::OnCommand_batterydisable(TTrain *Train, command_data const &Command) { if (!Train->mvOccupied->isBatteryButtonImpulse) { // regular button behavior if (Command.action == GLFW_PRESS) { // visual feedback Train->ggBatteryButton.UpdateValue(0.0f, Train->dsbSwitch); Train->ggBatteryOffButton.UpdateValue(1.0f, Train->dsbSwitch); Train->mvOccupied->BatterySwitch(false); // side-effects if (Train->mvOccupied->LightsPosNo > 0) { Train->Dynamic()->SetLights(); } } else if (Command.action == GLFW_RELEASE) { if (Train->ggBatteryButton.type() == TGaugeType::push) { // return the switch to neutral position Train->ggBatteryButton.UpdateValue(0.5f); } Train->ggBatteryOffButton.UpdateValue(0.0f, Train->dsbSwitch); } } else // impulse button behavior { if (Command.action == GLFW_PRESS) { if (Train->mvOccupied->shouldHoldBatteryButton) { // jesli przycisk trzeba przytrzymac Train->ggBatteryButton.UpdateValue(1.0f, Train->dsbSwitch); Train->ggBatteryOffButton.UpdateValue(1.0f, Train->dsbSwitch); Train->fBatteryTimer = Train->mvOccupied->BatteryButtonHoldTime; // start timer } else { // jesli przycisk dziala od razu Train->mvOccupied->BatterySwitch(false); Train->allowBatteryToggle = false; // side-effects if (Train->mvOccupied->LightsPosNo > 0) { Train->Dynamic()->SetLights(); } // visual feedback Train->ggBatteryButton.UpdateValue(1.0f, Train->dsbSwitch); Train->ggBatteryOffButton.UpdateValue(1.0f, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggBatteryButton.UpdateValue(0.0f, Train->dsbSwitch); Train->ggBatteryOffButton.UpdateValue(0.0f, Train->dsbSwitch); Train->allowBatteryToggle = true; Train->mvOccupied->batterySwAlreadyFired = false; } else if (Command.action == GLFW_REPEAT && Train->mvOccupied->shouldHoldBatteryButton) { // trzymamy przycisk if (Train->fBatteryTimer <= 0.0 && Train->mvOccupied->Battery == true && !Train->mvOccupied->batterySwAlreadyFired) { Train->mvOccupied->BatterySwitch(false); Train->mvOccupied->batterySwAlreadyFired = true; // side-effects if (Train->mvOccupied->LightsPosNo > 0) { Train->Dynamic()->SetLights(); } Train->allowBatteryToggle = false; } } } } void TTrain::OnCommand_cabactivationtoggle(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_REPEAT) { // keep the switch from flipping back and forth if key is held down if (0 == Train->mvOccupied->CabActive) { // turn on OnCommand_cabactivationenable(Train, Command); } else { // turn off OnCommand_cabactivationdisable(Train, Command); } } } void TTrain::OnCommand_cabactivationenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback if (Train->ggCabActivationButton.type() == TGaugeType::push) { Train->ggCabActivationButton.UpdateValue(1.0f, Train->dsbSwitch); } Train->mvOccupied->CabActivisation(); // side-effects if (Train->mvOccupied->LightsPosNo > 0) { Train->Dynamic()->SetLights(); } } else if (Command.action == GLFW_RELEASE) { if (Train->ggCabActivationButton.type() == TGaugeType::push) { // return the switch to neutral position Train->ggCabActivationButton.UpdateValue(0.5f); } } } void TTrain::OnCommand_cabactivationdisable(TTrain *Train, command_data const &Command) { // TBD, TODO: ewentualnie zablokować z FIZ, np. w samochodach się nie odłącza akumulatora if (Command.action == GLFW_PRESS) { // visual feedback if (Train->ggCabActivationButton.type() == TGaugeType::push) { Train->ggCabActivationButton.UpdateValue(0.0f, Train->dsbSwitch); } Train->mvOccupied->CabDeactivisation(); if ((Train->mvOccupied->LightsPosNo > 0) && (Train->mvOccupied->InactiveCabFlag & activation::redmarkers)) { Train->Dynamic()->SetLights(); } } else if (Command.action == GLFW_RELEASE) { if (Train->ggCabActivationButton.type() == TGaugeType::push) { // return the switch to neutral position Train->ggCabActivationButton.UpdateValue(0.5f); } } } void TTrain::OnCommand_pantographtogglefront(TTrain *Train, command_data const &Command) { // HACK: presence of pantograph selector prevents manual operation of the individual valves if (Train->m_controlmapper.contains("pantselect_sw:")) { return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const &pantograph{Train->mvPantographUnit->Pantographs[end::front]}; auto const state{pantograph.valve.is_enabled || pantograph.is_active}; // fallback for impulse switches if (state) { OnCommand_pantographlowerfront(Train, Command); } else { OnCommand_pantographraisefront(Train, Command); } } else if (Command.action == GLFW_RELEASE) { // impulse switches return automatically to neutral position if (Train->mvOccupied->PantSwitchType == "impulse") { auto const ismanual{Train->iCabn == 0}; Train->mvOccupied->OperatePantographValve(end::front, operation_t::none, (ismanual ? range_t::local : range_t::consist)); } } } void TTrain::OnCommand_pantographtogglerear(TTrain *Train, command_data const &Command) { // HACK: presence of pantograph selector prevents manual operation of the individual valves if (Train->m_controlmapper.contains("pantselect_sw:")) { return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const &pantograph{Train->mvPantographUnit->Pantographs[end::rear]}; auto const state{pantograph.valve.is_enabled || pantograph.is_active}; // fallback for impulse switches if (state) { OnCommand_pantographlowerrear(Train, Command); } else { OnCommand_pantographraiserear(Train, Command); } } else if (Command.action == GLFW_RELEASE) { // impulse switches return automatically to neutral position if (Train->mvOccupied->PantSwitchType == "impulse") { auto const ismanual{Train->iCabn == 0}; Train->mvOccupied->OperatePantographValve(end::rear, operation_t::none, (ismanual ? range_t::local : range_t::consist)); } } } void TTrain::OnCommand_pantographraisefront(TTrain *Train, command_data const &Command) { // HACK: presence of pantograph selector prevents manual operation of the individual valves if (Train->m_controlmapper.contains("pantselect_sw:")) { return; } // prevent operation without submodel outside of engine compartment if ((Train->iCabn != 0) && (false == Train->m_controlmapper.contains("pantfront_sw:"))) { return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down // HACK: don't propagate pantograph commands issued from engine compartment, these are presumed to be manually moved levers auto const ismanual{Train->iCabn == 0}; Train->mvOccupied->OperatePantographValve(end::front, (Train->mvOccupied->PantSwitchType == "impulse" ? operation_t::enable_on : operation_t::enable), (ismanual ? range_t::local : range_t::consist)); } else if (Command.action == GLFW_RELEASE) { // NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch OnCommand_pantographtogglefront(Train, Command); } } void TTrain::OnCommand_pantographraiserear(TTrain *Train, command_data const &Command) { // HACK: presence of pantograph selector prevents manual operation of the individual valves if (Train->m_controlmapper.contains("pantselect_sw:")) { return; } // prevent operation without submodel outside of engine compartment if ((Train->iCabn != 0) && (false == Train->m_controlmapper.contains("pantrear_sw:"))) { return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down // HACK: don't propagate pantograph commands issued from engine compartment, these are presumed to be manually moved levers auto const ismanual{Train->iCabn == 0}; Train->mvOccupied->OperatePantographValve(end::rear, (Train->mvOccupied->PantSwitchType == "impulse" ? operation_t::enable_on : operation_t::enable), (ismanual ? range_t::local : range_t::consist)); } else if (Command.action == GLFW_RELEASE) { // NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch OnCommand_pantographtogglerear(Train, Command); } } void TTrain::OnCommand_pantographlowerfront(TTrain *Train, command_data const &Command) { // HACK: presence of pantograph selector prevents manual operation of the individual valves if (Train->m_controlmapper.contains("pantselect_sw:")) { return; } // prevent operation without submodel outside of engine compartment if ((Train->iCabn != 0) && (false == Train->m_controlmapper.contains(Train->mvOccupied->PantSwitchType == "impulse" ? "pantfrontoff_sw:" : "pantfront_sw:"))) { return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down // HACK: don't propagate pantograph commands issued from engine compartment, these are presumed to be manually moved levers auto const ismanual{Train->iCabn == 0}; Train->mvOccupied->OperatePantographValve(end::front, (Train->mvOccupied->PantSwitchType == "impulse" ? operation_t::disable_on : operation_t::disable), (ismanual ? range_t::local : range_t::consist)); } else if (Command.action == GLFW_RELEASE) { // NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch OnCommand_pantographtogglefront(Train, Command); } } void TTrain::OnCommand_pantographlowerrear(TTrain *Train, command_data const &Command) { // HACK: presence of pantograph selector prevents manual operation of the individual valves if (Train->m_controlmapper.contains("pantselect_sw:")) { return; } if ((Train->iCabn != 0) && (false == Train->m_controlmapper.contains(Train->mvOccupied->PantSwitchType == "impulse" ? "pantrearoff_sw:" : "pantrear_sw:"))) { return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down // HACK: don't propagate pantograph commands issued from engine compartment, these are presumed to be manually moved levers auto const ismanual{Train->iCabn == 0}; Train->mvOccupied->OperatePantographValve(end::rear, (Train->mvOccupied->PantSwitchType == "impulse" ? operation_t::disable_on : operation_t::disable), (ismanual ? range_t::local : range_t::consist)); } else if (Command.action == GLFW_RELEASE) { // NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch OnCommand_pantographtogglerear(Train, Command); } } void TTrain::OnCommand_pantographlowerall(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggPantAllDownButton.SubModel == nullptr) { // TODO: expand definition of cab controls so we can know if the control is present without testing for presence of 3d switch if (Command.action == GLFW_PRESS) { WriteLog("Lower All Pantographs switch is missing, or wasn't defined"); } return; } if (Train->ggPantAllDownButton.type() == TGaugeType::toggle) { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { Train->mvPantographUnit->DropAllPantographs(false == Train->mvPantographUnit->PantAllDown); // visual feedback Train->ggPantAllDownButton.UpdateValue((Train->mvPantographUnit->PantAllDown ? 1.0 : 0.0), Train->dsbSwitch); } } else { // impulse switch Train->mvControlled->DropAllPantographs(Command.action == GLFW_PRESS); // visual feedback Train->ggPantAllDownButton.UpdateValue((Command.action == GLFW_PRESS ? 1.0 : 0.0), Train->dsbSwitch); } } void TTrain::OnCommand_pantographselectnext(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_PRESS) { return; } if (false == Train->m_controlmapper.contains("pantselect_sw:")) { return; } Train->change_pantograph_selection(1); } void TTrain::OnCommand_pantographselectprevious(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_PRESS) { return; } if (false == Train->m_controlmapper.contains("pantselect_sw:")) { return; } Train->change_pantograph_selection(-1); } void TTrain::OnCommand_pantographtoggleselected(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Command.action == GLFW_PRESS) { // recalculate pantograph state (hujhujhuj) Train->change_pantograph_selection(1); Train->change_pantograph_selection(-1); // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const state{Train->mvPantographUnit->PantsValve.is_enabled || Train->mvPantographUnit->PantsValve.is_active}; // fallback for impulse switches if (state) { OnCommand_pantographlowerselected(Train, Command); } else { OnCommand_pantographraiseselected(Train, Command); } } else if (Command.action == GLFW_RELEASE) { // impulse switches return automatically to neutral position if (Train->m_controlmapper.contains("pantselectedoff_sw:")) { // two buttons setup if (Train->ggPantSelectedButton.type() != TGaugeType::toggle) { Train->mvOccupied->OperatePantographsValve(operation_t::enable_off); // visual feedback Train->ggPantSelectedButton.UpdateValue(0.0, Train->dsbSwitch); } if (Train->ggPantSelectedDownButton.type() != TGaugeType::toggle) { Train->mvOccupied->OperatePantographsValve(operation_t::disable_off); // visual feedback Train->ggPantSelectedDownButton.UpdateValue(0.0, Train->dsbSwitch); } } else { if (Train->ggPantSelectedButton.type() != TGaugeType::toggle) { // special case, just one impulse switch controlling both states // with neutral position mid-way Train->mvOccupied->OperatePantographsValve(operation_t::none); // visual feedback Train->ggPantSelectedButton.UpdateValue(0.5, Train->dsbSwitch); } } } } void TTrain::OnCommand_pantographraiseselected(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Command.action == GLFW_PRESS) { // raise selected Train->mvOccupied->OperatePantographsValve(Train->ggPantSelectedButton.type() != TGaugeType::toggle ? operation_t::enable_on : operation_t::enable); // visual feedback Train->ggPantSelectedButton.UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch OnCommand_pantographtoggleselected(Train, Command); } } void TTrain::OnCommand_pantographlowerselected(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Command.action == GLFW_PRESS) { // lower selected Train->mvOccupied->OperatePantographsValve(Train->ggPantSelectedDownButton.type() != TGaugeType::toggle ? operation_t::disable_on : operation_t::disable); // visual feedback if (Train->m_controlmapper.contains("pantselectedoff_sw:")) { // two button setup Train->ggPantSelectedDownButton.UpdateValue(1.0, Train->dsbSwitch); } else { // single button Train->ggPantSelectedButton.UpdateValue(0.0, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { // NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch OnCommand_pantographtoggleselected(Train, Command); } } void TTrain::update_pantograph_valves() { auto const &presets{mvOccupied->PantsPreset.first}; auto &selection{mvOccupied->PantsPreset.second[cab_to_end()]}; auto const preset{presets[selection] - '0'}; auto const swapends{cab_to_end() != end::front}; // check desired states for both pantographs; value: whether the pantograph should be raised auto const frontstate{preset & (swapends ? 2 : 1)}; auto const rearstate{preset & (swapends ? 1 : 2)}; mvOccupied->OperatePantographValve(end::front, (frontstate ? operation_t::enable : operation_t::disable)); mvOccupied->OperatePantographValve(end::rear, (rearstate ? operation_t::enable : operation_t::disable)); } void TTrain::change_pantograph_selection(int const Change) { auto const &presets{mvOccupied->PantsPreset.first}; auto &selection{mvOccupied->PantsPreset.second[cab_to_end()]}; auto const initialstate{selection}; selection = std::clamp(selection + Change, 0, std::max(presets.size() - 1, 0)); if (selection == initialstate) { return; } // no change, nothing to do // potentially adjust pantograph valves to match the new state if (false == m_controlmapper.contains("pantvalves_sw:")) { update_pantograph_valves(); } } void TTrain::OnCommand_pantographvalvesupdate(TTrain *Train, command_data const &Command) { bool hasSeparateSwitches = Train->m_controlmapper.contains("pantvalvesupdate_bt:") && Train->m_controlmapper.contains("pantvalvesoff_bt:"); if (Command.action == GLFW_REPEAT) { return; } if (Command.action == GLFW_PRESS) { if (hasSeparateSwitches) { // implement action Train->update_pantograph_valves(); // visual feedback Train->ggPantValvesUpdate.UpdateValue(1.0, Train->dsbSwitch); } // Old logic to maintain compatibility else { Train->update_pantograph_valves(); Train->ggPantValvesButton.UpdateValue(1.0, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { // visual feedback // NOTE: pantvalves_sw: is a specialized button, with no toggle behavior support if (hasSeparateSwitches) Train->ggPantValvesUpdate.UpdateValue(0.5, Train->dsbSwitch); // Old logic to maintain compatibility else Train->ggPantValvesButton.UpdateValue(0.5, Train->dsbSwitch); } } void TTrain::OnCommand_pantographvalvesoff(TTrain *Train, command_data const &Command) { bool hasSeparateSwitches = Train->m_controlmapper.contains("pantvalvesupdate_bt:") && Train->m_controlmapper.contains("pantvalvesoff_bt:"); if (Command.action == GLFW_REPEAT) { return; } if (Command.action == GLFW_PRESS) { // implement action Train->mvOccupied->OperatePantographValve(end::front, operation_t::disable); Train->mvOccupied->OperatePantographValve(end::rear, operation_t::disable); // visual feedback if (hasSeparateSwitches) Train->ggPantValvesOff.UpdateValue(1.0, Train->dsbSwitch); else Train->ggPantValvesButton.UpdateValue(0.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // visual feedback // NOTE: pantvalves_sw: is a speciali zed button, with no toggle behavior support if (hasSeparateSwitches) Train->ggPantValvesOff.UpdateValue(0.f, Train->dsbSwitch); else Train->ggPantValvesButton.UpdateValue(0.5, Train->dsbSwitch); } } void TTrain::OnCommand_pantographcompressorvalvetoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only react to press if (Train->mvControlled->bPantKurek3 == false) { // connect pantographs with primary tank OnCommand_pantographcompressorvalveenable(Train, Command); } else { // connect pantograps with pantograph compressor OnCommand_pantographcompressorvalvedisable(Train, Command); } } } void TTrain::OnCommand_pantographcompressorvalveenable(TTrain *Train, command_data const &Command) { auto const valveispresent{(Train->ggPantCompressorValve.SubModel != nullptr) || ((Train->mvOccupied == Train->mvPantographUnit) && (Train->iCabn == 0))}; if (false == valveispresent) { // tylko w maszynowym, unless actual device is present return; } if (Command.action == GLFW_PRESS) { // only react to press // connect pantographs with primary tank Train->mvControlled->bPantKurek3 = true; // visual feedback: Train->ggPantCompressorValve.UpdateValue(0.0); } } void TTrain::OnCommand_pantographcompressorvalvedisable(TTrain *Train, command_data const &Command) { auto const valveispresent{(Train->ggPantCompressorValve.SubModel != nullptr) || ((Train->mvOccupied == Train->mvPantographUnit) && (Train->iCabn == 0))}; if (false == valveispresent) { // tylko w maszynowym, unless actual device is present return; } if (Command.action == GLFW_PRESS) { // only react to press // connect pantograps with pantograph compressor Train->mvControlled->bPantKurek3 = false; // visual feedback: Train->ggPantCompressorValve.UpdateValue(1.0); } } void TTrain::OnCommand_pantographcompressoractivate(TTrain *Train, command_data const &Command) { // tylko w maszynowym, unless actual device is present auto const switchispresent{(Train->m_controlmapper.contains("pantcompressor_sw:")) || ((Train->mvOccupied == Train->mvPantographUnit) && (Train->iCabn == 0))}; if (false == switchispresent) { return; } if (Command.action != GLFW_RELEASE) { // press or hold to activate if ((Train->mvPantographUnit->PantPress < 4.8) && (true == Train->mvPantographUnit->Power24vIsAvailable)) { // needs live power source and low enough pressure to work Train->mvPantographUnit->PantCompFlag = true; } // visual feedback Train->ggPantCompressorButton.UpdateValue(1.0); } else { // release to disable Train->mvPantographUnit->PantCompFlag = false; // visual feedback Train->ggPantCompressorButton.UpdateValue(0.0); } } void TTrain::OnCommand_linebreakertoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // press or hold... if (Train->m_linebreakerstate == 0) { // ...to close the circuit // NOTE: bit of a dirty shortcut here OnCommand_linebreakerclose(Train, Command); } else if (Train->m_linebreakerstate == 1) { // ...to open the circuit OnCommand_linebreakeropen(Train, Command); } } else if (Command.action == GLFW_RELEASE) { // release... if ((Train->ggMainOnButton.SubModel != nullptr) || (Train->ggMainButton.type() != TGaugeType::toggle)) { // only impulse switches react to release events // NOTE: we presume dedicated state switch is of impulse type if (Train->m_linebreakerstate == 0) { // ...after opening circuit, or holding for too short time to close it OnCommand_linebreakeropen(Train, Command); } else { // ...after closing the circuit // NOTE: bit of a dirty shortcut here OnCommand_linebreakerclose(Train, Command); } } // HACK: ignition key ignores lack of submodel, so we can start vehicles without any modeled controls Train->ggIgnitionKey.UpdateValue(0.0); } } void TTrain::OnCommand_linebreakeropen(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback if (Train->ggMainOffButton.SubModel != nullptr) { Train->ggMainOffButton.UpdateValue(1.0, Train->dsbSwitch); } else if (Train->ggMainButton.SubModel != nullptr) { Train->ggMainButton.UpdateValue(0.0, Train->dsbSwitch); } else if (Train->ggMainOnButton.SubModel != nullptr) { // NOTE: legacy behaviour, for vehicles equipped only with impulse close switch // it doesn't make any real sense to animate this one, but some people can't get over how there's no visual reaction to their keypress Train->ggMainOnButton.UpdateValue(1.0, Train->dsbSwitch); return; } // play sound immediately when the switch is hit, not after release Train->fMainRelayTimer = 0.0f; if (Train->m_linebreakerstate == 0) { return; } // already in the desired state if (true == Train->mvControlled->MainSwitch(false)) { Train->m_linebreakerstate = 0; } } else if (Command.action == GLFW_RELEASE) { // visual feedback // we don't exactly know which of the two buttons was used, so reset both // for setup with two separate swiches if (Train->ggMainOnButton.SubModel != nullptr) { Train->ggMainOnButton.UpdateValue(0.0, Train->dsbSwitch); } if (Train->ggMainOffButton.SubModel != nullptr) { Train->ggMainOffButton.UpdateValue(0.0, Train->dsbSwitch); } // and the two-state switch too, for good measure if (Train->ggMainButton.SubModel != nullptr) { Train->ggMainButton.UpdateValue((Train->ggMainButton.type() != TGaugeType::toggle ? 0.5 : 0.0), Train->dsbSwitch); } } } void TTrain::OnCommand_linebreakerclose(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback if (Train->ggMainOnButton.SubModel != nullptr) { // two separate switches to close and break the circuit Train->ggMainOnButton.UpdateValue(1.0, Train->dsbSwitch); } else if (Train->ggMainButton.SubModel != nullptr) { // single two-state switch Train->ggMainButton.UpdateValue(1.0, Train->dsbSwitch); } else { // no switch capable of doing the job // HACK: ignition key ignores lack of submodel, so we can start vehicles without any modeled controls Train->ggIgnitionKey.UpdateValue(1.0); return; } // the actual closing of the line breaker is handled in the train update routine } else if (Command.action == GLFW_RELEASE) { // visual feedback if (Train->ggMainOnButton.SubModel != nullptr) { // setup with two separate switches Train->ggMainOnButton.UpdateValue(0.0, Train->dsbSwitch); } else if (Train->ggMainButton.SubModel != nullptr) { if (Train->ggMainButton.type() != TGaugeType::toggle) { Train->ggMainButton.UpdateValue(0.5, Train->dsbSwitch); } } if (Train->m_linebreakerstate == 1) { return; } // already in the desired state if (Train->m_linebreakerstate == 2) { // we don't need to start the diesel twice, but the other types (with impulse switch setup) still need to be launched // NOTE: this behaviour should depend on MainOnButton presence and type_delayed // TODO: change it when/if vehicle definition files get their proper switch types if (Train->mvControlled->EngineType == TEngineType::ElectricSeriesMotor) { // try to finalize state change of the line breaker, set the state based on the outcome Train->m_linebreakerstate = (Train->mvControlled->MainSwitch(true) ? 1 : 0); } } // on button release reset the closing timer Train->fMainRelayTimer = 0.0f; } } void TTrain::OnCommand_fuelpumptoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggFuelPumpButton.type() == TGaugeType::push) { // impulse switch // currently there's no off button so we always try to turn it on OnCommand_fuelpumpenable(Train, Command); } else { // two-state switch if (Command.action == GLFW_RELEASE) { return; } if (false == Train->mvControlled->FuelPump.is_enabled) { // turn on OnCommand_fuelpumpenable(Train, Command); } else { // turn off OnCommand_fuelpumpdisable(Train, Command); } } } void TTrain::OnCommand_fuelpumpenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggFuelPumpButton.type() == TGaugeType::push) { // impulse switch if (Command.action == GLFW_PRESS) { // visual feedback Train->ggFuelPumpButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvControlled->FuelPumpSwitch(true); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggFuelPumpButton.UpdateValue(0.0, Train->dsbSwitch); Train->mvControlled->FuelPumpSwitch(false); } } else { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { // visual feedback Train->ggFuelPumpButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvControlled->FuelPumpSwitch(true); Train->mvControlled->FuelPumpSwitchOff(false); } } } void TTrain::OnCommand_fuelpumpdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggFuelPumpButton.type() == TGaugeType::push) { // impulse switch // currently there's no disable return type switch return; } else { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { // visual feedback Train->ggFuelPumpButton.UpdateValue(0.0, Train->dsbSwitch); Train->mvControlled->FuelPumpSwitch(false); Train->mvControlled->FuelPumpSwitchOff(true); } } } void TTrain::OnCommand_oilpumptoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggOilPumpButton.type() == TGaugeType::push) { // impulse switch // currently there's no off button so we always try to turn it on OnCommand_oilpumpenable(Train, Command); } else { // two-state switch if (Command.action == GLFW_RELEASE) { return; } if (false == Train->mvControlled->OilPump.is_enabled) { // turn on OnCommand_oilpumpenable(Train, Command); } else { // turn off OnCommand_oilpumpdisable(Train, Command); } } } void TTrain::OnCommand_oilpumpenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggOilPumpButton.type() == TGaugeType::push) { // impulse switch if (Command.action == GLFW_PRESS) { // visual feedback Train->ggOilPumpButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvControlled->OilPumpSwitch(true); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggOilPumpButton.UpdateValue(0.0, Train->dsbSwitch); Train->mvControlled->OilPumpSwitch(false); } } else { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { // visual feedback Train->ggOilPumpButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvControlled->OilPumpSwitch(true); Train->mvControlled->OilPumpSwitchOff(false); } } } void TTrain::OnCommand_oilpumpdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggOilPumpButton.type() == TGaugeType::push) { // impulse switch // currently there's no disable return type switch return; } else { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { // visual feedback Train->ggOilPumpButton.UpdateValue(0.0, Train->dsbSwitch); Train->mvControlled->OilPumpSwitch(false); Train->mvControlled->OilPumpSwitchOff(true); } } } void TTrain::OnCommand_waterheaterbreakertoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->mvControlled->WaterHeater.breaker) { // turn on OnCommand_waterheaterbreakerclose(Train, Command); } else { // turn off OnCommand_waterheaterbreakeropen(Train, Command); } } } void TTrain::OnCommand_waterheaterbreakerclose(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterHeaterBreakerButton.UpdateValue(1.0, Train->dsbSwitch); if (true == Train->mvControlled->WaterHeater.breaker) { return; } // already enabled Train->mvControlled->WaterHeaterBreakerSwitch(true); } } void TTrain::OnCommand_waterheaterbreakeropen(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterHeaterBreakerButton.UpdateValue(0.0, Train->dsbSwitch); if (false == Train->mvControlled->WaterHeater.breaker) { return; } // already enabled Train->mvControlled->WaterHeaterBreakerSwitch(false); } } void TTrain::OnCommand_waterheatertoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->mvControlled->WaterHeater.is_enabled) { // turn on OnCommand_waterheaterenable(Train, Command); } else { // turn off OnCommand_waterheaterdisable(Train, Command); } } } void TTrain::OnCommand_waterheaterenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterHeaterButton.UpdateValue(1.0, Train->dsbSwitch); if (true == Train->mvControlled->WaterHeater.is_enabled) { return; } // already enabled Train->mvControlled->WaterHeaterSwitch(true); } } void TTrain::OnCommand_waterheaterdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterHeaterButton.UpdateValue(0.0, Train->dsbSwitch); if (false == Train->mvControlled->WaterHeater.is_enabled) { return; } // already disabled Train->mvControlled->WaterHeaterSwitch(false); } } void TTrain::OnCommand_waterpumpbreakertoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->mvControlled->WaterPump.breaker) { // turn on OnCommand_waterpumpbreakerclose(Train, Command); } else { // turn off OnCommand_waterpumpbreakeropen(Train, Command); } } } void TTrain::OnCommand_waterpumpbreakerclose(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterPumpBreakerButton.UpdateValue(1.0, Train->dsbSwitch); if (true == Train->mvControlled->WaterPump.breaker) { return; } // already enabled Train->mvControlled->WaterPumpBreakerSwitch(true); } } void TTrain::OnCommand_waterpumpbreakeropen(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterPumpBreakerButton.UpdateValue(0.0, Train->dsbSwitch); if (false == Train->mvControlled->WaterPump.breaker) { return; } // already enabled Train->mvControlled->WaterPumpBreakerSwitch(false); } } void TTrain::OnCommand_waterpumptoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggWaterPumpButton.type() == TGaugeType::push) { // impulse switch // currently there's no off button so we always try to turn it on OnCommand_waterpumpenable(Train, Command); } else { // two-state switch if (Command.action == GLFW_RELEASE) { return; } if (false == Train->mvControlled->WaterPump.is_enabled) { // turn on OnCommand_waterpumpenable(Train, Command); } else { // turn off OnCommand_waterpumpdisable(Train, Command); } } } void TTrain::OnCommand_waterpumpenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggWaterPumpButton.type() == TGaugeType::push) { // impulse switch if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterPumpButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvControlled->WaterPumpSwitch(true); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggWaterPumpButton.UpdateValue(0.0, Train->dsbSwitch); Train->mvControlled->WaterPumpSwitch(false); } } else { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterPumpButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvControlled->WaterPumpSwitch(true); Train->mvControlled->WaterPumpSwitchOff(false); } } } void TTrain::OnCommand_waterpumpdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggWaterPumpButton.type() == TGaugeType::push) { // impulse switch // currently there's no disable return type switch return; } else { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterPumpButton.UpdateValue(0.0, Train->dsbSwitch); Train->mvControlled->WaterPumpSwitch(false); Train->mvControlled->WaterPumpSwitchOff(true); } } } void TTrain::OnCommand_watercircuitslinktoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->mvControlled->WaterCircuitsLink) { // turn on OnCommand_watercircuitslinkenable(Train, Command); } else { // turn off OnCommand_watercircuitslinkdisable(Train, Command); } } } void TTrain::OnCommand_watercircuitslinkenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterCircuitsLinkButton.UpdateValue(1.0, Train->dsbSwitch); if (true == Train->mvControlled->WaterCircuitsLink) { return; } // already enabled Train->mvControlled->WaterCircuitsLinkSwitch(true); } } void TTrain::OnCommand_watercircuitslinkdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggWaterCircuitsLinkButton.UpdateValue(0.0, Train->dsbSwitch); if (false == Train->mvControlled->WaterCircuitsLink) { return; } // already disabled Train->mvControlled->WaterCircuitsLinkSwitch(false); } } void TTrain::OnCommand_convertertoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const overloadrelayisopen{(Train->Dynamic()->Mechanik != nullptr ? Train->Dynamic()->Mechanik->IsAnyConverterOverloadRelayOpen : Train->mvOccupied->ConvOvldFlag)}; if (Train->mvOccupied->ConvSwitchType != "impulse" ? Train->ggConverterButton.GetValue() < 0.5 : ((false == Train->mvOccupied->Power110vIsAvailable) && (false == overloadrelayisopen))) { // turn on OnCommand_converterenable(Train, Command); } else { // turn off OnCommand_converterdisable(Train, Command); } } else if (Command.action == GLFW_RELEASE) { // on button release... if (Train->mvOccupied->ConvSwitchType == "impulse") { // ...return switches to start position if applicable Train->ggConverterButton.UpdateValue(0.0, Train->dsbSwitch); Train->ggConverterOffButton.UpdateValue(0.0, Train->dsbSwitch); } } } void TTrain::OnCommand_converterenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggConverterButton.UpdateValue(1.0, Train->dsbSwitch); // impulse type switch has no effect if there's no power // NOTE: this is most likely setup wrong, but the whole thing is smoke and mirrors anyway if ((Train->mvOccupied->ConvSwitchType != "impulse") || (Train->mvControlled->Mains)) { // won't start if the line breaker button is still held Train->mvOccupied->ConverterSwitch(true); } } else if (Command.action == GLFW_RELEASE) { // potentially reset impulse switch position, using shared code branch OnCommand_convertertoggle(Train, Command); } } void TTrain::OnCommand_converterdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggConverterButton.UpdateValue(0.0, Train->dsbSwitch); if (Train->ggConverterOffButton.SubModel != nullptr) { Train->ggConverterOffButton.UpdateValue(1.0, Train->dsbSwitch); } Train->mvOccupied->ConverterSwitch(false); } else if (Command.action == GLFW_RELEASE) { // potentially reset impulse switch position, using shared code branch OnCommand_convertertoggle(Train, Command); } } void TTrain::OnCommand_convertertogglelocal(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->ConverterStart == start_t::automatic) { // let the automatic thing do its automatic thing... return; } if (Train->ggConverterLocalButton.SubModel == nullptr) { return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if ((false == Train->mvOccupied->ConverterAllowLocal) && (Train->ggConverterLocalButton.GetValue() < 0.5)) { // turn on // visual feedback Train->ggConverterLocalButton.UpdateValue(1.0, Train->dsbSwitch); // effect Train->mvOccupied->ConverterAllowLocal = true; /* if( true == Train->mvControlled->ConverterSwitch( true, range::local ) ) { // side effects // control the compressor, if it's paired with the converter if( Train->mvControlled->CompressorPower == 2 ) { // hunter-091012: tak jest poprawnie Train->mvControlled->CompressorSwitch( true, range::local ); } } */ } else { // turn off // visual feedback Train->ggConverterLocalButton.UpdateValue(0.0, Train->dsbSwitch); // effect Train->mvOccupied->ConverterAllowLocal = false; /* if( true == Train->mvControlled->ConverterSwitch( false, range::local ) ) { // side effects // control the compressor, if it's paired with the converter if( Train->mvControlled->CompressorPower == 2 ) { // hunter-091012: tak jest poprawnie Train->mvControlled->CompressorSwitch( false, range::local ); } // if there's no (low voltage) power source left, drop pantographs if( false == Train->mvControlled->Battery ) { Train->mvControlled->PantFront( false, range::local ); Train->mvControlled->PantRear( false, range::local ); } } */ } } } void TTrain::OnCommand_converteroverloadrelayreset(TTrain *Train, command_data const &Command) { if (Train->ggConverterFuseButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Converter Overload Relay Reset button is missing, or wasn't defined"); } // return; } if (Command.action == GLFW_PRESS) { // visual feedback Train->ggConverterFuseButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvControlled->RelayReset(relay_t::primaryconverteroverload); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggConverterFuseButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_compressortoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const compressorisenabled{(Train->Dynamic()->Mechanik ? Train->Dynamic()->Mechanik->IsAnyCompressorEnabled : Train->mvOccupied->CompressorAllow)}; if (false == compressorisenabled) { // turn on OnCommand_compressorenable(Train, Command); } else { // turn off OnCommand_compressordisable(Train, Command); } } /* // disabled because we don't have yet support for compressor switch type definition else if( Command.action == GLFW_RELEASE ) { // on button release... if( Train->mvOccupied->CompSwitchType == "impulse" ) { // ...return switches to start position if applicable Train->ggCompressorButton.UpdateValue( 0.0, Train->dsbSwitch ); Train->ggCompressorOffButton.UpdateValue( 0.0, Train->dsbSwitch ); } } */ } void TTrain::OnCommand_compressorenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // visual feedback Train->ggCompressorButton.UpdateValue(1.0, Train->dsbSwitch); // impulse type switch has no effect if there's no power // NOTE: this is most likely setup wrong, but the whole thing is smoke and mirrors anyway // if( ( Train->mvOccupied->CompSwitchType != "impulse" ) // || ( Train->mvControlled->Mains ) ) { Train->mvOccupied->CompressorSwitch(true); // } } else if (Command.action == GLFW_RELEASE) { // potentially reset impulse switch position, using shared code branch OnCommand_compressortoggle(Train, Command); } } void TTrain::OnCommand_compressordisable(TTrain *Train, command_data const &Command) { if (Train->mvControlled->CompressorPower >= 2) { return; } if (Command.action == GLFW_PRESS) { // visual feedback Train->ggCompressorButton.UpdateValue(0.0, Train->dsbSwitch); /* if( Train->ggCompressorOffButton.SubModel != nullptr ) { Train->ggCompressorOffButton.UpdateValue( 1.0, Train->dsbSwitch ); } */ Train->mvOccupied->CompressorSwitch(false); } else if (Command.action == GLFW_RELEASE) { // potentially reset impulse switch position, using shared code branch OnCommand_compressortoggle(Train, Command); } } void TTrain::OnCommand_compressortogglelocal(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->CompressorPower >= 2) { return; } if (Train->ggCompressorLocalButton.SubModel == nullptr) { return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->mvOccupied->CompressorAllowLocal) { // turn on // visual feedback Train->ggCompressorLocalButton.UpdateValue(1.0, Train->dsbSwitch); // effect Train->mvOccupied->CompressorAllowLocal = true; } else { // turn off // visual feedback Train->ggCompressorLocalButton.UpdateValue(0.0, Train->dsbSwitch); // effect Train->mvOccupied->CompressorAllowLocal = false; } } } void TTrain::OnCommand_compressorpresetactivatenext(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->CompressorListPosNo == 0) { return; } if (Command.action == GLFW_REPEAT) { return; } if (Train->ggCompressorListButton.type() == TGaugeType::push) { // impulse switch if (Train->mvOccupied->CompressorListPosNo < Train->mvOccupied->CompressorListDefPos + 1) { return; } Train->mvOccupied->ChangeCompressorPreset((Command.action == GLFW_PRESS ? Train->mvOccupied->CompressorListDefPos + 1 : Train->mvOccupied->CompressorListDefPos)); // visual feedback Train->ggCompressorListButton.UpdateValue(Train->mvOccupied->CompressorListPos - 1, Train->dsbSwitch); } else { // multi-state switch if (Command.action == GLFW_RELEASE) { return; } if ((Train->mvOccupied->CompressorListPos < Train->mvOccupied->CompressorListPosNo) || (true == Train->mvOccupied->CompressorListWrap)) { // active light preset is stored as value in range 1-LigthPosNo Train->mvOccupied->ChangeCompressorPreset((Train->mvOccupied->CompressorListPos < Train->mvOccupied->CompressorListPosNo ? Train->mvOccupied->CompressorListPos + 1 : 1)); // wrap mode // visual feedback Train->ggCompressorListButton.UpdateValue(Train->mvOccupied->CompressorListPos - 1, Train->dsbSwitch); } } } void TTrain::OnCommand_compressorpresetactivateprevious(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->CompressorListPosNo == 0) { return; } if (Command.action != GLFW_PRESS) { return; } // one change per key press if (Train->ggCompressorListButton.type() == TGaugeType::push) { // impulse switch toggles only between positions 'default' and 'default+1' return; } if ((Train->mvOccupied->CompressorListPos > 1) || (true == Train->mvOccupied->CompressorListWrap)) { // active light preset is stored as value in range 1-LigthPosNo Train->mvOccupied->ChangeCompressorPreset((Train->mvOccupied->CompressorListPos > 1 ? Train->mvOccupied->CompressorListPos - 1 : Train->mvOccupied->CompressorListPosNo)); // wrap mode // visual feedback if (Train->ggCompressorListButton.SubModel != nullptr) { Train->ggCompressorListButton.UpdateValue(Train->mvOccupied->CompressorListPos - 1, Train->dsbSwitch); } } } void TTrain::OnCommand_compressorpresetactivatedefault(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->CompressorListPosNo == 0) { return; } if (Command.action != GLFW_PRESS) { return; } // one change per key press Train->mvOccupied->ChangeCompressorPreset(Train->mvOccupied->CompressorListDefPos); // visual feedback if (Train->ggCompressorListButton.SubModel != nullptr) { Train->ggCompressorListButton.UpdateValue(Train->mvOccupied->CompressorListPos - 1, Train->dsbSwitch); } } void TTrain::OnCommand_motorblowerstogglefront(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggMotorBlowersFrontButton.type() == TGaugeType::push) { // impulse switch // currently there's no off button so we always try to turn it on OnCommand_motorblowersenablefront(Train, Command); } else { // two-state switch if (Command.action == GLFW_RELEASE) { return; } if (false == Train->mvControlled->MotorBlowers[end::front].is_enabled) { // turn on OnCommand_motorblowersenablefront(Train, Command); } else { // turn off OnCommand_motorblowersdisablefront(Train, Command); } } } void TTrain::OnCommand_motorblowersenablefront(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggMotorBlowersFrontButton.type() == TGaugeType::push) { // impulse switch if (Command.action == GLFW_PRESS) { // visual feedback Train->ggMotorBlowersFrontButton.UpdateValue(1.f, Train->dsbSwitch); Train->mvControlled->MotorBlowersSwitch(true, end::front); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggMotorBlowersFrontButton.UpdateValue(0.f, Train->dsbSwitch); Train->mvControlled->MotorBlowersSwitch(false, end::front); } } else { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { // visual feedback Train->ggMotorBlowersFrontButton.UpdateValue(1.f, Train->dsbSwitch); Train->mvControlled->MotorBlowersSwitch(true, end::front); Train->mvControlled->MotorBlowersSwitchOff(false, end::front); } } } void TTrain::OnCommand_motorblowersdisablefront(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggMotorBlowersFrontButton.type() == TGaugeType::push) { // impulse switch // currently there's no disable return type switch return; } else { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { // visual feedback Train->ggMotorBlowersFrontButton.UpdateValue(0.f, Train->dsbSwitch); Train->mvControlled->MotorBlowersSwitch(false, end::front); Train->mvControlled->MotorBlowersSwitchOff(true, end::front); } } } void TTrain::OnCommand_motorblowerstogglerear(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggMotorBlowersRearButton.type() == TGaugeType::push) { // impulse switch // currently there's no off button so we always try to turn it on OnCommand_motorblowersenablerear(Train, Command); } else { // two-state switch if (Command.action == GLFW_RELEASE) { return; } if (false == Train->mvControlled->MotorBlowers[end::rear].is_enabled) { // turn on OnCommand_motorblowersenablerear(Train, Command); } else { // turn off OnCommand_motorblowersdisablerear(Train, Command); } } } void TTrain::OnCommand_motorblowersenablerear(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggMotorBlowersRearButton.type() == TGaugeType::push) { // impulse switch if (Command.action == GLFW_PRESS) { // visual feedback Train->ggMotorBlowersRearButton.UpdateValue(1.f, Train->dsbSwitch); Train->mvControlled->MotorBlowersSwitch(true, end::rear); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggMotorBlowersRearButton.UpdateValue(0.f, Train->dsbSwitch); Train->mvControlled->MotorBlowersSwitch(false, end::rear); } } else { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { // visual feedback Train->ggMotorBlowersRearButton.UpdateValue(1.f, Train->dsbSwitch); Train->mvControlled->MotorBlowersSwitch(true, end::rear); Train->mvControlled->MotorBlowersSwitchOff(false, end::rear); } } } void TTrain::OnCommand_motorblowersdisablerear(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggMotorBlowersRearButton.type() == TGaugeType::push) { // impulse switch // currently there's no disable return type switch return; } else { // two-state switch, only cares about press events if (Command.action == GLFW_PRESS) { // visual feedback Train->ggMotorBlowersRearButton.UpdateValue(0.f, Train->dsbSwitch); Train->mvControlled->MotorBlowersSwitch(false, end::rear); Train->mvControlled->MotorBlowersSwitchOff(true, end::rear); } } } void TTrain::OnCommand_motorblowersdisableall(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (Train->ggMotorBlowersAllOffButton.type() == TGaugeType::push) { // impulse switch if (Command.action == GLFW_PRESS) { // visual feedback Train->ggMotorBlowersAllOffButton.UpdateValue(1.f, Train->dsbSwitch); Train->mvControlled->MotorBlowersSwitchOff(true, end::front); Train->mvControlled->MotorBlowersSwitchOff(true, end::rear); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggMotorBlowersAllOffButton.UpdateValue(0.f, Train->dsbSwitch); Train->mvControlled->MotorBlowersSwitchOff(false, end::front); Train->mvControlled->MotorBlowersSwitchOff(false, end::rear); } } else { // two-state switch, only cares about press events // NOTE: generally this switch doesn't come in two-state form if (Command.action == GLFW_PRESS) { if (Train->ggMotorBlowersAllOffButton.GetDesiredValue() < 0.5f) { // switch is off, activate Train->mvControlled->MotorBlowersSwitchOff(true, end::front); Train->mvControlled->MotorBlowersSwitchOff(true, end::rear); // visual feedback Train->ggMotorBlowersRearButton.UpdateValue(1.f, Train->dsbSwitch); } else { // deactivate Train->mvControlled->MotorBlowersSwitchOff(false, end::front); Train->mvControlled->MotorBlowersSwitchOff(false, end::rear); // visual feedback Train->ggMotorBlowersRearButton.UpdateValue(0.f, Train->dsbSwitch); } } } } void TTrain::OnCommand_coolingfanstoggle(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_PRESS) { return; } Train->mvControlled->RVentForceOn = (!Train->mvControlled->RVentForceOn); } void TTrain::OnCommand_motorconnectorsopen(TTrain *Train, command_data const &Command) { // TODO: don't rely on presense of 3d model to determine presence of the switch if (Train->ggStLinOffButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Open Motor Power Connectors button is missing, or wasn't defined"); } return; } // HACK: because we don't have modeled actual circuits this is a simplification of the real mechanics // namely, pressing the button will flip it in the entire unit, which isn't exactly physically possible if (Command.action == GLFW_PRESS) { // button works while it's held down but we can ignore repeats if (false == Train->mvControlled->StLinSwitchOff) { // open the connectors // visual feedback Train->ggStLinOffButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvControlled->StLinSwitchOff = true; Train->set_paired_open_motor_connectors_button(true); } else { // potentially close the connectors OnCommand_motorconnectorsclose(Train, Command); } } else if (Command.action == GLFW_RELEASE) { // button released if (Train->mvControlled->StLinSwitchType != "toggle") { // default button type (impulse) ceases its work on button release // visual feedback Train->ggStLinOffButton.UpdateValue(0.0, Train->dsbSwitch); Train->mvControlled->StLinSwitchOff = false; Train->set_paired_open_motor_connectors_button(false); } } } void TTrain::OnCommand_motorconnectorsclose(TTrain *Train, command_data const &Command) { // TODO: don't rely on presense of 3d model to determine presence of the switch if (Train->ggStLinOffButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Open Motor Power Connectors button is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { if (Train->mvControlled->StLinSwitchType == "toggle") { // default type of button (impulse) has only one effect on press, but the toggle type can toggle the state // visual feedback Train->ggStLinOffButton.UpdateValue(0.0, Train->dsbSwitch); } if (false == Train->mvControlled->StLinSwitchOff) { return; } // already closed Train->mvControlled->StLinSwitchOff = false; Train->set_paired_open_motor_connectors_button(false); } } void TTrain::OnCommand_motordisconnect(TTrain *Train, command_data const &Command) { if ((Train->mvControlled->TrainType == dt_EZT ? (Train->mvControlled != Train->mvOccupied) : (Train->iCabn != 0))) { // tylko w maszynowym return; } if (Command.action == GLFW_PRESS) { Train->mvControlled->CutOffEngine(); } } void TTrain::OnCommand_motoroverloadrelaythresholdtoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if ((true == Train->mvControlled->ShuntModeAllow ? (false == Train->mvControlled->ShuntMode) : (false == Train->mvControlled->MotorOverloadRelayHighThreshold))) { // turn on OnCommand_motoroverloadrelaythresholdsethigh(Train, Command); } else { // turn off OnCommand_motoroverloadrelaythresholdsetlow(Train, Command); } } } void TTrain::OnCommand_motoroverloadrelaythresholdsetlow(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvControlled->CurrentSwitch(false); // visual feedback Train->ggMaxCurrentCtrl.UpdateValue(Train->mvControlled->MotorOverloadRelayHighThreshold ? 1 : 0, Train->dsbSwitch); } } void TTrain::OnCommand_motoroverloadrelaythresholdsethigh(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvControlled->CurrentSwitch(true); // visual feedback Train->ggMaxCurrentCtrl.UpdateValue(Train->mvControlled->MotorOverloadRelayHighThreshold ? 1 : 0, Train->dsbSwitch); } } void TTrain::OnCommand_motoroverloadrelayreset(TTrain *Train, command_data const &Command) { if (Train->ggFuseButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Motor Overload Relay Reset button is missing, or wasn't defined"); } // return; } if (Command.action == GLFW_PRESS) { // visual feedback Train->ggFuseButton.UpdateValue(1.0, Train->dsbSwitch); Train->mvControlled->FuseOn(); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggFuseButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_universalrelayreset(TTrain *Train, command_data const &Command) { auto const itemindex = static_cast(Command.command) - static_cast(user_command::universalrelayreset1); auto &item = Train->ggRelayResetButtons[itemindex]; // NOTE: relay reset switches are impulse-only if (Command.action == GLFW_PRESS) { Train->mvOccupied->UniversalResetButton(itemindex); // visual feedback item.UpdateValue(1.0); } else if (Command.action == GLFW_RELEASE) { // visual feedback item.UpdateValue(0.0); } } void TTrain::OnCommand_lightspresetactivatenext(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo == 0) { // no preset selector return; } if (Command.action != GLFW_PRESS) { // one change per key press return; } if ((Train->mvOccupied->LightsPos < Train->mvOccupied->LightsPosNo) || (true == Train->mvOccupied->LightsWrap)) { // active light preset is stored as value in range 1-LigthPosNo auto const restartcycle{Train->mvOccupied->LightsPos >= Train->mvOccupied->LightsPosNo}; Train->mvOccupied->LightsPos = (false == restartcycle ? Train->mvOccupied->LightsPos + 1 : 1); // wrap mode Train->Dynamic()->SetLights(); // visual feedback if (Train->ggLightsButton.SubModel != nullptr) { // HACK: skip submodel animation when restarting cycle, since it plays in the 'wrong' direction if (false == restartcycle) { Train->ggLightsButton.UpdateValue(Train->mvOccupied->LightsPos - 1, Train->dsbSwitch); } else { Train->ggLightsButton.PutValue(Train->mvOccupied->LightsPos - 1); } } } } void TTrain::OnCommand_lightspresetactivateprevious(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo == 0) { // no preset selector return; } if (Command.action != GLFW_PRESS) { // one change per key press return; } if ((Train->mvOccupied->LightsPos > 1) || (true == Train->mvOccupied->LightsWrap)) { // active light preset is stored as value in range 1-LigthPosNo auto const restartcycle{Train->mvOccupied->LightsPos <= 1}; Train->mvOccupied->LightsPos = (false == restartcycle ? Train->mvOccupied->LightsPos - 1 : Train->mvOccupied->LightsPosNo); // wrap mode Train->Dynamic()->SetLights(); // visual feedback if (Train->ggLightsButton.SubModel != nullptr) { // HACK: skip submodel animation when restarting cycle, since it plays in the 'wrong' direction if (false == restartcycle) { Train->ggLightsButton.UpdateValue(Train->mvOccupied->LightsPos - 1, Train->dsbSwitch); } else { Train->ggLightsButton.PutValue(Train->mvOccupied->LightsPos - 1); } } } } void TTrain::OnCommand_headlighttoggleleft(TTrain *Train, command_data const &Command) { auto const vehicleend{Train->cab_to_end()}; if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if ((Train->mvOccupied->iLights[vehicleend] & light::headlight_left) == 0) { // turn on OnCommand_headlightenableleft(Train, Command); } else { // turn off OnCommand_headlightdisableleft(Train, Command); } } } void TTrain::OnCommand_lightsset(TTrain *Train, command_data const &Command) { // set custom item in Lights inventory Train->mvOccupied->Lights[end::front][17] = Command.param1; Train->mvOccupied->Lights[end::rear][17] = Command.param2; Train->mvOccupied->LightsPos = 18; // nasza custom pozycja Train->Dynamic()->SetLights(); } void TTrain::OnCommand_headlightenableleft(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // visual feedback Train->ggLeftLightButton.UpdateValue(1.0, Train->dsbSwitch); // implementation auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::headlight_left) == 0) { Train->mvOccupied->iLights[vehicleend] ^= light::headlight_left; } // if the light is controlled by 3-way switch, disable marker light if (Train->ggLeftEndLightButton.SubModel == nullptr) { Train->mvOccupied->iLights[vehicleend] &= ~light::redmarker_left; } } } void TTrain::OnCommand_headlightdisableleft(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down int const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::headlight_left) == 0) { return; } // already disabled Train->mvOccupied->iLights[vehicleend] ^= light::headlight_left; // visual feedback Train->ggLeftLightButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_headlighttoggleright(TTrain *Train, command_data const &Command) { auto const vehicleend{Train->cab_to_end()}; if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if ((Train->mvOccupied->iLights[vehicleend] & light::headlight_right) == 0) { // turn on OnCommand_headlightenableright(Train, Command); } else { // turn off OnCommand_headlightdisableright(Train, Command); } } } void TTrain::OnCommand_headlightenableright(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // visual feedback Train->ggRightLightButton.UpdateValue(1.0, Train->dsbSwitch); // implementation auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::headlight_right) == 0) { Train->mvOccupied->iLights[vehicleend] ^= light::headlight_right; } // if the light is controlled by 3-way switch, disable marker light if (Train->ggRightEndLightButton.SubModel == nullptr) { Train->mvOccupied->iLights[vehicleend] &= ~light::redmarker_right; } } } void TTrain::OnCommand_headlightdisableright(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::headlight_right) == 0) { return; } // already disabled Train->mvOccupied->iLights[vehicleend] ^= light::headlight_right; // visual feedback Train->ggRightLightButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_headlighttoggleupper(TTrain *Train, command_data const &Command) { auto const vehicleend{Train->cab_to_end()}; if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if ((Train->mvOccupied->iLights[vehicleend] & light::headlight_upper) == 0) { // turn on OnCommand_headlightenableupper(Train, Command); } else { // turn off OnCommand_headlightdisableupper(Train, Command); } } } void TTrain::OnCommand_headlightenableupper(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::headlight_upper) != 0) { return; } // already enabled Train->mvOccupied->iLights[vehicleend] ^= light::headlight_upper; // visual feedback Train->ggUpperLightButton.UpdateValue(1.0, Train->dsbSwitch); } } void TTrain::OnCommand_headlightdisableupper(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::headlight_upper) == 0) { return; } // already disabled Train->mvOccupied->iLights[vehicleend] ^= light::headlight_upper; // visual feedback Train->ggUpperLightButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_redmarkertoggleleft(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::redmarker_left) == 0) { // turn on OnCommand_redmarkerenableleft(Train, Command); } else { // turn off OnCommand_redmarkerdisableleft(Train, Command); } } } void TTrain::OnCommand_redmarkerenableleft(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::redmarker_left) != 0) { return; } // already enabled Train->mvOccupied->iLights[vehicleend] ^= light::redmarker_left; // visual feedback if (Train->ggLeftEndLightButton.SubModel != nullptr) { Train->ggLeftEndLightButton.UpdateValue(1.0, Train->dsbSwitch); } else { // we interpret lack of dedicated switch as a sign the light is controlled with 3-way switch // this is crude, but for now will do Train->ggLeftLightButton.UpdateValue(-1.0, Train->dsbSwitch); // if the light is controlled by 3-way switch, disable the headlight Train->mvOccupied->iLights[vehicleend] &= ~light::headlight_left; } } } void TTrain::OnCommand_redmarkerdisableleft(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::redmarker_left) == 0) { return; } // already disabled Train->mvOccupied->iLights[vehicleend] ^= light::redmarker_left; // visual feedback if (Train->ggLeftEndLightButton.SubModel != nullptr) { Train->ggLeftEndLightButton.UpdateValue(0.0, Train->dsbSwitch); } else { // we interpret lack of dedicated switch as a sign the light is controlled with 3-way switch // this is crude, but for now will do Train->ggLeftLightButton.UpdateValue(0.0, Train->dsbSwitch); } } } void TTrain::OnCommand_redmarkertoggleright(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::redmarker_right) == 0) { // turn on OnCommand_redmarkerenableright(Train, Command); } else { // turn off OnCommand_redmarkerdisableright(Train, Command); } } } void TTrain::OnCommand_redmarkerenableright(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::redmarker_right) != 0) { return; } // already enabled Train->mvOccupied->iLights[vehicleend] ^= light::redmarker_right; // visual feedback if (Train->ggRightEndLightButton.SubModel != nullptr) { Train->ggRightEndLightButton.UpdateValue(1.0, Train->dsbSwitch); } else { // we interpret lack of dedicated switch as a sign the light is controlled with 3-way switch // this is crude, but for now will do Train->ggRightLightButton.UpdateValue(-1.0, Train->dsbSwitch); // if the light is controlled by 3-way switch, disable the headlight Train->mvOccupied->iLights[vehicleend] &= ~light::headlight_right; } } } void TTrain::OnCommand_redmarkerdisableright(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleend{Train->cab_to_end()}; if ((Train->mvOccupied->iLights[vehicleend] & light::redmarker_right) == 0) { return; } // already disabled Train->mvOccupied->iLights[vehicleend] ^= light::redmarker_right; // visual feedback if (Train->ggRightEndLightButton.SubModel != nullptr) { Train->ggRightEndLightButton.UpdateValue(0.0, Train->dsbSwitch); } else { // we interpret lack of dedicated switch as a sign the light is controlled with 3-way switch // this is crude, but for now will do Train->ggRightLightButton.UpdateValue(0.0, Train->dsbSwitch); } } } void TTrain::OnCommand_headlighttogglerearleft(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; // only reacting to press, so the switch doesn't flip back and forth if key is held down if ((Train->mvOccupied->iLights[vehicleotherend] & light::headlight_right) == 0) { OnCommand_headlightenablerearleft(Train, Command); } else { OnCommand_headlightdisablerearleft(Train, Command); } } } void TTrain::OnCommand_headlightenablerearleft(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; // already enabled if ((Train->mvOccupied->iLights[vehicleotherend] & light::headlight_right) == 0) { // turn on Train->mvOccupied->iLights[vehicleotherend] ^= light::headlight_right; // visual feedback Train->ggRearLeftLightButton.UpdateValue(1.0, Train->dsbSwitch); } } } void TTrain::OnCommand_headlightdisablerearleft(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; // already disabled if ((Train->mvOccupied->iLights[vehicleotherend] & light::headlight_right) == 0) { return; } // turn off Train->mvOccupied->iLights[vehicleotherend] ^= light::headlight_right; // visual feedback Train->ggRearLeftLightButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_headlighttogglerearright(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; // only reacting to press, so the switch doesn't flip back and forth if key is held down if ((Train->mvOccupied->iLights[vehicleotherend] & light::headlight_left) == 0) { OnCommand_headlightenablerearright(Train, Command); } else { OnCommand_headlightdisablerearright(Train, Command); } } } void TTrain::OnCommand_headlightenablerearright(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; if ((Train->mvOccupied->iLights[vehicleotherend] & light::headlight_left) == 0) { // turn on Train->mvOccupied->iLights[vehicleotherend] ^= light::headlight_left; // visual feedback Train->ggRearRightLightButton.UpdateValue(1.0, Train->dsbSwitch); } } } void TTrain::OnCommand_headlightdisablerearright(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; // already disabled if ((Train->mvOccupied->iLights[vehicleotherend] & light::headlight_left) == 0) { return; } // turn off Train->mvOccupied->iLights[vehicleotherend] ^= light::headlight_left; // visual feedback Train->ggRearRightLightButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_headlighttogglerearupper(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; if ((Train->mvOccupied->iLights[vehicleotherend] & light::headlight_upper) == 0) { OnCommand_headlightenablerearupper(Train, Command); } else { OnCommand_headlightdisablerearupper(Train, Command); } } } void TTrain::OnCommand_headlightenablerearupper(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; if ((Train->mvOccupied->iLights[vehicleotherend] & light::headlight_upper) == 0) { // turn on Train->mvOccupied->iLights[vehicleotherend] ^= light::headlight_upper; // visual feedback Train->ggRearUpperLightButton.UpdateValue(1.0, Train->dsbSwitch); } } } void TTrain::OnCommand_headlightdisablerearupper(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; // already disabled? if ((Train->mvOccupied->iLights[vehicleotherend] & light::headlight_upper) == 0) { return; } // turn off Train->mvOccupied->iLights[vehicleotherend] ^= light::headlight_upper; // visual feedback Train->ggRearUpperLightButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_modernlightdimmerincrease(TTrain *Train, command_data const &Command) { if (!Train->mvOccupied->enableModernDimmer) return; // if modern dimmer is disabled, skip entire command if (Command.action == GLFW_PRESS) { // update modern dimmer state auto &dimPos = Train->mvOccupied->modernDimmerPosition; auto dimCount = Train->mvOccupied->dimPositions.size(); if (dimPos + 1 < dimCount) dimPos++; else if (Train->mvOccupied->modernDimmerCanCycle) dimPos = 0; // return to 0 else return; // już na minimum i nie można cyklicznie // update lightning // Train->Dynamic()->SetLights(); Train->Dynamic()->SetLightDimmings(); // visual feedback if (Train->ggModernLightDimSw.SubModel != nullptr) Train->ggModernLightDimSw.UpdateValue(dimPos, Train->dsbSwitch); } } void TTrain::OnCommand_modernlightdimmerdecrease(TTrain *Train, command_data const &Command) { if (!Train->mvOccupied->enableModernDimmer) return; // jeśli dimmer jest wyłączony, olewamy if (Command.action == GLFW_PRESS) { auto &dimPos = Train->mvOccupied->modernDimmerPosition; auto dimCount = Train->mvOccupied->dimPositions.size(); if (dimCount == 0) return; if (dimPos > 0) dimPos--; else if (Train->mvOccupied->modernDimmerCanCycle) dimPos = static_cast(dimCount - 1); // ostatnia pozycja else return; // już na minimum i nie można cyklicznie // Train->Dynamic()->SetLights(); Train->Dynamic()->SetLightDimmings(); if (Train->ggModernLightDimSw.SubModel != nullptr) Train->ggModernLightDimSw.UpdateValue(dimPos, Train->dsbSwitch); } } void TTrain::OnCommand_redmarkertogglerearleft(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; // only reacting to press, so the switch doesn't flip back and forth if key is held down if ((Train->mvOccupied->iLights[vehicleotherend] & light::redmarker_right) == 0) { OnCommand_redmarkerenablerearleft(Train, Command); } else { OnCommand_redmarkerdisablerearleft(Train, Command); } } } void TTrain::OnCommand_redmarkerenablerearleft(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; if ((Train->mvOccupied->iLights[vehicleotherend] & light::redmarker_right) == 0) { // turn on Train->mvOccupied->iLights[vehicleotherend] ^= light::redmarker_right; // visual feedback Train->ggRearLeftEndLightButton.UpdateValue(1.0, Train->dsbSwitch); } } } void TTrain::OnCommand_redmarkerdisablerearleft(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; if ((Train->mvOccupied->iLights[vehicleotherend] & light::redmarker_right) == 0) { return; } // turn off Train->mvOccupied->iLights[vehicleotherend] ^= light::redmarker_right; // visual feedback Train->ggRearLeftEndLightButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_redmarkertogglerearright(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; // only reacting to press, so the switch doesn't flip back and forth if key is held down if ((Train->mvOccupied->iLights[vehicleotherend] & light::redmarker_left) == 0) { OnCommand_redmarkerenablerearright(Train, Command); } else { OnCommand_redmarkerdisablerearright(Train, Command); } } } void TTrain::OnCommand_redmarkerenablerearright(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; if ((Train->mvOccupied->iLights[vehicleotherend] & light::redmarker_left) == 0) { // turn on Train->mvOccupied->iLights[vehicleotherend] ^= light::redmarker_left; // visual feedback Train->ggRearRightEndLightButton.UpdateValue(1.0, Train->dsbSwitch); } } } void TTrain::OnCommand_redmarkerdisablerearright(TTrain *Train, command_data const &Command) { if (Train->mvOccupied->LightsPosNo > 0) { // lights are controlled by preset selector return; } if (Command.action == GLFW_PRESS) { // NOTE: we toggle the light on opposite side, as 'rear right' is 'front left' on the rear end etc auto const vehicleotherend{(Train->cab_to_end() == end::front ? end::rear : end::front)}; if ((Train->mvOccupied->iLights[vehicleotherend] & light::redmarker_left) == 0) { return; } // turn off Train->mvOccupied->iLights[vehicleotherend] ^= light::redmarker_left; // visual feedback Train->ggRearRightEndLightButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_redmarkerstoggle(TTrain *Train, command_data const &Command) { if ((true == Command.freefly) && (Command.action == GLFW_PRESS)) { auto *vehicle{std::get(simulation::Region->find_vehicle(Command.location, 10, false, true))}; if (vehicle == nullptr) { return; } auto locationHead = vehicle->HeadPosition() - glm::dvec3(Command.location); // TODO: Maybe command_data should be dvec3? auto locationRear = vehicle->RearPosition() - glm::dvec3(Command.location); int const CouplNr{std::clamp(vehicle->DirectionGet() * (glm::dot(locationHead, locationHead) > glm::dot(locationRear, locationRear) ? 1 : -1), 0, 1)}; // z [-1,1] zrobić [0,1] auto const lightset{light::redmarker_left | light::redmarker_right}; vehicle->MoverParameters->iLights[CouplNr] = (false == TestFlag(vehicle->MoverParameters->iLights[CouplNr], lightset) ? vehicle->MoverParameters->iLights[CouplNr] |= lightset : // turn signals on vehicle->MoverParameters->iLights[CouplNr] ^= lightset); // turn signals off } } void TTrain::OnCommand_endsignalstoggle(TTrain *Train, command_data const &Command) { if ((true == Command.freefly) && (Command.action == GLFW_PRESS)) { auto *vehicle{std::get(simulation::Region->find_vehicle(Command.location, 10, false, true))}; if (vehicle == nullptr) { return; } int const CouplNr{ std::clamp(vehicle->DirectionGet() * (glm::length2(vehicle->HeadPosition() - glm::dvec3(Command.location)) > glm::length2(vehicle->RearPosition() - glm::dvec3(Command.location)) ? 1 : -1), 0, 1)}; // z [-1,1] zrobić [0,1] auto const lightset{light::rearendsignals}; vehicle->MoverParameters->iLights[CouplNr] = (false == TestFlag(vehicle->MoverParameters->iLights[CouplNr], lightset) ? vehicle->MoverParameters->iLights[CouplNr] |= lightset : // turn signals on vehicle->MoverParameters->iLights[CouplNr] ^= lightset); // turn signals off } } void TTrain::OnCommand_headlightsdimtoggle(TTrain *Train, command_data const &Command) { if (Train->DynamicObject->MoverParameters->enableModernDimmer) return; if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Train->DynamicObject->MoverParameters->modernDimmerPosition == 0) { // turn on OnCommand_headlightsdimenable(Train, Command); } else { // turn off OnCommand_headlightsdimdisable(Train, Command); } } } void TTrain::OnCommand_headlightsdimenable(TTrain *Train, command_data const &Command) { if (Train->DynamicObject->MoverParameters->enableModernDimmer) return; if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Train->ggDimHeadlightsButton.SubModel != nullptr) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels // visual feedback Train->ggDimHeadlightsButton.UpdateValue(1.0, Train->dsbSwitch); } /* // to jest stara logika if (true == Train->DynamicObject->DimHeadlights) { return; } // already enabled Train->DynamicObject->DimHeadlights = true; */ Train->DynamicObject->MoverParameters->modernDimmerPosition = 1; // ustawiamy modern dimmer na flage przyciemnienia Train->DynamicObject->SetLightDimmings(); } } void TTrain::OnCommand_headlightsdimdisable(TTrain *Train, command_data const &Command) { if (Train->DynamicObject->MoverParameters->enableModernDimmer) // nie wiem dlaczego to tak dziala ze jest odwrocona logika return; if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Train->ggDimHeadlightsButton.SubModel != nullptr) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels // visual feedback Train->ggDimHeadlightsButton.UpdateValue(0.0, Train->dsbSwitch); } /* // stara logika przyciemniania if( false == Train->DynamicObject->DimHeadlights ) { return; } // already enabled Train->DynamicObject->DimHeadlights = false; */ Train->DynamicObject->MoverParameters->modernDimmerPosition = 0; // ustawiamy modern dimmer na flage rozjasnienia Train->DynamicObject->SetLightDimmings(); } } void TTrain::OnCommand_interiorlighttoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->Cabine[Train->iCabn].bLight) { // turn on OnCommand_interiorlightenable(Train, Command); } else { // turn off OnCommand_interiorlightdisable(Train, Command); } } } void TTrain::OnCommand_interiorlightenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->m_controlmapper.contains("cablight_sw:")) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels WriteLog("Interior Light switch is missing, or wasn't defined"); return; } // store lighting switch states if (false == Train->DynamicObject->JointCabs) { // vehicles with separate cabs get separate lighting switch states Train->Cabine[Train->iCabn].bLight = true; } else { // joint virtual cabs share lighting switch states for (auto &cab : Train->Cabine) { cab.bLight = true; } } } } void TTrain::OnCommand_interiorlightdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->m_controlmapper.contains("cablight_sw:")) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels WriteLog("Interior Light switch is missing, or wasn't defined"); return; } // store lighting switch states if (false == Train->DynamicObject->JointCabs) { // vehicles with separate cabs get separate lighting switch states Train->Cabine[Train->iCabn].bLight = false; } else { // joint virtual cabs share lighting switch states for (auto &cab : Train->Cabine) { cab.bLight = false; } } } } void TTrain::OnCommand_interiorlightdimtoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->Cabine[Train->iCabn].bLightDim) { // turn on OnCommand_interiorlightdimenable(Train, Command); } else { // turn off OnCommand_interiorlightdimdisable(Train, Command); } } } void TTrain::OnCommand_interiorlightdimenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Train->ggCabLightDimButton.SubModel == nullptr) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels WriteLog("Dim Interior Light switch is missing, or wasn't defined"); return; } // visual feedback Train->ggCabLightDimButton.UpdateValue(1.0, Train->dsbSwitch); // store lighting switch states if (false == Train->DynamicObject->JointCabs) { // vehicles with separate cabs get separate lighting switch states Train->Cabine[Train->iCabn].bLightDim = true; } else { // joint virtual cabs share lighting switch states for (auto &cab : Train->Cabine) { cab.bLightDim = true; } } } } void TTrain::OnCommand_interiorlightdimdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Train->ggCabLightDimButton.SubModel == nullptr) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels WriteLog("Dim Interior Light switch is missing, or wasn't defined"); return; } // visual feedback Train->ggCabLightDimButton.UpdateValue(0.0, Train->dsbSwitch); // store lighting switch states if (false == Train->DynamicObject->JointCabs) { // vehicles with separate cabs get separate lighting switch states Train->Cabine[Train->iCabn].bLightDim = false; } else { // joint virtual cabs share lighting switch states for (auto &cab : Train->Cabine) { cab.bLightDim = false; } } } } void TTrain::OnCommand_compartmentlightstoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } // keep the switch from flipping back and forth if key is held down if ((false == Train->mvOccupied->CompartmentLights.is_active) && (false == Train->mvOccupied->CompartmentLights.is_enabled)) { // turn on OnCommand_compartmentlightsenable(Train, Command); } else { // turn off OnCommand_compartmentlightsdisable(Train, Command); } } void TTrain::OnCommand_compartmentlightsenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->mvOccupied->CompartmentLightsSwitch(true); if (Train->m_controlmapper.contains("compartmentlights_sw:")) { auto const istoggle{(static_cast(Train->ggCompartmentLightsButton.type()) & static_cast(TGaugeType::toggle)) != 0}; if (istoggle) { Train->mvOccupied->CompartmentLightsSwitchOff(false); } } // visual feedback if (Train->m_controlmapper.contains("compartmentlights_sw:")) { Train->ggCompartmentLightsButton.UpdateValue(1.0f, Train->dsbSwitch); } if (Train->m_controlmapper.contains("compartmentlightson_sw:")) { Train->ggCompartmentLightsOnButton.UpdateValue(1.0f, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { if (Train->m_controlmapper.contains("compartmentlights_sw:")) { if (Train->ggCompartmentLightsButton.type() == TGaugeType::push) { // return the switch to neutral position Train->mvOccupied->CompartmentLightsSwitch(false); Train->mvOccupied->CompartmentLightsSwitchOff(false); Train->ggCompartmentLightsButton.UpdateValue(0.5f); } } if (Train->m_controlmapper.contains("compartmentlightson_sw:")) { Train->mvOccupied->CompartmentLightsSwitch(false); Train->ggCompartmentLightsOnButton.UpdateValue(0.0f, Train->dsbSwitch); } } } void TTrain::OnCommand_compartmentlightsdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->mvOccupied->CompartmentLightsSwitchOff(true); if (Train->m_controlmapper.contains("compartmentlights_sw:")) { auto const istoggle{(static_cast(Train->ggCompartmentLightsButton.type()) & static_cast(TGaugeType::toggle)) != 0}; if (istoggle) { Train->mvOccupied->CompartmentLightsSwitch(false); } } // visual feedback if (Train->m_controlmapper.contains("compartmentlights_sw:")) { Train->ggCompartmentLightsButton.UpdateValue(0.0f, Train->dsbSwitch); } if (Train->m_controlmapper.contains("compartmentlightsoff_sw:")) { Train->ggCompartmentLightsOffButton.UpdateValue(1.0f, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { if (Train->m_controlmapper.contains("compartmentlights_sw:")) { if (Train->ggCompartmentLightsButton.type() == TGaugeType::push) { // return the switch to neutral position Train->mvOccupied->CompartmentLightsSwitch(false); Train->mvOccupied->CompartmentLightsSwitchOff(false); Train->ggCompartmentLightsButton.UpdateValue(0.5f); } } if (Train->m_controlmapper.contains("compartmentlightsoff_sw:")) { Train->mvOccupied->CompartmentLightsSwitchOff(false); Train->ggCompartmentLightsOffButton.UpdateValue(0.0f, Train->dsbSwitch); } } } void TTrain::OnCommand_instrumentlighttoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->InstrumentLightActive) { // turn on OnCommand_instrumentlightenable(Train, Command); } else { // turn off OnCommand_instrumentlightdisable(Train, Command); } } } void TTrain::OnCommand_instrumentlightenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Train->ggInstrumentLightButton.SubModel == nullptr) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels WriteLog("Instrument Light switch is missing, or wasn't defined"); return; } // visual feedback Train->ggInstrumentLightButton.UpdateValue(1.0, Train->dsbSwitch); if (true == Train->InstrumentLightActive) { return; } // already enabled Train->InstrumentLightActive = true; } } void TTrain::OnCommand_instrumentlightdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Train->ggInstrumentLightButton.SubModel == nullptr) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels WriteLog("Instrument Light switch is missing, or wasn't defined"); return; } // visual feedback Train->ggInstrumentLightButton.UpdateValue(0.0, Train->dsbSwitch); if (false == Train->InstrumentLightActive) { return; } // already disabled Train->InstrumentLightActive = false; } } void TTrain::OnCommand_dashboardlighttoggle(TTrain *Train, command_data const &Command) { if (false == Train->DashboardLightActive) { OnCommand_dashboardlightenable(Train, Command); } else { OnCommand_dashboardlightdisable(Train, Command); } } void TTrain::OnCommand_dashboardlightenable(TTrain *Train, command_data const &Command) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Command.action != GLFW_PRESS) { return; } if (Train->ggDashboardLightButton.SubModel == nullptr) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels WriteLog("Dashboard Light switch is missing, or wasn't defined"); return; } if (false == Train->DashboardLightActive) { // turn on Train->DashboardLightActive = true; // visual feedback Train->ggDashboardLightButton.UpdateValue(1.0, Train->dsbSwitch); } } void TTrain::OnCommand_dashboardlightdisable(TTrain *Train, command_data const &Command) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Command.action != GLFW_PRESS) { return; } if (Train->ggDashboardLightButton.SubModel == nullptr) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels WriteLog("Dashboard Light switch is missing, or wasn't defined"); return; } if (Train->DashboardLightActive) { // turn off Train->DashboardLightActive = false; // visual feedback Train->ggDashboardLightButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_timetablelighttoggle(TTrain *Train, command_data const &Command) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Command.action != GLFW_PRESS) { return; } if (false == Train->TimetableLightActive) { OnCommand_timetablelightenable(Train, Command); } else { OnCommand_timetablelightdisable(Train, Command); } } void TTrain::OnCommand_timetablelightenable(TTrain *Train, command_data const &Command) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Command.action != GLFW_PRESS) { return; } if (Train->ggTimetableLightButton.SubModel == nullptr) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels WriteLog("Timetable Light switch is missing, or wasn't defined"); return; } if (false == Train->TimetableLightActive) { // turn on Train->TimetableLightActive = true; // visual feedback Train->ggTimetableLightButton.UpdateValue(1.0, Train->dsbSwitch); } } void TTrain::OnCommand_timetablelightdisable(TTrain *Train, command_data const &Command) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (Command.action != GLFW_PRESS) { return; } if (Train->ggTimetableLightButton.SubModel == nullptr) { // TODO: proper control deviced definition for the interiors, that doesn't hinge of presence of 3d submodels WriteLog("Timetable Light switch is missing, or wasn't defined"); return; } if (Train->TimetableLightActive) { // turn off Train->TimetableLightActive = false; // visual feedback Train->ggTimetableLightButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_heatingtoggle(TTrain *Train, command_data const &Command) { if (Train->ggTrainHeatingButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Train Heating switch is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { // ignore repeats so the switch doesn't flip back and forth if key is held down if (false == Train->mvControlled->HeatingAllow) { // turn on OnCommand_heatingenable(Train, Command); } else { // turn off OnCommand_heatingdisable(Train, Command); } } else if (Command.action == GLFW_RELEASE) { if (Train->ggTrainHeatingButton.type() == TGaugeType::push) { // impulse switch // visual feedback Train->ggTrainHeatingButton.UpdateValue(0.0, Train->dsbSwitch); } } } void TTrain::OnCommand_heatingenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->mvOccupied->HeatingSwitch(true); // visual feedback Train->ggTrainHeatingButton.UpdateValue(1.0, Train->dsbSwitch); } } void TTrain::OnCommand_heatingdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->mvOccupied->HeatingSwitch(false); // visual feedback Train->ggTrainHeatingButton.UpdateValue((Train->ggTrainHeatingButton.type() == TGaugeType::push ? 1.0 : 0.0), Train->dsbSwitch); } } void TTrain::OnCommand_generictoggle(TTrain *Train, command_data const &Command) { auto const itemindex = static_cast(Command.command) - static_cast(user_command::generictoggle0); auto &item = Train->ggUniversals[itemindex]; /* if( item.SubModel == nullptr ) { if( Command.action == GLFW_PRESS ) { WriteLog( "Train generic item " + std::to_string( itemindex ) + " is missing, or wasn't defined" ); } return; } */ if (Command.action == GLFW_PRESS) { if (item.type() == TGaugeType::push) { // impulse switch // turn on // visual feedback item.UpdateValue(1.0); } else { // two-state switch if (item.GetDesiredValue() < 0.5) { // turn on // visual feedback item.UpdateValue(1.0); } else { // turn off // visual feedback item.UpdateValue(0.0); } } } else if (Command.action == GLFW_RELEASE) { if (item.type() == TGaugeType::push) { // impulse switch // turn off // visual feedback item.UpdateValue(0.0); } } } void TTrain::OnCommand_springbraketoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->mvOccupied->SpringBrake.Activate) { // turn on OnCommand_springbrakeenable(Train, Command); } else { // turn off OnCommand_springbrakedisable(Train, Command); } } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggSpringBrakeOffButton.UpdateValue(0.0, Train->dsbSwitch); Train->ggSpringBrakeOnButton.UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_springbrakeenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SpringBrakeActivate(true); // visual feedback Train->ggSpringBrakeOnButton.UpdateValue(1.0, Train->dsbSwitch); Train->ggSpringBrakeOffButton.UpdateValue(0.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggSpringBrakeOnButton.UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_springbrakedisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SpringBrakeActivate(false); // visual feedback Train->ggSpringBrakeOffButton.UpdateValue(1.0, Train->dsbSwitch); Train->ggSpringBrakeOnButton.UpdateValue(0.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggSpringBrakeOffButton.UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_springbrakeshutofftoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if (false == Train->mvOccupied->SpringBrake.ShuttOff) { // turn on OnCommand_springbrakeshutoffenable(Train, Command); } else { // turn off OnCommand_springbrakeshutoffdisable(Train, Command); } } }; void TTrain::OnCommand_springbrakeshutoffenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SpringBrakeShutOff(true); } }; void TTrain::OnCommand_springbrakeshutoffdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SpringBrakeShutOff(false); } }; void TTrain::OnCommand_springbrakerelease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down auto *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; if (vehicle == nullptr) { return; } Train->mvOccupied->SpringBrakeRelease(); } }; void TTrain::OnCommand_speedcontrolincrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SpeedCtrlInc(); // visual feedback Train->ggSpeedControlIncreaseButton.UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggSpeedControlIncreaseButton.UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_speedcontroldecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SpeedCtrlDec(); // visual feedback Train->ggSpeedControlDecreaseButton.UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggSpeedControlDecreaseButton.UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_speedcontrolpowerincrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SpeedCtrlPowerInc(); // visual feedback Train->ggSpeedControlPowerIncreaseButton.UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggSpeedControlPowerIncreaseButton.UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_speedcontrolpowerdecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SpeedCtrlPowerDec(); // visual feedback Train->ggSpeedControlPowerDecreaseButton.UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggSpeedControlPowerDecreaseButton.UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_speedcontrolbutton(TTrain *Train, command_data const &Command) { auto const itemindex = static_cast(Command.command) - static_cast(user_command::speedcontrolbutton0); auto &item = Train->ggSpeedCtrlButtons[itemindex]; if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down Train->mvOccupied->SpeedCtrlButton(itemindex); // visual feedback Train->ggSpeedCtrlButtons[itemindex].UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback Train->ggSpeedCtrlButtons[itemindex].UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_inverterenable(TTrain *Train, command_data const &Command) { auto itemindex = static_cast(Command.command) - static_cast(user_command::inverterenable1); auto &item = Train->ggInverterEnableButtons[itemindex]; if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down bool kier = (Train->DynamicObject->DirectionGet() * Train->mvOccupied->CabOccupied > 0); int flag = Train->DynamicObject->MoverParameters->InverterControlCouplerFlag; TDynamicObject *p = Train->DynamicObject->GetFirstDynamic(Train->mvOccupied->CabOccupied < 0 ? end::rear : end::front, flag); while (p) { if (p->MoverParameters->eimc[eimc_p_Pmax] > 1) { if (itemindex < p->MoverParameters->InvertersNo) { p->MoverParameters->Inverters[itemindex].Activate = true; break; } else { itemindex -= p->MoverParameters->InvertersNo; } } p = (kier ? p->Next(flag) : p->Prev(flag)); } // visual feedback item.UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback item.UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_inverterdisable(TTrain *Train, command_data const &Command) { auto itemindex = static_cast(Command.command) - static_cast(user_command::inverterdisable1); auto &item = Train->ggInverterDisableButtons[itemindex]; if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down bool kier = (Train->DynamicObject->DirectionGet() * Train->mvOccupied->CabOccupied > 0); int flag = Train->DynamicObject->MoverParameters->InverterControlCouplerFlag; TDynamicObject *p = Train->DynamicObject->GetFirstDynamic(Train->mvOccupied->CabOccupied < 0 ? end::rear : end::front, flag); while (p) { if (p->MoverParameters->eimc[eimc_p_Pmax] > 1) { if (itemindex < p->MoverParameters->InvertersNo) { p->MoverParameters->Inverters[itemindex].Activate = false; break; } else { itemindex -= p->MoverParameters->InvertersNo; } } p = (kier ? p->Next(flag) : p->Prev(flag)); } // visual feedback item.UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // release // visual feedback item.UpdateValue(0.0, Train->dsbSwitch); } }; void TTrain::OnCommand_invertertoggle(TTrain *Train, command_data const &Command) { auto itemindex = static_cast(Command.command) - static_cast(user_command::invertertoggle1); auto &item = Train->ggInverterToggleButtons[itemindex]; if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down bool kier = (Train->DynamicObject->DirectionGet() * Train->mvOccupied->CabOccupied > 0); int flag = Train->DynamicObject->MoverParameters->InverterControlCouplerFlag; TDynamicObject *p = Train->DynamicObject->GetFirstDynamic(Train->mvOccupied->CabOccupied < 0 ? end::rear : end::front, flag); while (p) { if (p->MoverParameters->eimc[eimc_p_Pmax] > 1) { if (itemindex < p->MoverParameters->InvertersNo) { p->MoverParameters->Inverters[itemindex].Activate = !p->MoverParameters->Inverters[itemindex].Activate; // visual feedback item.UpdateValue(p->MoverParameters->Inverters[itemindex].Activate ? 1.0 : 0.0, Train->dsbSwitch); break; } else { itemindex -= p->MoverParameters->InvertersNo; } } p = (kier ? p->Next(flag) : p->Prev(flag)); } } }; void TTrain::OnCommand_doorlocktoggle(TTrain *Train, command_data const &Command) { if (Train->ggDoorSignallingButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Door Lock switch is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the sound can loop uninterrupted if (false == Train->mvOccupied->Doors.lock_enabled) { // turn on // TODO: door lock command to send through consist Train->mvOccupied->LockDoors(true); // visual feedback Train->ggDoorSignallingButton.UpdateValue(1.0, Train->dsbSwitch); } else { // turn off // TODO: door lock command to send through consist Train->mvOccupied->LockDoors(false); // visual feedback Train->ggDoorSignallingButton.UpdateValue(0.0, Train->dsbSwitch); } } } void TTrain::OnCommand_doortoggleleft(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // NOTE: test how the door state check works with consists where the occupied vehicle doesn't have opening doors if (false == ((Train->ggDoorLeftButton.GetDesiredValue() > 0.5) || (Train->ggDoorLeftOnButton.GetDesiredValue() > 0.5))) { // open OnCommand_dooropenleft(Train, Command); } else { // close if ((Train->ggDoorAllOffButton.SubModel != nullptr) && (Train->ggDoorLeftOffButton.SubModel == nullptr)) { // OnCommand_doorcloseall( Train, Command ); // if two-button setup lacks dedicated closing button require the user to press appropriate button manually return; } else { OnCommand_doorcloseleft(Train, Command); } } } else if (Command.action == GLFW_RELEASE) { if (true == ((Train->ggDoorLeftButton.GetDesiredValue() > 0.5) || (Train->ggDoorLeftOnButton.GetDesiredValue() > 0.5))) { // open if ((Train->mvOccupied->Doors.has_autowarning) && (Train->mvOccupied->DepartureSignal)) { // complete closing the doors if ((Train->ggDoorAllOffButton.SubModel != nullptr) && (Train->ggDoorLeftOffButton.SubModel == nullptr)) { // OnCommand_doorcloseall( Train, Command ); // if two-button setup lacks dedicated closing button require the user to press appropriate button manually return; } else { OnCommand_doorcloseleft(Train, Command); } } else { OnCommand_dooropenleft(Train, Command); } } else { // close if ((Train->ggDoorAllOffButton.SubModel != nullptr) && (Train->ggDoorLeftOffButton.SubModel == nullptr)) { // OnCommand_doorcloseall( Train, Command ); // if two-button setup lacks dedicated closing button require the user to press appropriate button manually return; } else { OnCommand_doorcloseleft(Train, Command); } } // visual feedback // dedicated closing buttons are presumed to be impulse switches and return automatically to neutral position // NOTE: temporary arrangement, can be removed when LD system is in place if (Train->ggDoorLeftOffButton.SubModel) Train->ggDoorLeftOffButton.UpdateValue(0.0, Train->dsbSwitch); if (Train->ggDoorLeftOnButton.SubModel) Train->ggDoorLeftOnButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_doorpermitleft(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (false == Train->mvOccupied->Doors.permit_presets.empty()) { return; } auto const side{(Train->cab_to_end() == end::front ? side::left : side::right)}; if (Command.action == GLFW_PRESS) { if (Train->ggDoorLeftPermitButton.is_push()) { // impulse switch Train->mvOccupied->PermitDoors(side); // visual feedback Train->ggDoorLeftPermitButton.UpdateValue(1.0, Train->dsbSwitch); // start potential timer for remote door control Train->m_doorpermittimers[side] = Train->mvOccupied->DoorsOpenWithPermitAfter; } else { // two-state switch auto const newstate{!(Train->ggDoorLeftPermitButton.GetDesiredValue() > 0.5)}; Train->mvOccupied->PermitDoors(side, newstate); // visual feedback Train->ggDoorLeftPermitButton.UpdateValue((newstate ? 1.0 : 0.0), Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { if (Train->ggDoorLeftPermitButton.is_push()) { // impulse switch // visual feedback Train->ggDoorLeftPermitButton.UpdateValue(0.0, Train->dsbSwitch); // reset potential remote door control timer Train->m_doorpermittimers[side] = -1.f; } } } void TTrain::OnCommand_doorpermitright(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_REPEAT) { return; } if (false == Train->mvOccupied->Doors.permit_presets.empty()) { return; } auto const side{(Train->cab_to_end() == end::front ? side::right : side::left)}; if (Command.action == GLFW_PRESS) { if (Train->ggDoorRightPermitButton.type() == TGaugeType::push) { // impulse switch Train->mvOccupied->PermitDoors(side); // visual feedback Train->ggDoorRightPermitButton.UpdateValue(1.0, Train->dsbSwitch); // start potential timer for remote door control Train->m_doorpermittimers[side] = Train->mvOccupied->DoorsOpenWithPermitAfter; } else { // two-state switch auto const newstate{!(Train->ggDoorRightPermitButton.GetDesiredValue() > 0.5)}; Train->mvOccupied->PermitDoors(side, newstate); // visual feedback Train->ggDoorRightPermitButton.UpdateValue((newstate ? 1.0 : 0.0), Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { if (Train->ggDoorRightPermitButton.type() == TGaugeType::push) { // impulse switch // visual feedback Train->ggDoorRightPermitButton.UpdateValue(0.0, Train->dsbSwitch); // reset potential remote door control timer Train->m_doorpermittimers[side] = -1.f; } } } void TTrain::OnCommand_doorpermitpresetactivatenext(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->mvOccupied->ChangeDoorPermitPreset(1); // visual feedback Train->ggDoorPermitPresetButton.UpdateValue(Train->mvOccupied->Doors.permit_preset, Train->dsbSwitch); } } void TTrain::OnCommand_doorpermitpresetactivateprevious(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->mvOccupied->ChangeDoorPermitPreset(-1); // visual feedback Train->ggDoorPermitPresetButton.UpdateValue(Train->mvOccupied->Doors.permit_preset, Train->dsbSwitch); } } void TTrain::OnCommand_dooropenleft(TTrain *Train, command_data const &Command) { auto const remoteopencontrol{(Train->mvOccupied->Doors.open_control == control_t::driver) || (Train->mvOccupied->Doors.open_control == control_t::mixed)}; if (false == remoteopencontrol) { return; } if ((Train->ggDoorLeftOnButton.SubModel == nullptr) && (Train->ggDoorLeftButton.SubModel == nullptr)) { return; } if (Command.action == GLFW_PRESS) { Train->mvOccupied->OperateDoors((Train->cab_to_end() == end::front ? side::left : side::right), true); // visual feedback if (Train->ggDoorLeftOnButton.SubModel != nullptr) { // two separate impulse switches Train->ggDoorLeftOnButton.UpdateValue(1.0, Train->dsbSwitch); } else { // single two-state switch Train->ggDoorLeftButton.UpdateValue(1.0, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { // visual feedback if (Train->ggDoorLeftOnButton.SubModel != nullptr) { // two separate impulse switches Train->ggDoorLeftOnButton.UpdateValue(0.0, Train->dsbSwitch); } } } void TTrain::OnCommand_doorcloseleft(TTrain *Train, command_data const &Command) { auto const remoteclosecontrol{(Train->mvOccupied->Doors.close_control == control_t::driver) || (Train->mvOccupied->Doors.close_control == control_t::mixed)}; if (false == remoteclosecontrol) { return; } if ((Train->ggDoorLeftOffButton.SubModel == nullptr) && (Train->ggDoorLeftButton.SubModel == nullptr)) { return; } if (Command.action == GLFW_PRESS) { if (Train->mvOccupied->Doors.has_autowarning) { // automatic departure signal delays actual door closing until the button is released Train->mvOccupied->signal_departure(true); } else { // TODO: move door opening/closing to the update, so the switch animation doesn't hinge on door working Train->mvOccupied->OperateDoors((Train->cab_to_end() == end::front ? side::left : side::right), false); } // visual feedback if (Train->ggDoorLeftOffButton.SubModel != nullptr) { // two separate switches to open and close the door Train->ggDoorLeftOffButton.UpdateValue(1.0, Train->dsbSwitch); } else { // single two-state switch Train->ggDoorLeftButton.UpdateValue(0.0, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { if (Train->mvOccupied->Doors.has_autowarning) { // automatic departure signal delays actual door closing until the button is released Train->mvOccupied->signal_departure(false); // now we can actually close the door Train->mvOccupied->OperateDoors((Train->cab_to_end() == end::front ? side::left : side::right), false); } // visual feedback // dedicated closing buttons are presumed to be impulse switches and return automatically to neutral position if (Train->ggDoorLeftOffButton.SubModel) Train->ggDoorLeftOffButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_doortoggleright(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // NOTE: test how the door state check works with consists where the occupied vehicle doesn't have opening doors if (false == ((Train->ggDoorRightButton.GetDesiredValue() > 0.5) || (Train->ggDoorRightOnButton.GetDesiredValue() > 0.5))) { // open OnCommand_dooropenright(Train, Command); } else { // close if ((Train->ggDoorAllOffButton.SubModel != nullptr) && (Train->ggDoorRightOffButton.SubModel == nullptr)) { // OnCommand_doorcloseall( Train, Command ); // if two-button setup lacks dedicated closing button require the user to press appropriate button manually return; } else { OnCommand_doorcloseright(Train, Command); } } } else if (Command.action == GLFW_RELEASE) { if (true == ((Train->ggDoorRightButton.GetDesiredValue() > 0.5) || (Train->ggDoorRightOnButton.GetDesiredValue() > 0.5))) { // open if ((Train->mvOccupied->Doors.has_autowarning) && (Train->mvOccupied->DepartureSignal)) { // complete closing the doors if ((Train->ggDoorAllOffButton.SubModel != nullptr) && (Train->ggDoorRightOffButton.SubModel == nullptr)) { // OnCommand_doorcloseall( Train, Command ); // if two-button setup lacks dedicated closing button require the user to press appropriate button manually return; } else { OnCommand_doorcloseright(Train, Command); } } else { OnCommand_dooropenright(Train, Command); } } else { // close if ((Train->ggDoorAllOffButton.SubModel != nullptr) && (Train->ggDoorRightOffButton.SubModel == nullptr)) { // OnCommand_doorcloseall( Train, Command ); // if two-button setup lacks dedicated closing button require the user to press appropriate button manually return; } else { OnCommand_doorcloseright(Train, Command); } } // visual feedback // dedicated closing buttons are presumed to be impulse switches and return automatically to neutral position // NOTE: temporary arrangement, can be removed when LD system is in place if (Train->ggDoorRightOffButton.SubModel) Train->ggDoorRightOffButton.UpdateValue(0.0, Train->dsbSwitch); if (Train->ggDoorRightOnButton.SubModel) Train->ggDoorRightOnButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_dooropenright(TTrain *Train, command_data const &Command) { auto const remoteopencontrol{(Train->mvOccupied->Doors.open_control == control_t::driver) || (Train->mvOccupied->Doors.open_control == control_t::mixed)}; if (false == remoteopencontrol) { return; } if ((Train->ggDoorRightOnButton.SubModel == nullptr) && (Train->ggDoorRightButton.SubModel == nullptr)) { return; } if (Command.action == GLFW_PRESS) { Train->mvOccupied->OperateDoors((Train->cab_to_end() == end::front ? side::right : side::left), true); // visual feedback if (Train->ggDoorRightOnButton.SubModel != nullptr) { // two separate impulse switches Train->ggDoorRightOnButton.UpdateValue(1.0, Train->dsbSwitch); } else { // single two-state switch Train->ggDoorRightButton.UpdateValue(1.0, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { // visual feedback if (Train->ggDoorRightOnButton.SubModel != nullptr) { // two separate impulse switches Train->ggDoorRightOnButton.UpdateValue(0.0, Train->dsbSwitch); } } } void TTrain::OnCommand_doorcloseright(TTrain *Train, command_data const &Command) { auto const remoteclosecontrol{(Train->mvOccupied->Doors.close_control == control_t::driver) || (Train->mvOccupied->Doors.close_control == control_t::mixed)}; if (false == remoteclosecontrol) { return; } if ((Train->ggDoorRightOffButton.SubModel == nullptr) && (Train->ggDoorRightButton.SubModel == nullptr)) { return; } if (Command.action == GLFW_PRESS) { if (Train->mvOccupied->Doors.has_autowarning) { // automatic departure signal delays actual door closing until the button is released Train->mvOccupied->signal_departure(true); } else { Train->mvOccupied->OperateDoors((Train->cab_to_end() == end::front ? side::right : side::left), false); } // visual feedback if (Train->ggDoorRightOffButton.SubModel != nullptr) { // two separate switches to open and close the door Train->ggDoorRightOffButton.UpdateValue(1.0, Train->dsbSwitch); } else { // single two-state switch Train->ggDoorRightButton.UpdateValue(0.0, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { if (Train->mvOccupied->Doors.has_autowarning) { // automatic departure signal delays actual door closing until the button is released Train->mvOccupied->signal_departure(false); // now we can actually close the door Train->mvOccupied->OperateDoors((Train->cab_to_end() == end::front ? side::right : side::left), false); } // visual feedback // dedicated closing buttons are presumed to be impulse switches and return automatically to neutral position if (Train->ggDoorRightOffButton.SubModel) Train->ggDoorRightOffButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_dooropenall(TTrain *Train, command_data const &Command) { auto const remoteopencontrol{(Train->mvOccupied->Doors.open_control == control_t::driver) || (Train->mvOccupied->Doors.open_control == control_t::mixed)}; if (false == remoteopencontrol) { return; } if (Train->ggDoorAllOnButton.SubModel == nullptr) { // TODO: expand definition of cab controls so we can know if the control is present without testing for presence of 3d switch if (Command.action == GLFW_PRESS) { WriteLog("Open All Doors switch is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { Train->mvOccupied->OperateDoors(side::right, true); Train->mvOccupied->OperateDoors(side::left, true); // visual feedback Train->ggDoorAllOnButton.UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggDoorAllOnButton.UpdateValue(0.0); } } void TTrain::OnCommand_doorcloseall(TTrain *Train, command_data const &Command) { auto const remoteclosecontrol{(Train->mvOccupied->Doors.close_control == control_t::driver) || (Train->mvOccupied->Doors.close_control == control_t::mixed)}; if (false == remoteclosecontrol) { return; } if (Train->ggDoorAllOffButton.SubModel == nullptr) { // TODO: expand definition of cab controls so we can know if the control is present without testing for presence of 3d switch if (Command.action == GLFW_PRESS) { WriteLog("Close All Doors switch is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { if (Train->mvOccupied->Doors.has_autowarning) { Train->mvOccupied->signal_departure(true); } if (Train->ggDoorAllOffButton.type() != TGaugeType::push_delayed) { // delays the action until the button is released Train->mvOccupied->OperateDoors(side::right, false); Train->mvOccupied->OperateDoors(side::left, false); } // visual feedback Train->ggDoorLeftButton.UpdateValue(0.0, Train->dsbSwitch); Train->ggDoorRightButton.UpdateValue(0.0, Train->dsbSwitch); if (Train->ggDoorAllOffButton.SubModel) Train->ggDoorAllOffButton.UpdateValue(1.0, Train->dsbSwitch); } else if (Command.action == GLFW_RELEASE) { if (Train->mvOccupied->Doors.has_autowarning) { Train->mvOccupied->signal_departure(false); } if (Train->ggDoorAllOffButton.type() == TGaugeType::push_delayed) { // now we can actually close the door Train->mvOccupied->OperateDoors(side::right, false); Train->mvOccupied->OperateDoors(side::left, false); } // visual feedback if (Train->ggDoorAllOffButton.SubModel) Train->ggDoorAllOffButton.UpdateValue(0.0); } } void TTrain::OnCommand_doorsteptoggle(TTrain *Train, command_data const &Command) { // TODO: move logic/visualization code to the gauge, on_command() should return hint whether it should invoke a reaction if (Command.action == GLFW_PRESS) { // effect if (false == Train->ggDoorStepButton.is_delayed()) { Train->mvOccupied->PermitDoorStep(false == Train->mvOccupied->Doors.step_enabled); } // visual feedback auto const isactive{(Train->ggDoorStepButton.is_push() // always press push button || Train->mvOccupied->Doors.step_enabled)}; // for toggle buttons indicate item state Train->ggDoorStepButton.UpdateValue(isactive ? 1 : 0); } else if (Command.action == GLFW_RELEASE) { // effect if (Train->ggDoorStepButton.is_delayed()) { Train->mvOccupied->PermitDoorStep(false == Train->mvOccupied->Doors.step_enabled); } // visual feedback if (Train->ggDoorStepButton.is_push()) { Train->ggDoorStepButton.UpdateValue(0); } } } void TTrain::OnCommand_doormodetoggle(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { Train->mvOccupied->ChangeDoorControlMode(false == Train->mvOccupied->Doors.remote_only); } } void TTrain::OnCommand_mirrorstoggle(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_PRESS) { return; } // only reacting to press, so the sound can loop uninterrupted if (false == Train->mvOccupied->MirrorForbidden) { // turn on Train->mvOccupied->MirrorForbidden = true; } else { // turn off Train->mvOccupied->MirrorForbidden = false; } } void TTrain::OnCommand_nearestcarcouplingincrease(TTrain *Train, command_data const &Command) { if ((true == Command.freefly) && (Command.action == GLFW_PRESS)) { // tryb freefly, press only auto coupler{-1}; auto *vehicle{Train->DynamicObject->ABuScanNearestObject(Command.location, Train->DynamicObject->GetTrack(), 1, 1500, coupler)}; if (vehicle == nullptr) vehicle = Train->DynamicObject->ABuScanNearestObject(Command.location, Train->DynamicObject->GetTrack(), -1, 1500, coupler); if ((coupler != -1) && (vehicle != nullptr)) { vehicle->couple(coupler); } if (Train->DynamicObject->Mechanik) { // aktualizacja flag kierunku w składzie Train->DynamicObject->Mechanik->CheckVehicles(Connect); } } } void TTrain::OnCommand_nearestcarcouplingdisconnect(TTrain *Train, command_data const &Command) { if ((true == Command.freefly) && (Command.action == GLFW_PRESS)) { // tryb freefly, press only auto coupler{-1}; auto *vehicle{Train->DynamicObject->ABuScanNearestObject(Command.location, Train->DynamicObject->GetTrack(), 1, 1500, coupler)}; if (vehicle == nullptr) vehicle = Train->DynamicObject->ABuScanNearestObject(Command.location, Train->DynamicObject->GetTrack(), -1, 1500, coupler); if ((coupler != -1) && (vehicle != nullptr)) { vehicle->uncouple(coupler); } if (Train->DynamicObject->Mechanik) { // aktualizacja flag kierunku w składzie Train->DynamicObject->Mechanik->CheckVehicles(Disconnect); } } } void TTrain::OnCommand_nearestcarcoupleradapterattach(TTrain *Train, command_data const &Command) { if ((true == Command.freefly) && (Command.action == GLFW_PRESS)) { // tryb freefly, press only auto *vehicle{std::get(simulation::Region->find_vehicle(Command.location, 50, false, true))}; if (vehicle == nullptr) { return; } auto const coupler = (glm::length2(glm::vec3{vehicle->CouplerPosition(end::front)} - Command.location) < glm::length2(glm::vec3{vehicle->CouplerPosition(end::rear)} - Command.location) ? end::front : end::rear); vehicle->attach_coupler_adapter(coupler); } } void TTrain::OnCommand_nearestcarcoupleradapterremove(TTrain *Train, command_data const &Command) { if ((true == Command.freefly) && (Command.action == GLFW_PRESS)) { // tryb freefly, press only auto *vehicle{std::get(simulation::Region->find_vehicle(Command.location, 50, false, true))}; if (vehicle == nullptr) { return; } auto const coupler = (glm::length2(glm::vec3{vehicle->CouplerPosition(end::front)} - Command.location) < glm::length2(glm::vec3{vehicle->CouplerPosition(end::rear)} - Command.location) ? end::front : end::rear); vehicle->remove_coupler_adapter(coupler); } } void TTrain::OnCommand_occupiedcarcouplingdisconnect(TTrain *Train, command_data const &Command) { // if( false == Train->m_controlmapper.contains( "couplingdisconnect_sw:" ) ) { return; } if (Command.action == GLFW_PRESS) { // visual feedback Train->m_couplingdisconnect = true; if (Train->iCabn == 0) { return; } if (Train->DynamicObject) { Train->DynamicObject->uncouple(Train->cab_to_end()); if (Train->DynamicObject->Mechanik) { // aktualizacja flag kierunku w składzie Train->DynamicObject->Mechanik->CheckVehicles(Disconnect); } } } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->m_couplingdisconnect = false; } } void TTrain::OnCommand_occupiedcarcouplingdisconnectback(TTrain *Train, command_data const &Command) { // if( false == Train->m_controlmapper.contains( "couplingdisconnect_sw:" ) ) { return; } if (Command.action == GLFW_PRESS) { // visual feedback Train->m_couplingdisconnectback = true; if (Train->iCabn == 0) { return; } if (Train->DynamicObject) { Train->DynamicObject->uncouple(1 - Train->cab_to_end()); if (Train->DynamicObject->Mechanik) { // aktualizacja flag kierunku w składzie Train->DynamicObject->Mechanik->CheckVehicles(Disconnect); } } } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->m_couplingdisconnectback = false; } } void TTrain::OnCommand_departureannounce(TTrain *Train, command_data const &Command) { if (Train->ggDepartureSignalButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Departure Signal button is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { // only reacting to press, so the sound can loop uninterrupted if (false == Train->mvOccupied->DepartureSignal) { // turn on Train->mvOccupied->signal_departure(true); // visual feedback Train->ggDepartureSignalButton.UpdateValue(1.0, Train->dsbSwitch); } } else if (Command.action == GLFW_RELEASE) { // turn off Train->mvOccupied->signal_departure(false); // visual feedback Train->ggDepartureSignalButton.UpdateValue(0.0, Train->dsbSwitch); } } void TTrain::OnCommand_hornlowactivate(TTrain *Train, command_data const &Command) { if ((Train->ggHornButton.SubModel == nullptr) && (Train->ggHornLowButton.SubModel == nullptr)) { if (Command.action == GLFW_PRESS) { WriteLog("Horn button is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { // only need to react to press, sound will continue until stopped if (false == TestFlag(Train->mvOccupied->WarningSignal, 1)) { // turn on Train->mvOccupied->WarningSignal |= 1; /* if( true == TestFlag( Train->mvOccupied->WarningSignal, 2 ) ) { // low and high horn are treated as mutually exclusive Train->mvControlled->WarningSignal &= ~2; } */ // visual feedback Train->ggHornButton.UpdateValue(-1.0); Train->ggHornLowButton.UpdateValue(1.0); } } else if (Command.action == GLFW_RELEASE) { // turn off /* // NOTE: we turn off both low and high horn, due to unreliability of release event when shift key is involved Train->mvOccupied->WarningSignal &= ~( 1 | 2 ); */ Train->mvOccupied->WarningSignal &= ~1; // visual feedback Train->ggHornButton.UpdateValue(0.0); Train->ggHornLowButton.UpdateValue(0.0); } } void TTrain::OnCommand_hornhighactivate(TTrain *Train, command_data const &Command) { if ((Train->ggHornButton.SubModel == nullptr) && (Train->ggHornHighButton.SubModel == nullptr)) { if (Command.action == GLFW_PRESS) { WriteLog("Horn button is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { // only need to react to press, sound will continue until stopped if (false == TestFlag(Train->mvOccupied->WarningSignal, 2)) { // turn on Train->mvOccupied->WarningSignal |= 2; /* if( true == TestFlag( Train->mvOccupied->WarningSignal, 1 ) ) { // low and high horn are treated as mutually exclusive Train->mvControlled->WarningSignal &= ~1; } */ // visual feedback Train->ggHornButton.UpdateValue(1.0); Train->ggHornHighButton.UpdateValue(1.0); } } else if (Command.action == GLFW_RELEASE) { // turn off /* // NOTE: we turn off both low and high horn, due to unreliability of release event when shift key is involved Train->mvOccupied->WarningSignal &= ~( 1 | 2 ); */ Train->mvOccupied->WarningSignal &= ~2; // visual feedback Train->ggHornButton.UpdateValue(0.0); Train->ggHornHighButton.UpdateValue(0.0); } } void TTrain::OnCommand_whistleactivate(TTrain *Train, command_data const &Command) { if (Train->ggWhistleButton.SubModel == nullptr) { if (Command.action == GLFW_PRESS) { WriteLog("Whistle button is missing, or wasn't defined"); } return; } if (Command.action == GLFW_PRESS) { // only need to react to press, sound will continue until stopped if (false == TestFlag(Train->mvOccupied->WarningSignal, 4)) { // turn on Train->mvOccupied->WarningSignal |= 4; // visual feedback Train->ggWhistleButton.UpdateValue(1.0); } } else if (Command.action == GLFW_RELEASE) { // turn off Train->mvOccupied->WarningSignal &= ~4; // visual feedback Train->ggWhistleButton.UpdateValue(0.0); } } void TTrain::OnCommand_radiotoggle(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_PRESS) { return; } // NOTE: we ignore the lack of 3d model to allow system reset after receiving radio-stop signal /* if( false == Train->m_controlmapper.contains( "radio_sw:" ) ) { return; } */ // only reacting to press, so the sound can loop uninterrupted if (false == Train->mvOccupied->Radio) { // turn on OnCommand_radioenable(Train, Command); } else { // turn off OnCommand_radiodisable(Train, Command); } } void TTrain::OnCommand_radioenable(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_PRESS) { return; } if (false == Train->mvOccupied->Radio) { Train->mvOccupied->Radio = true; } } void TTrain::OnCommand_radiodisable(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_PRESS) { return; } if (Train->mvOccupied->Radio) { Train->mvOccupied->Radio = false; } } void TTrain::OnCommand_radiochannelincrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { command_data newCommand = Command; newCommand.param1 = Train->RadioChannel() + 1; OnCommand_radiochannelset(Train, newCommand); Train->ggRadioChannelNext.UpdateValue(1.0); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggRadioChannelNext.UpdateValue(0.0); } } void TTrain::OnCommand_radiochanneldecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { command_data newCommand = Command; newCommand.param1 = Train->RadioChannel() - 1; OnCommand_radiochannelset(Train, newCommand); Train->ggRadioChannelPrevious.UpdateValue(1.0); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggRadioChannelPrevious.UpdateValue(0.0); } } void TTrain::OnCommand_radiochannelset(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // on press or hold Train->RadioChannel() = std::clamp((int)Command.param1, 1, 10); Train->ggRadioChannelSelector.UpdateValue(Train->RadioChannel() - 1); } } void TTrain::OnCommand_radiostopsend(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { if ((true == Train->mvOccupied->Radio) && (Train->mvOccupied->Power24vIsAvailable || Train->mvOccupied->Power110vIsAvailable)) { simulation::Region->RadioStop(Train->Dynamic()->GetPosition()); } // visual feedback Train->ggRadioStop.UpdateValue(1.0); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggRadioStop.UpdateValue(0.0); } } void TTrain::OnCommand_radiostopenable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS && Train->ggRadioStop.GetValue() == 0) { if ((true == Train->mvOccupied->Radio) && (Train->mvOccupied->Power24vIsAvailable || Train->mvOccupied->Power110vIsAvailable)) { simulation::Region->RadioStop(Train->Dynamic()->GetPosition()); } // visual feedback Train->ggRadioStop.UpdateValue(1.0); } } void TTrain::OnCommand_radiostopdisable(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS && Train->ggRadioStop.GetValue() > 0) { // visual feedback Train->ggRadioStop.UpdateValue(0.0); } } void TTrain::OnCommand_radiostoptest(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { if ((Train->RadioChannel() == 10) && (true == Train->mvOccupied->Radio) && (Train->mvOccupied->Power24vIsAvailable || Train->mvOccupied->Power110vIsAvailable)) { Train->Dynamic()->RadioStop(); } // visual feedback Train->ggRadioTest.UpdateValue(1.0); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggRadioTest.UpdateValue(0.0); } } void TTrain::OnCommand_radiocall1send(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { if ((Train->RadioChannel() != 10) && (true == Train->mvOccupied->Radio) && (Train->mvOccupied->Power24vIsAvailable || Train->mvOccupied->Power110vIsAvailable)) { simulation::Events.queue_receivers(radio_message::call1, Train->Dynamic()->GetPosition()); } // visual feedback Train->ggRadioCall1.UpdateValue(1.0); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggRadioCall1.UpdateValue(0.0); } } void TTrain::OnCommand_radiocall3send(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { if ((Train->RadioChannel() != 10) && (true == Train->mvOccupied->Radio) && (Train->mvOccupied->Power24vIsAvailable || Train->mvOccupied->Power110vIsAvailable)) { simulation::Events.queue_receivers(radio_message::call3, Train->Dynamic()->GetPosition()); } // visual feedback Train->ggRadioCall3.UpdateValue(1.0); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggRadioCall3.UpdateValue(0.0); } } void TTrain::OnCommand_radiovolumeincrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { command_data newCommand = Command; newCommand.param1 = Train->m_radiovolume + 0.125; OnCommand_radiovolumeset(Train, newCommand); Train->ggRadioVolumeNext.UpdateValue(1.0); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggRadioVolumeNext.UpdateValue(0.0); } } void TTrain::OnCommand_radiovolumedecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { command_data newCommand = Command; newCommand.param1 = Train->m_radiovolume - 0.125; OnCommand_radiovolumeset(Train, newCommand); Train->ggRadioVolumePrevious.UpdateValue(1.0); } else if (Command.action == GLFW_RELEASE) { // visual feedback Train->ggRadioVolumePrevious.UpdateValue(0.0); } } void TTrain::OnCommand_radiovolumeset(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_RELEASE) { // on press or hold Train->m_radiovolume = std::clamp(Command.param1, 0.0, 1.0); Train->ggRadioVolumeSelector.UpdateValue(Train->m_radiovolume); audio::event_volume_change = true; } } void TTrain::OnCommand_cabchangeforward(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { auto const *owner{(Train->DynamicObject->ctOwner != nullptr ? Train->DynamicObject->ctOwner : Train->DynamicObject->Mechanik)}; auto const movedirection{1}; if (false == Train->CabChange(movedirection)) { auto const exitdirection{(movedirection > 0 ? end::front : end::rear)}; if (TestFlag(Train->mvOccupied->Couplers[exitdirection].CouplingFlag, coupling::gangway)) { // przejscie do nastepnego pojazdu auto *targetvehicle = (exitdirection == end::front ? Train->DynamicObject->PrevConnected() : Train->DynamicObject->NextConnected()); targetvehicle->MoverParameters->CabOccupied = (Train->mvOccupied->Neighbours[exitdirection].vehicle_end ? -1 : 1); Train->MoveToVehicle(targetvehicle); } } // HACK: match consist door permit state with the preset in the active cab if (Train->ggDoorPermitPresetButton.SubModel != nullptr) { Train->mvOccupied->ChangeDoorPermitPreset(0); } // HACK: update lights state if (Train->mvOccupied->LightsPosNo > 0) { Train->DynamicObject->SetLights(); } } } void TTrain::OnCommand_cabchangebackward(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { auto const *owner{(Train->DynamicObject->ctOwner != nullptr ? Train->DynamicObject->ctOwner : Train->DynamicObject->Mechanik)}; auto const movedirection{-1}; if (false == Train->CabChange(movedirection)) { // current vehicle doesn't extend any farther in this direction, check if we there's one connected we can move to auto const exitdirection{(movedirection > 0 ? end::front : end::rear)}; if (TestFlag(Train->mvOccupied->Couplers[exitdirection].CouplingFlag, coupling::gangway)) { // przejscie do nastepnego pojazdu auto *targetvehicle = (exitdirection == end::front ? Train->DynamicObject->PrevConnected() : Train->DynamicObject->NextConnected()); targetvehicle->MoverParameters->CabOccupied = (Train->mvOccupied->Neighbours[exitdirection].vehicle_end ? -1 : 1); Train->MoveToVehicle(targetvehicle); } } // HACK: match consist door permit state with the preset in the active cab if (Train->ggDoorPermitPresetButton.SubModel != nullptr) { Train->mvOccupied->ChangeDoorPermitPreset(0); } // HACK: update lights state if (Train->mvOccupied->LightsPosNo > 0) { Train->DynamicObject->SetLights(); } } } void TTrain::OnCommand_vehiclemoveforwards(TTrain *Train, const command_data &Command) { if (Command.action == GLFW_RELEASE || !DebugModeFlag) return; Train->DynamicObject->move_set(100.0); } void TTrain::OnCommand_vehiclemovebackwards(TTrain *Train, const command_data &Command) { if (Command.action == GLFW_RELEASE || !DebugModeFlag) return; Train->DynamicObject->move_set(-100.0); } void TTrain::OnCommand_vehicleboost(TTrain *Train, const command_data &Command) { if (Command.action == GLFW_RELEASE || !DebugModeFlag) return; double boost = Command.param1 != 0.0 ? Command.param1 : 2.78; if (Train->DynamicObject == nullptr) { return; } auto *vehicle{Train->DynamicObject}; while (vehicle) { vehicle->MoverParameters->V += vehicle->DirectionGet() * boost; vehicle = vehicle->Next(); // pozostałe też } vehicle = Train->DynamicObject->Prev(); while (vehicle) { vehicle->MoverParameters->V += vehicle->DirectionGet() * boost; vehicle = vehicle->Prev(); // w drugą stronę też } } // 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); } bool TTrain::Update(double const Deltatime) { // train state update // line breaker: if (m_linebreakerstate == 0) { if (true == mvControlled->Mains) { // crude way to sync state of the linebreaker with ai-issued commands m_linebreakerstate = 1; } } if (m_linebreakerstate == 1) { if (false == (mvControlled->Mains || mvControlled->dizel_startup)) { // crude way to catch cases where the main was knocked out // because the state of the line breaker isn't changed to match, we need to do it here manually m_linebreakerstate = 0; } } if (((ggMainButton.SubModel != nullptr) && (ggMainButton.GetDesiredValue() > 0.95)) || ((ggMainOnButton.SubModel != nullptr) && (ggMainOnButton.GetDesiredValue() > 0.95) || (ggIgnitionKey.GetDesiredValue() > 0.95))) { // HACK: fallback // keep track of period the line breaker button is held down, to determine when/if circuit closes if (mvControlled->MainSwitchCheck()) { fMainRelayTimer += Deltatime; } } else { // button isn't down, reset the timer fMainRelayTimer = 0.0f; } if (ggMainOffButton.GetDesiredValue() > 0.95) { // if the button disconnecting the line breaker is down prevent the timer from accumulating fMainRelayTimer = 0.0f; } if (m_linebreakerstate == 0) { if (fMainRelayTimer > mvControlled->InitialCtrlDelay) { // wlaczanie WSa z opoznieniem // mark the line breaker as ready to close; for electric series vehicles with impulse switch the setup is completed on button release m_linebreakerstate = 2; } } if (m_linebreakerstate == 2) { // for diesels and/or vehicles with toggle switch setup we complete the engine start here // TODO: make it a test for main_on_bt of type push_delayed instead if ((ggMainOnButton.SubModel == nullptr) || (mvControlled->EngineType != TEngineType::ElectricSeriesMotor)) { // try to finalize state change of the line breaker, set the state based on the outcome m_linebreakerstate = (mvControlled->MainSwitch(true) ? 1 : 0); } } // door permits for (auto idx = 0; idx < 2; ++idx) { auto &doorpermittimer{m_doorpermittimers[idx]}; if (doorpermittimer < 0.f) { continue; } doorpermittimer -= Deltatime; if (doorpermittimer < 0.f) { mvOccupied->OperateDoors(static_cast(idx), true); } } // train measurement timer if (trainLenghtMeasureTimer >= 0.f) { trainLenghtMeasureTimer -= Deltatime; if (trainLenghtMeasureTimer < 0.f) trainLenghtMeasureTimer = -1.f; } // battery timer if (fBatteryTimer >= 0.f) { fBatteryTimer -= Deltatime; if (fBatteryTimer < 0.f) fBatteryTimer = -1.f; } // helper variables if (DynamicObject->Mechanik != nullptr) { m_doors = (DynamicObject->Mechanik->IsAnyDoorOpen[side::right] || DynamicObject->Mechanik->IsAnyDoorOpen[side::left]); m_doorpermits = (DynamicObject->Mechanik->IsAnyDoorPermitActive[side::right] || DynamicObject->Mechanik->IsAnyDoorPermitActive[side::left]); m_doorspermitleft = mvOccupied->Doors.instances[(cab_to_end() == end::front ? side::left : side::right)].open_permit && ((simulation::Time.data().wSecond % 2 < 1) || (mvOccupied->DoorsPermitLightBlinking < 1) || ((mvOccupied->DoorsPermitLightBlinking < 2) && (DynamicObject->Mechanik->IsAnyDoorOpen[(cab_to_end() == end::front ? side::left : side::right)]) || ((mvOccupied->DoorsPermitLightBlinking < 3) && DynamicObject->Mechanik->IsAnyDoorOnlyOpen[(cab_to_end() == end::front ? side::left : side::right)]))); m_doorspermitright = mvOccupied->Doors.instances[(cab_to_end() == end::front ? side::right : side::left)].open_permit && ((simulation::Time.data().wSecond % 2 < 1) || (mvOccupied->DoorsPermitLightBlinking < 1) || ((mvOccupied->DoorsPermitLightBlinking < 2) && (DynamicObject->Mechanik->IsAnyDoorOpen[(cab_to_end() == end::front ? side::right : side::left)]) || ((mvOccupied->DoorsPermitLightBlinking < 3) && DynamicObject->Mechanik->IsAnyDoorOnlyOpen[(cab_to_end() == end::front ? side::right : side::left)]))); } m_dirforward = (mvControlled->DirActive > 0); m_dirneutral = (mvControlled->DirActive == 0); m_dirbackward = (mvControlled->DirActive < 0); // check for received user commands // NOTE: this is a temporary arrangement, for the transition period from old command setup to the new one // eventually commands are going to be retrieved directly by the vehicle, filtered through active control stand // and ultimately executed, provided the stand allows it. command_data commanddata; while (simulation::Commands.pop(commanddata, static_cast(command_target::vehicle) | id())) { auto lookup = m_commandhandlers.find(commanddata.command); if (lookup != m_commandhandlers.end()) { // debug data if (commanddata.action == GLFW_PRESS) { WriteLog(mvOccupied->Name + " received command: [" + simulation::Commands_descriptions[static_cast(commanddata.command)].name + "]"); } // pass the command to the assigned handler lookup->second(this, commanddata); } } UpdateCab(); if ((DynamicObject->Mechanik != nullptr) && (false == DynamicObject->Mechanik->AIControllFlag)) { // nie blokujemy AI if ((mvOccupied->TrainType == dt_ET40) || (mvOccupied->TrainType == dt_EP05) || (mvOccupied->HasCamshaft)) { // dla ET40 i EU05 automatyczne cofanie nastawnika - i tak nie będzie to działać dobrze... // TODO: use deltatime to stabilize speed /* if( false == ( ( input::command == user_command::mastercontrollerset ) || ( input::command == user_command::mastercontrollerincrease ) || ( input::command == user_command::mastercontrollerdecrease ) ) ) { */ if (false == (m_mastercontrollerinuse || Global.ctrlState)) { m_mastercontrollerreturndelay -= Deltatime; if (m_mastercontrollerreturndelay < 0.f) { m_mastercontrollerreturndelay = EU07_CONTROLLER_BASERETURNDELAY; if (mvOccupied->MainCtrlPos > mvOccupied->MainCtrlActualPos) { mvOccupied->DecMainCtrl(1); } else if (mvOccupied->MainCtrlPos < mvOccupied->MainCtrlActualPos) { // Ra 15-01: a to nie miało być tylko cofanie? mvOccupied->IncMainCtrl(1); } } } } } // McZapkie: predkosc wyswietlana na tachometrze brana jest z obrotow kol auto const maxtacho{3.0}; double maxSpeed = mvControlled->Vmax * 1.05; // zachowanie starej logiki jak nie ma definicji max tarczki if (mvOccupied->maxTachoSpeed != 0) { maxSpeed = mvOccupied->maxTachoSpeed; } fTachoVelocity = static_cast(std::min(std::abs(11.31 * mvControlled->WheelDiameter * mvControlled->nrot), maxSpeed)); { // skacze osobna zmienna float ff = simulation::Time.data().wSecond; // skacze co sekunde - pol sekundy // pomiar, pol sekundy ustawienie if (ff != fTachoTimer) // jesli w tej sekundzie nie zmienial { if (fTachoVelocity >= 5) // jedzie fTachoVelocityJump = fTachoVelocity + (2.0 - LocalRandom(3) + LocalRandom(3)) * 0.5; else if (fTachoVelocity < 5 && fTachoVelocity > 1) fTachoVelocityJump = Random(0, 4); // tu ma sie bujac jak wariat i zatrzymac na jakiejs predkosci // fTachoVelocityJump = 0; // stoi fTachoTimer = ff; // juz zmienil } } if (fTachoVelocity > 1) // McZapkie-270503: podkrecanie tachometru { // szybciej zacznij stukac fTachoCount = std::min(maxtacho, fTachoCount + Deltatime * 3); } else if (fTachoCount > 0) { // schodz powoli - niektore haslery to ze 4 sekundy potrafia stukac fTachoCount = std::max(0.0, fTachoCount - Deltatime * 0.66); } // Ra 2014-09: napięcia i prądy muszą być ustalone najpierw, bo wysyłane są ewentualnie na PoKeys if ((mvControlled->EngineType != TEngineType::DieselElectric) && (mvControlled->EngineType != TEngineType::ElectricInductionMotor)) { // Ra 2014-09: czy taki rozdzia? ma sens? fHVoltage = std::max(mvControlled->PantographVoltage, mvControlled->GetTrainsetHighVoltage()); // Winger czy to nie jest zle? } // *mvControlled->Mains); else { fHVoltage = mvControlled->EngineVoltage; } if (ShowNextCurrent) { // jeśli pokazywać drugi człon if (mvSecond) { // o ile jest ten drugi fHCurrent[0] = mvSecond->ShowCurrent(0) * 1.05; fHCurrent[1] = mvSecond->ShowCurrent(1) * 1.05; fHCurrent[2] = mvSecond->ShowCurrent(2) * 1.05; fHCurrent[3] = mvSecond->ShowCurrent(3) * 1.05; } else fHCurrent[0] = fHCurrent[1] = fHCurrent[2] = fHCurrent[3] = 0.0; // gdy nie ma człona } else { // normalne pokazywanie fHCurrent[0] = mvControlled->ShowCurrent(0); fHCurrent[1] = mvControlled->ShowCurrent(1); fHCurrent[2] = mvControlled->ShowCurrent(2); fHCurrent[3] = mvControlled->ShowCurrent(3); } bool kier = (DynamicObject->DirectionGet() * mvOccupied->CabOccupied > 0); TDynamicObject *p = DynamicObject->GetFirstDynamic(mvOccupied->CabOccupied < 0 ? end::rear : end::front, 4); int in = 0; fEIMParams[0][6] = 0; iCarNo = 0; iPowerNo = 0; iUnitNo = 1; for (int i = 0; i < 8; i++) { bMains[i] = false; fCntVol[i] = 0.0f; bPants[i][0] = false; bPants[i][1] = false; bFuse[i] = false; bBatt[i] = false; bConv[i] = false; bComp[i][0] = false; bComp[i][1] = false; // bComp[i][2] = false; // bComp[i][3] = false; bHeat[i] = false; } bCompressors.clear(); for (int i = 0; i < 20; i++) { if (p) { fPress[i][0] = p->MoverParameters->BrakePress; fPress[i][1] = p->MoverParameters->PipePress; fPress[i][2] = p->MoverParameters->ScndPipePress; fPress[i][3] = p->MoverParameters->CntrlPipePress; fPress[i][4] = p->MoverParameters->Hamulec->GetBRP(); fPress[i][5] = (p->MoverParameters->TotalMass - p->MoverParameters->Mred) * 0.001; fPress[i][6] = p->MoverParameters->SpringBrake.SBP; bBrakes[i][0] = p->MoverParameters->SpringBrake.IsActive; bBrakes[i][1] = p->MoverParameters->SpringBrake.ShuttOff; bDoors[i][1] = (p->MoverParameters->Doors.instances[side::left].position > 0.f); bDoors[i][2] = (p->MoverParameters->Doors.instances[side::right].position > 0.f); bDoors[i][3] = (p->MoverParameters->Doors.instances[side::left].step_position > 0.f); bDoors[i][4] = (p->MoverParameters->Doors.instances[side::right].step_position > 0.f); bDoors[i][0] = (bDoors[i][1] || bDoors[i][2]); iDoorNo[i] = p->iAnimType[ANIM_DOORS]; iUnits[i] = iUnitNo; cCode[i] = p->MoverParameters->TypeName[p->MoverParameters->TypeName.length() - 1]; asCarName[i] = p->name(); if (p->MoverParameters->EnginePowerSource.SourceType == TPowerSource::CurrentCollector) { bPants[iUnitNo - 1][end::front] = (bPants[iUnitNo - 1][end::front] || p->MoverParameters->Pantographs[end::front].is_active); bPants[iUnitNo - 1][end::rear] = (bPants[iUnitNo - 1][end::rear] || p->MoverParameters->Pantographs[end::rear].is_active); } // TBD, TODO: clean up compressor data arrangement? if (iUnitNo <= 8) { bComp[iUnitNo - 1][0] = (bComp[iUnitNo - 1][0] || p->MoverParameters->CompressorAllow || (p->MoverParameters->CompressorStart == start_t::automatic)); } if (p->MoverParameters->CompressorSpeed > 0.00001) { if (iUnitNo <= 8) { bComp[iUnitNo - 1][1] = (bComp[iUnitNo - 1][1] || p->MoverParameters->CompressorFlag); } bCompressors.emplace_back(p->MoverParameters->CompressorAllow || (p->MoverParameters->CompressorStart == start_t::automatic), p->MoverParameters->CompressorFlag, i); } bSlip[i] = p->MoverParameters->SlippingWheels; if ((in < 8) && (p->MoverParameters->eimc[eimc_p_Pmax] > 1)) { fEIMParams[1 + in][0] = p->MoverParameters->eimv[eimv_Fmax]; fEIMParams[1 + in][1] = std::max(fEIMParams[1 + in][0], 0.f); fEIMParams[1 + in][2] = -std::min(fEIMParams[1 + in][0], 0.f); fEIMParams[1 + in][3] = p->MoverParameters->eimv[eimv_Fmax] / std::max(p->MoverParameters->eimv[eimv_Fful], 1.); fEIMParams[1 + in][4] = std::max(fEIMParams[1 + in][3], 0.f); fEIMParams[1 + in][5] = -std::min(fEIMParams[1 + in][3], 0.f); fEIMParams[1 + in][6] = p->MoverParameters->eimv[eimv_If]; fEIMParams[1 + in][7] = p->MoverParameters->eimv[eimv_U]; fEIMParams[1 + in][8] = p->MoverParameters->Itot; // p->MoverParameters->eimv[eimv_Ipoj]; fEIMParams[1 + in][9] = p->MoverParameters->EngineVoltage; fEIMParams[0][6] += fEIMParams[1 + in][8]; bMains[in] = p->MoverParameters->Mains; fCntVol[in] = p->MoverParameters->BatteryVoltage; bFuse[in] = p->MoverParameters->FuseFlag; bBatt[in] = p->MoverParameters->Battery; bConv[in] = p->MoverParameters->ConverterFlag; bHeat[in] = p->MoverParameters->Heating; // bComp[in][2] = (p->MoverParameters->CompressorAllow || (p->MoverParameters->CompressorStart == start_t::automatic)); // bComp[in][3] = (p->MoverParameters->CompressorFlag); in++; iPowerNo = in; } if ((in < 8) && ((p->MoverParameters->EngineType == TEngineType::DieselEngine) || (p->MoverParameters->EngineType == TEngineType::DieselElectric))) { fDieselParams[1 + in][0] = p->MoverParameters->enrot * 60; fDieselParams[1 + in][1] = p->MoverParameters->nrot; fDieselParams[1 + in][2] = p->MoverParameters->RList[p->MoverParameters->MainCtrlPos].R; fDieselParams[1 + in][3] = p->MoverParameters->dizel_fill; fDieselParams[1 + in][4] = p->MoverParameters->RList[p->MoverParameters->MainCtrlPos].Mn; fDieselParams[1 + in][5] = p->MoverParameters->dizel_engage; fDieselParams[1 + in][6] = p->MoverParameters->dizel_heat.Twy; fDieselParams[1 + in][7] = p->MoverParameters->OilPump.pressure; fDieselParams[1 + in][8] = p->MoverParameters->dizel_heat.Ts; fDieselParams[1 + in][9] = p->MoverParameters->hydro_R_Fill; bMains[in] = p->MoverParameters->Mains; fCntVol[in] = p->MoverParameters->BatteryVoltage; bFuse[in] = p->MoverParameters->FuseFlag; bBatt[in] = p->MoverParameters->Battery; bConv[in] = p->MoverParameters->ConverterFlag; bHeat[in] = p->MoverParameters->Heating; in++; iPowerNo = in; } if ((kier ? p->Next(coupling::permanent) : p->Prev(coupling::permanent)) != (kier ? p->Next(coupling::control) : p->Prev(coupling::control))) iUnitNo++; p = (kier ? p->Next(coupling::control) : p->Prev(coupling::control)); iCarNo = i + 1; } else { fPress[i][0] = fPress[i][1] = fPress[i][2] = fPress[i][3] = fPress[i][4] = fPress[i][5] = 0; bDoors[i][0] = bDoors[i][1] = bDoors[i][2] = bDoors[i][3] = bDoors[i][4] = false; bBrakes[i][0] = bBrakes[i][1] = false; bSlip[i] = false; iUnits[i] = 0; cCode[i] = 0; //'0'; asCarName[i] = ""; } } // if (mvControlled == mvOccupied) // fEIMParams[0][3] = mvControlled->eimv[eimv_Fzad]; // procent zadany // else // fEIMParams[0][3] = // mvControlled->eimv[eimv_Fzad] - mvOccupied->LocalBrakeRatio(); // procent zadany fEIMParams[0][3] = mvOccupied->eimic_real; fEIMParams[0][4] = std::max(fEIMParams[0][3], 0.f); fEIMParams[0][5] = -std::min(fEIMParams[0][3], 0.f); fEIMParams[0][1] = fEIMParams[0][4] * mvControlled->eimv[eimv_Fful]; fEIMParams[0][2] = fEIMParams[0][5] * mvControlled->eimv[eimv_Fful]; fEIMParams[0][0] = fEIMParams[0][1] - fEIMParams[0][2]; fEIMParams[0][7] = 0; fEIMParams[0][8] = 0; fEIMParams[0][9] = 0; for (int i = in; i < 8; i++) { for (int j = 0; j <= 9; j++) { fEIMParams[1 + i][j] = 0; fDieselParams[1 + i][j] = 0; } } #ifdef _WIN32 if (Global.iFeedbackMode == 4) { // wykonywać tylko gdy wyprowadzone na pulpit // Ra: sterowanie miernikiem: zbiornik główny Console::ValueSet(0, mvOccupied->Compressor); // Ra: sterowanie miernikiem: przewód główny Console::ValueSet(1, mvOccupied->PipePress); // Ra: sterowanie miernikiem: cylinder hamulcowy Console::ValueSet(2, mvOccupied->BrakePress); // woltomierz wysokiego napięcia Console::ValueSet(3, fHVoltage); // Ra: sterowanie miernikiem: drugi amperomierz Console::ValueSet(4, fHCurrent[2]); // pierwszy amperomierz; dla EZT prąd całkowity Console::ValueSet(5, fHCurrent[(mvControlled->TrainType & dt_EZT) ? 0 : 1]); // Ra: prędkość na pin 43 - wyjście analogowe (to nie jest PWM); skakanie zapewnia mechanika napędu Console::ValueSet(6, fTachoVelocity); } #endif //------------------ // hunter-261211: nadmiarowy przetwornicy i ogrzewania // Ra 15-01: to musi stąd wylecieć - zależności nie mogą być w kabinie if (mvControlled->ConverterFlag == true) { fConverterTimer += Deltatime; if ((mvControlled->CompressorFlag == true) && (mvControlled->CompressorPower == 1) && ((mvControlled->EngineType == TEngineType::ElectricSeriesMotor) || (mvControlled->TrainType == dt_EZT)) && (DynamicObject->Controller == Humandriver) // hunter-110212: poprawka dla EZT && (false == DynamicObject->Mechanik->AIControllFlag)) { // hunter-091012: poprawka (zmiana warunku z CompressorPower /rozne od 0/ na /rowne 1/) if (fConverterTimer < fConverterPrzekaznik) { mvControlled->ConvOvldFlag = true; if (mvControlled->TrainType != dt_EZT) mvControlled->MainSwitch(false, (mvControlled->TrainType == dt_EZT ? range_t::unit : range_t::local)); } else if (fConverterTimer >= fConverterPrzekaznik) { // changed switch from always true to take into account state of the compressor switch mvControlled->CompressorSwitch(mvControlled->CompressorAllow); } } } else fConverterTimer = 0; //------------------ auto const lowvoltagepower{mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable}; // youBy - prad w drugim czlonie: galaz lub calosc { TDynamicObject *tmp{nullptr}; if (DynamicObject->NextConnected()) if ((TestFlag(mvControlled->Couplers[end::rear].CouplingFlag, coupling::control)) && (mvOccupied->CabOccupied == 1)) tmp = DynamicObject->NextConnected(); if (DynamicObject->PrevConnected()) if ((TestFlag(mvControlled->Couplers[end::front].CouplingFlag, coupling::control)) && (mvOccupied->CabOccupied == -1)) tmp = DynamicObject->PrevConnected(); if (tmp) { if (tmp->MoverParameters->Power > 0) { if (ggI1B.SubModel) { ggI1B.UpdateValue(tmp->MoverParameters->ShowCurrent(1)); ggI1B.Update(); } if (ggI2B.SubModel) { ggI2B.UpdateValue(tmp->MoverParameters->ShowCurrent(2)); ggI2B.Update(); } if (ggI3B.SubModel) { ggI3B.UpdateValue(tmp->MoverParameters->ShowCurrent(3)); ggI3B.Update(); } if (ggItotalB.SubModel) { ggItotalB.UpdateValue(tmp->MoverParameters->ShowCurrent(0)); ggItotalB.Update(); } if (ggWater1TempB.SubModel) { ggWater1TempB.UpdateValue(tmp->MoverParameters->dizel_heat.temperatura1); ggWater1TempB.Update(); } if (ggOilPressB.SubModel) { ggOilPressB.UpdateValue(tmp->MoverParameters->OilPump.pressure); ggOilPressB.Update(); } } } } // McZapkie-300302: zegarek if (ggClockMInd.SubModel) { ggClockSInd.UpdateValue(simulation::Time.data().wSecond); ggClockSInd.Update(); ggClockMInd.UpdateValue(simulation::Time.data().wMinute); ggClockMInd.Update(); ggClockHInd.UpdateValue(simulation::Time.data().wHour + simulation::Time.data().wMinute / 60.0); ggClockHInd.Update(); } Cabine[iCabn].Update(lowvoltagepower); // nowy sposób ustawienia animacji /* if (ggZbS.SubModel) { ggZbS.UpdateValue(mvOccupied->Handle->GetCP()); ggZbS.Update(); } */ // replacement for the above. TODO: move it to a more suitable place m_brakehandlecp = mvOccupied->Handle->GetCP(); // youBy - napiecie na silnikach if (ggEngineVoltage.SubModel) { if (mvControlled->DynamicBrakeFlag) { ggEngineVoltage.UpdateValue(std::abs(mvControlled->Im * 5)); } else { int x; if ((mvControlled->TrainType == dt_ET42) && (mvControlled->Imax == mvControlled->ImaxHi)) x = 1; else x = 2; if ((mvControlled->RList[mvControlled->MainCtrlActualPos].Mn > 0) && (std::abs(mvControlled->Im) > 0)) { ggEngineVoltage.UpdateValue((x * (std::abs(mvControlled->EngineVoltage) - mvControlled->RList[mvControlled->MainCtrlActualPos].R * std::abs(mvControlled->Im)) / mvControlled->RList[mvControlled->MainCtrlActualPos].Mn)); } else { ggEngineVoltage.UpdateValue(0); } } ggEngineVoltage.Update(); } // Winger 140404 - woltomierz NN if (ggLVoltage.SubModel) { // NOTE: since we don't have functional converter object, we're faking it here by simple check whether converter is on // TODO: implement object-based circuits and power systems model so we can have this working more properly ggLVoltage.UpdateValue(std::max((mvOccupied->Power110vIsAvailable ? mvOccupied->NominalBatteryVoltage : 0.0), (mvOccupied->Power24vIsAvailable ? mvOccupied->BatteryVoltage : 0.0))); ggLVoltage.Update(); } if (mvControlled->EngineType == TEngineType::DieselElectric) { // ustawienie zmiennych dla silnika spalinowego fEngine[1] = mvControlled->ShowEngineRotation(1); fEngine[2] = mvControlled->ShowEngineRotation(2); } else if (mvControlled->EngineType == TEngineType::DieselEngine) { // albo dla innego spalinowego fEngine[1] = mvControlled->ShowEngineRotation(1); fEngine[2] = mvControlled->ShowEngineRotation(2); fEngine[3] = mvControlled->ShowEngineRotation(3); if (ggMainGearStatus.SubModel) { if (mvControlled->Mains) ggMainGearStatus.UpdateValue(1.1 - std::abs(mvControlled->dizel_automaticgearstatus)); else ggMainGearStatus.UpdateValue(0.0); ggMainGearStatus.Update(); } if ((ggIgnitionKey.SubModel) && (ggIgnitionKey.GetDesiredValue() == 0.0)) { ggIgnitionKey.UpdateValue((mvControlled->Mains) || (mvControlled->dizel_startup) || (fMainRelayTimer > 0.f) || ((ggMainButton.SubModel != nullptr) && (ggMainButton.GetDesiredValue() > 0.95)) || ((ggMainOnButton.SubModel != nullptr) && (ggMainOnButton.GetDesiredValue() > 0.95))); } ggIgnitionKey.Update(); } if (mvControlled->SlippingWheels) { // Ra 2014-12: lokomotywy 181/182 dostają SlippingWheels po zahamowaniu powyżej 2.85 bara i buczały double veldiff = (DynamicObject->GetVelocity() - fTachoVelocity) / mvControlled->Vmax; if (veldiff < -0.01) { // 1% Vmax rezerwy, żeby 181/182 nie buczały po zahamowaniu, ale to proteza if (std::abs(mvControlled->Im) > 10.0) { btLampkaPoslizg.Turn(true); } } } else { btLampkaPoslizg.Turn(false); } // Lampka pracujacej sprezacki if (mvControlled->CompressorFlag || mvOccupied->CompressorFlag) btCompressors.Turn(true); else btCompressors.Turn(false); // Lampka zezwolenia na hamowanie ED if (mvControlled->EpFuse) btEDenabled.Turn(true); else btEDenabled.Turn(false); // Lampka aktywowanej kabiny if (mvControlled->CabActive != 0) { btCabActived.Turn(true); } else { btCabActived.Turn(false); } if (mvControlled->Battery && mvControlled->CabActive != 0) btAKLVents.Turn(true); else btAKLVents.Turn(false); if (true == lowvoltagepower) { // McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa if (mvOccupied->SecuritySystem.is_vigilance_blinking()) { if (fBlinkTimer > fCzuwakBlink) fBlinkTimer = -fCzuwakBlink; else fBlinkTimer += Deltatime; btLampkaCzuwaka.Turn(fBlinkTimer > 0); } else { fBlinkTimer = 0.0; btLampkaCzuwaka.Turn(false); } btLampkaSHP.Turn(mvOccupied->SecuritySystem.is_cabsignal_blinking()); btLampkaCzuwakaSHP.Turn(btLampkaSHP.GetValue() || btLampkaCzuwaka.GetValue()); btLampkaWylSzybki.Turn((((m_linebreakerstate == 2) || (true == mvControlled->Mains)) ? true : false)); btLampkaWylSzybkiOff.Turn((((m_linebreakerstate == 2) || (true == mvControlled->Mains)) ? false : true)); btLampkaMainBreakerReady.Turn((((mvControlled->MainsInitTimeCountdown > 0.0) || (m_linebreakerstate == 2) || (true == mvControlled->Mains)) ? false : true)); btLampkaMainBreakerBlinkingIfReady.Turn( (((m_linebreakerstate == 2) || (true == mvControlled->Mains) || ((mvControlled->MainsInitTimeCountdown < 0.0) && (simulation::Time.data().wMilliseconds > 500))) ? true : false)); btLampkaPrzetw.Turn(mvOccupied->Power110vIsAvailable); btLampkaPrzetwOff.Turn(false == mvOccupied->Power110vIsAvailable); btLampkaNadmPrzetw.Turn(Dynamic()->Mechanik ? Dynamic()->Mechanik->IsAnyConverterOverloadRelayOpen : mvControlled->ConvOvldFlag); btLampkaOpory.Turn(mvControlled->StLinFlag ? mvControlled->ResistorsFlagCheck() : false); btLampkaBezoporowa.Turn((true == mvControlled->ResistorsFlagCheck()) || (mvControlled->MainCtrlActualPos == 0)); // do EU04 btLampkaStyczn.Turn(((mvControlled->StLinFlag) || (mvControlled->ControlPressureSwitch)) ? false : (mvControlled->BrakePress < 1.0)); // mozna prowadzic rozruch btLampkaPrzekRozn.Turn(((mvControlled->GroundRelay) || (mvControlled->ControlPressureSwitch)) ? false : (mvControlled->BrakePress < 1.0)); // relay is off and needs a reset btLampkaNadmSil.Turn(((false == mvControlled->FuseFlagCheck()) || (mvControlled->ControlPressureSwitch)) ? false : (mvControlled->BrakePress < 1.0)); // relay is off and needs a reset if (((mvControlled->CabOccupied == 1) && (TestFlag(mvControlled->Couplers[end::rear].CouplingFlag, coupling::control))) || ((mvControlled->CabOccupied == -1) && (TestFlag(mvControlled->Couplers[end::front].CouplingFlag, coupling::control)))) { btLampkaUkrotnienie.Turn(true); } else { btLampkaUkrotnienie.Turn(false); } // if // ((TestFlag(mvControlled->BrakeStatus,+b_Rused+b_Ractive)))//Lampka drugiego stopnia hamowania btLampkaHamPosp.Turn((TestFlag(mvOccupied->Hamulec->GetBrakeStatus(), 1))); // lampka drugiego stopnia hamowania // TODO: youBy wyciągnąć flagę wysokiego stopnia // hunter-121211: lampka zanikowo-pradowego wentylatorow: btLampkaNadmWent.Turn((mvControlled->RventRot < 5.0) && (mvControlled->ResistorsFlagCheck())); //------- btLampkaWysRozr.Turn(!(mvControlled->Imax < mvControlled->ImaxHi)); if ((false == mvControlled->DelayCtrlFlag) && ((mvControlled->ScndCtrlActualPos > 0) || ((mvControlled->RList[mvControlled->MainCtrlActualPos].ScndAct != 0) && (mvControlled->RList[mvControlled->MainCtrlActualPos].ScndAct != 255)))) { btLampkaBoczniki.Turn(true); } else { btLampkaBoczniki.Turn(false); } btLampkaNapNastHam.Turn(mvControlled->DirActive != 0); // napiecie na nastawniku hamulcowym btLampkaSprezarka.Turn(mvControlled->CompressorFlag); // mutopsitka dziala btLampkaSprezarkaOff.Turn(false == mvControlled->CompressorFlag); btLampkaFuelPumpOff.Turn(false == mvControlled->FuelPump.is_active); // boczniki unsigned char scp; // Ra: dopisałem "unsigned" // Ra: w SU45 boczniki wchodzą na MainCtrlPos, a nie na MainCtrlActualPos // - pokićkał ktoś? scp = mvControlled->RList[mvControlled->MainCtrlPos].ScndAct; scp = (scp == 255 ? 0 : scp); // Ra: whatta hella is this? if ((mvControlled->ScndCtrlPos > 0) || (mvControlled->ScndInMain != 0) && (scp > 0)) { // boczniki pojedynczo btLampkaBocznik1.Turn(true); btLampkaBocznik2.Turn(mvControlled->ScndCtrlPos > 1); btLampkaBocznik3.Turn(mvControlled->ScndCtrlPos > 2); btLampkaBocznik4.Turn(mvControlled->ScndCtrlPos > 3); } else { // wyłączone wszystkie cztery btLampkaBocznik1.Turn(false); btLampkaBocznik2.Turn(false); btLampkaBocznik3.Turn(false); btLampkaBocznik4.Turn(false); } if (mvControlled->Signalling == true) { if (mvOccupied->BrakePress >= 1.45f) { btLampkaHamowanie1zes.Turn(true); } if (mvControlled->BrakePress < 0.75f) { btLampkaHamowanie1zes.Turn(false); } } else { btLampkaHamowanie1zes.Turn(false); } switch (mvControlled->TrainType) { // zależnie od typu lokomotywy case dt_EZT: { btLampkaHamienie.Turn((mvControlled->BrakePress >= 0.2) && mvControlled->Signalling); break; } case dt_ET41: { // odhamowanie drugiego członu if (mvSecond) { // bo może komuś przyjść do głowy jeżdżenie jednym członem btLampkaHamienie.Turn(mvSecond->BrakePress < 0.4); } break; } default: { btLampkaHamienie.Turn((mvOccupied->BrakePress >= 0.1) || mvControlled->DynamicBrakeFlag); btLampkaBrakingOff.Turn((mvOccupied->BrakePress < 0.1) && (false == mvControlled->DynamicBrakeFlag)); break; } } // KURS90 btLampkaMaxSila.Turn(abs(mvControlled->Im) >= 350); btLampkaPrzekrMaxSila.Turn(abs(mvControlled->Im) >= 450); btLampkaRadio.Turn(mvOccupied->Radio); btLampkaRadioMessage.Turn(radio_message_played); btLampkaRadioStop.Turn(mvOccupied->Radio && mvOccupied->RadioStopFlag); btLampkaHamulecReczny.Turn(mvOccupied->ManualBrakePos > 0); // NBMX wrzesien 2003 - drzwi oraz sygnał odjazdu if (DynamicObject->Mechanik != nullptr) { btLampkaDoorLeft.Turn(DynamicObject->Mechanik->IsAnyDoorOpen[(cab_to_end() == end::front ? side::left : side::right)]); btLampkaDoorRight.Turn(DynamicObject->Mechanik->IsAnyDoorOpen[(cab_to_end() == end::front ? side::right : side::left)]); } btLampkaBlokadaDrzwi.Turn(mvOccupied->Doors.is_locked); btLampkaDoorLockOff.Turn(false == mvOccupied->Doors.lock_enabled); btLampkaDepartureSignal.Turn(mvControlled->DepartureSignal); btLampkaNapNastHam.Turn((mvControlled->DirActive != 0) && (mvOccupied->EpFuse)); // napiecie na nastawniku hamulcowym // Wylaczanie lampek kierunku gdy jedziemy // Feature uruchamiany z fiz z sekcji Ctrl. wpisem HideDirStatusWhenMoving=Yes (domyslnie No) if (mvOccupied->HideDirStatusWhenMoving && // Czy ta funkcja jest w ogole wlaczona mvOccupied->Vel > mvOccupied->HideDirStatusSpeed) // Uzaleznienie od predkosci { btLampkaForward.Turn(false); btLampkaBackward.Turn(false); btLampkaNeutral.Turn(false); } else { btLampkaForward.Turn(mvControlled->DirActive > 0); // jazda do przodu btLampkaBackward.Turn(mvControlled->DirActive < 0); // jazda do tyłu btLampkaNeutral.Turn(mvControlled->DirActive == 0); // kierunek neutral } btLampkaED.Turn(mvControlled->DynamicBrakeFlag); // hamulec ED btLampkaBrakeProfileG.Turn(TestFlag(mvOccupied->BrakeDelayFlag, bdelay_G)); btLampkaBrakeProfileP.Turn(TestFlag(mvOccupied->BrakeDelayFlag, bdelay_P)); btLampkaBrakeProfileR.Turn(TestFlag(mvOccupied->BrakeDelayFlag, bdelay_R)); btLampkaSpringBrakeActive.Turn(mvOccupied->SpringBrake.IsActive); btLampkaSpringBrakeInactive.Turn(!mvOccupied->SpringBrake.IsActive); // light indicators // NOTE: sides are hardcoded to deal with setups where single cab is equipped with all indicators btLampkaUpperLight.Turn((mvOccupied->iLights[end::front] & light::headlight_upper) != 0); btLampkaLeftLight.Turn((mvOccupied->iLights[end::front] & light::headlight_left) != 0); btLampkaRightLight.Turn((mvOccupied->iLights[end::front] & light::headlight_right) != 0); btLampkaLeftEndLight.Turn((mvOccupied->iLights[end::front] & light::redmarker_left) != 0); btLampkaRightEndLight.Turn((mvOccupied->iLights[end::front] & light::redmarker_right) != 0); btLampkaRearUpperLight.Turn((mvOccupied->iLights[end::rear] & light::headlight_upper) != 0); btLampkaRearLeftLight.Turn((mvOccupied->iLights[end::rear] & light::headlight_left) != 0); btLampkaRearRightLight.Turn((mvOccupied->iLights[end::rear] & light::headlight_right) != 0); btLampkaRearLeftEndLight.Turn((mvOccupied->iLights[end::rear] & light::redmarker_left) != 0); btLampkaRearRightEndLight.Turn((mvOccupied->iLights[end::rear] & light::redmarker_right) != 0); // others btLampkaMalfunction.Turn(mvControlled->dizel_heat.PA); // overheat indicator lamps btLampkaOilOverheat.Turn(mvControlled->dizel_heat.oil.is_hot); btLampkaWaterOverheat.Turn(mvControlled->dizel_heat.water.is_hot); btLampkaWaterAuxOverheat.Turn(mvControlled->dizel_heat.water_aux.is_hot); btLampkaEngineOverheat.Turn(mvControlled->dizel_heat.engine_is_hot); btLampkaMotorBlowers.Turn((mvControlled->MotorBlowers[end::front].is_active) && (mvControlled->MotorBlowers[end::rear].is_active)); btLampkaCoolingFans.Turn(mvControlled->RventRot > 1.0); btLampkaTempomat.Turn(mvOccupied->SpeedCtrlUnit.IsActive); btLampkaDistanceCounter.Turn(m_distancecounter >= 0.f); // universal devices state indicators for (auto idx = 0; idx < btUniversals.size(); ++idx) { btUniversals[idx].Turn(ggUniversals[idx].GetValue() > 0.5); } } else { // wylaczone btLampkaCzuwaka.Turn(false); btLampkaSHP.Turn(false); btLampkaCzuwakaSHP.Turn(false); btLampkaWylSzybki.Turn(false); btLampkaWylSzybkiOff.Turn(false); btLampkaMainBreakerReady.Turn(false); btLampkaMainBreakerBlinkingIfReady.Turn(false); btLampkaWysRozr.Turn(false); btLampkaOpory.Turn(false); btLampkaStyczn.Turn(false); btLampkaUkrotnienie.Turn(false); btLampkaHamPosp.Turn(false); btLampkaBoczniki.Turn(false); btLampkaNapNastHam.Turn(false); btLampkaPrzetw.Turn(false); btLampkaPrzetwOff.Turn(false); btLampkaNadmPrzetw.Turn(false); btLampkaSprezarka.Turn(false); btLampkaSprezarkaOff.Turn(false); btLampkaFuelPumpOff.Turn(false); btLampkaBezoporowa.Turn(false); btLampkaHamowanie1zes.Turn(false); btLampkaHamienie.Turn(false); btLampkaBrakingOff.Turn(false); btLampkaBrakeProfileG.Turn(false); btLampkaBrakeProfileP.Turn(false); btLampkaBrakeProfileR.Turn(false); btLampkaSpringBrakeActive.Turn(false); btLampkaSpringBrakeInactive.Turn(false); // overheat indicator lamps off btLampkaOilOverheat.Turn(false); btLampkaWaterOverheat.Turn(false); btLampkaWaterAuxOverheat.Turn(false); btLampkaEngineOverheat.Turn(false); btLampkaMaxSila.Turn(false); btLampkaPrzekrMaxSila.Turn(false); btLampkaRadio.Turn(false); btLampkaRadioMessage.Turn(false); btLampkaRadioStop.Turn(false); btLampkaHamulecReczny.Turn(false); btLampkaDoorLeft.Turn(false); btLampkaDoorRight.Turn(false); btLampkaBlokadaDrzwi.Turn(false); btLampkaDoorLockOff.Turn(false); btLampkaDepartureSignal.Turn(false); btLampkaNapNastHam.Turn(false); btLampkaForward.Turn(false); btLampkaBackward.Turn(false); btLampkaNeutral.Turn(false); btLampkaED.Turn(false); // light indicators btLampkaUpperLight.Turn(false); btLampkaLeftLight.Turn(false); btLampkaRightLight.Turn(false); btLampkaLeftEndLight.Turn(false); btLampkaRightEndLight.Turn(false); btLampkaRearUpperLight.Turn(false); btLampkaRearLeftLight.Turn(false); btLampkaRearRightLight.Turn(false); btLampkaRearLeftEndLight.Turn(false); btLampkaRearRightEndLight.Turn(false); // others btLampkaMalfunction.Turn(false); btLampkaMotorBlowers.Turn(false); btLampkaCoolingFans.Turn(false); btLampkaTempomat.Turn(false); btLampkaDistanceCounter.Turn(false); // universal devices state indicators for (auto &universal : btUniversals) { universal.Turn(false); } } { // yB - wskazniki drugiego czlonu TDynamicObject *tmp{nullptr}; //=mvControlled->mvSecond; //Ra 2014-07: trzeba to jeszcze wyjąć z kabiny... // Ra 2014-07: no nie ma potrzeby szukać tego w każdej klatce if ((TestFlag(mvControlled->Couplers[1].CouplingFlag, coupling::control)) && (mvOccupied->CabOccupied > 0)) tmp = DynamicObject->NextConnected(); if ((TestFlag(mvControlled->Couplers[0].CouplingFlag, coupling::control)) && (mvOccupied->CabOccupied < 0)) tmp = DynamicObject->PrevConnected(); if (tmp) { if (lowvoltagepower) { auto const *mover{tmp->MoverParameters}; btLampkaWylSzybkiB.Turn(mover->Mains); btLampkaWylSzybkiBOff.Turn((false == mover->Mains) /*&& ( mover->MainsInitTimeCountdown <= 0.0 )*/ /*&& ( fHVoltage != 0.0 )*/); btLampkaOporyB.Turn(mover->ResistorsFlagCheck()); btLampkaBezoporowaB.Turn((true == mover->ResistorsFlagCheck()) || (mover->MainCtrlActualPos == 0)); // do EU04 if ((mover->StLinFlag) || (mover->ControlPressureSwitch)) { btLampkaStycznB.Turn(false); } else if (mover->BrakePress < 1.0) { btLampkaStycznB.Turn(true); // mozna prowadzic rozruch } // hunter-271211: sygnalizacja poslizgu w pierwszym pojezdzie, gdy wystapi w drugim if (mover->SlippingWheels) { // Ra 2014-12: lokomotywy 181/182 dostają SlippingWheels po zahamowaniu powyżej 2.85 bara i buczały auto const veldiff{(DynamicObject->GetVelocity() - fTachoVelocity) / mvControlled->Vmax}; if (veldiff < -0.01) { // 1% Vmax rezerwy, żeby 181/182 nie buczały po zahamowaniu, ale to proteza auto const lightstate{std::abs(mover->Im) > 10.0}; btLampkaPoslizg.Turn(btLampkaPoslizg.GetValue() || lightstate); } } btLampkaSprezarkaB.Turn(mover->CompressorFlag); // mutopsitka dziala btLampkaSprezarkaBOff.Turn(false == mover->CompressorFlag); if (mvControlled->Signalling == true) { if (mover->BrakePress >= 1.45f) { btLampkaHamowanie2zes.Turn(true); } if (mover->BrakePress < 0.75f) { btLampkaHamowanie2zes.Turn(false); } } else { btLampkaHamowanie2zes.Turn(false); } btLampkaNadmPrzetwB.Turn(mover->ConvOvldFlag); // nadmiarowy przetwornicy? btLampkaPrzetwB.Turn(mover->ConverterFlag); // zalaczenie przetwornicy btLampkaPrzetwBOff.Turn(false == mover->ConverterFlag); btLampkaHVoltageB.Turn(mover->NoVoltRelay && mover->OvervoltageRelay); btLampkaMalfunctionB.Turn(mover->dizel_heat.PA); // motor fuse indicator turns on if the fuse was blown in any unit under control if (mover->Mains) { btLampkaNadmSil.Turn(btLampkaNadmSil.GetValue() || mover->FuseFlagCheck()); } } else // wylaczone { btLampkaWylSzybkiB.Turn(false); btLampkaWylSzybkiBOff.Turn(false); btLampkaOporyB.Turn(false); btLampkaStycznB.Turn(false); btLampkaSprezarkaB.Turn(false); btLampkaSprezarkaBOff.Turn(false); btLampkaBezoporowaB.Turn(false); btLampkaHamowanie2zes.Turn(false); btLampkaNadmPrzetwB.Turn(false); btLampkaPrzetwB.Turn(false); btLampkaPrzetwBOff.Turn(false); btLampkaHVoltageB.Turn(false); btLampkaMalfunctionB.Turn(false); } } } // McZapkie-080602: obroty (albo translacje) regulatorow if (ggJointCtrl.SubModel != nullptr) { // joint master controller moves forward to adjust power and backward to adjust brakes auto const brakerangemultiplier{/* NOTE: scaling disabled as it was conflicting with associating sounds with control positions ( mvControlled->CoupledCtrl ? mvControlled->MainCtrlPosNo + mvControlled->ScndCtrlPosNo : mvControlled->MainCtrlPosNo ) / static_cast(LocalBrakePosNo) */ 1}; // when SplitEDPneumaticBrake is active the negative range of the joint controller // represents the dedicated dynamic-brake lever (DBPN steps), not the pneumatic local brake auto const negativePart{mvControlled->SplitEDPneumaticBrake ? (mvControlled->DynamicBrakeCtrlPos > 0.0 ? mvControlled->DynamicBrakeCtrlPos * mvControlled->DynamicBrakeCtrlPosNo * -1 * brakerangemultiplier : 0.0) : (mvOccupied->LocalBrakePosA > 0.0 ? mvOccupied->LocalBrakePosA * LocalBrakePosNo * -1 * brakerangemultiplier : 0.0)}; ggJointCtrl.UpdateValue((negativePart < 0.0 ? negativePart : mvControlled->CoupledCtrl ? double(mvControlled->MainCtrlPos + mvControlled->ScndCtrlPos) : double(mvControlled->MainCtrlPos)), dsbNastawnikJazdy); ggJointCtrl.Update(); } if (ggMainCtrl.SubModel != nullptr) { #ifdef _WIN32 if ((DynamicObject->Mechanik != nullptr) && (false == DynamicObject->Mechanik->AIControllFlag) // nie blokujemy AI && (Global.iFeedbackMode == 4) && (Global.fCalibrateIn[2][1] != 0.0)) { set_master_controller(Console::AnalogCalibrateGet(2) * mvOccupied->MainCtrlPosNo); mvOccupied->eimic_analog = Console::AnalogCalibrateGet(2); } #endif if (mvControlled->CoupledCtrl) { ggMainCtrl.UpdateValue(double(mvControlled->MainCtrlPos + mvControlled->ScndCtrlPos), dsbNastawnikJazdy); } else { ggMainCtrl.UpdateValue(double(mvControlled->MainCtrlPos), dsbNastawnikJazdy); } ggMainCtrl.Update(); } if (ggMainCtrlAct.SubModel != nullptr) { if (mvControlled->CoupledCtrl) ggMainCtrlAct.UpdateValue(double(mvControlled->MainCtrlActualPos + mvControlled->ScndCtrlActualPos)); else ggMainCtrlAct.UpdateValue(double(mvControlled->MainCtrlActualPos)); ggMainCtrlAct.Update(); } if (ggScndCtrl.SubModel != nullptr) { // Ra: od byte odejmowane boolean i konwertowane potem na double? if (false == ggScndCtrl.is_push()) { ggScndCtrl.UpdateValue(double(mvControlled->ScndCtrlPos - ((mvControlled->TrainType == dt_ET42) && mvControlled->DynamicBrakeFlag)), dsbNastawnikBocz); } ggScndCtrl.Update(); } if (ggScndCtrlButton.SubModel != nullptr) { if (ggScndCtrlButton.is_toggle()) { ggScndCtrlButton.UpdateValue(((mvControlled->ScndCtrlPos > 0) ? 1.f : 0.f), dsbSwitch); } ggScndCtrlButton.Update(lowvoltagepower); } if (ggScndCtrlOffButton.SubModel != nullptr) { ggScndCtrlOffButton.Update(lowvoltagepower); } if (ggDistanceCounterButton.SubModel != nullptr) { ggDistanceCounterButton.Update(); } if (ggDirKey.SubModel != nullptr) { if (mvControlled->TrainType != dt_EZT) { ggDirKey.UpdateValue(double(mvControlled->DirActive), dsbReverserKey); } else { ggDirKey.UpdateValue(double(mvControlled->DirActive) + double(mvControlled->Imin == mvControlled->IminHi), dsbReverserKey); } ggDirKey.Update(); } if (ggBrakeCtrl.SubModel != nullptr) { #ifdef _WIN32 if (DynamicObject->Mechanik ? (DynamicObject->Mechanik->AIControllFlag ? false : (Global.iFeedbackMode == 4 /*|| (Global.bMWDmasterEnable && Global.bMWDBreakEnable)*/)) : false && Global.fCalibrateIn[0][1] != 0.0) // nie blokujemy AI { // Ra: nie najlepsze miejsce, ale na początek gdzieś to dać trzeba // Firleju: dlatego kasujemy i zastepujemy funkcją w Console if (mvOccupied->BrakeHandle == TBrakeHandle::FV4a) { double b = Console::AnalogCalibrateGet(0); b = b * 8.0 - 2.0; b = std::clamp(b, -2.0, (double)mvOccupied->BrakeCtrlPosNo); // przycięcie zmiennej do granic ggBrakeCtrl.UpdateValue(b); // przesów bez zaokrąglenia mvOccupied->BrakeLevelSet(b); } else if (mvOccupied->BrakeHandle == TBrakeHandle::FVel6) // może można usunąć ograniczenie do FV4a i FVel6? { double b = Console::AnalogCalibrateGet(0); b = b * 7.0 - 1.0; b = std::clamp(b, -1.0, (double)mvOccupied->BrakeCtrlPosNo); // przycięcie zmiennej do granic ggBrakeCtrl.UpdateValue(b); // przesów bez zaokrąglenia mvOccupied->BrakeLevelSet(b); } else { double b = Console::AnalogCalibrateGet(0); b = b * (mvOccupied->Handle->GetPos(bh_MAX) - mvOccupied->Handle->GetPos(bh_MIN)) + mvOccupied->Handle->GetPos(bh_MIN); b = std::clamp(b, mvOccupied->Handle->GetPos(bh_MIN), mvOccupied->Handle->GetPos(bh_MAX)); // przycięcie zmiennej do granic ggBrakeCtrl.UpdateValue(b); // przesów bez zaokrąglenia mvOccupied->BrakeLevelSet(b); } } else #endif { // else //standardowa prodedura z kranem powiązanym z klawiaturą // ggBrakeCtrl.UpdateValue(double(mvOccupied->BrakeCtrlPos)); ggBrakeCtrl.UpdateValue(mvOccupied->fBrakeCtrlPos); ggBrakeCtrl.Update(); } } if (ggLocalBrake.SubModel != nullptr) { #ifdef _WIN32 if ((DynamicObject->Mechanik != nullptr) && (false == DynamicObject->Mechanik->AIControllFlag) // nie blokujemy AI && (mvOccupied->BrakeLocHandle == TBrakeHandle::FD1) && ((Global.iFeedbackMode == 4) && Global.fCalibrateIn[0][1] != 0.0 /* || ( Global.bMWDmasterEnable && Global.bMWDBreakEnable )*/)) { // Ra: nie najlepsze miejsce, ale na początek gdzieś to dać trzeba // Firleju: dlatego kasujemy i zastepujemy funkcją w Console auto const b = std::clamp(Console::AnalogCalibrateGet(1), 0.f, 1.f); mvOccupied->LocalBrakePosA = b; ggLocalBrake.UpdateValue(b * LocalBrakePosNo); } else #endif { // standardowa prodedura z kranem powiązanym z klawiaturą ggLocalBrake.UpdateValue(mvOccupied->LocalBrakePosA * LocalBrakePosNo); } ggLocalBrake.Update(); } ggDirForwardButton.Update(lowvoltagepower); ggDirNeutralButton.Update(lowvoltagepower); ggDirBackwardButton.Update(lowvoltagepower); ggAlarmChain.Update(); ggBrakeProfileCtrl.Update(); ggBrakeProfileG.Update(); ggBrakeProfileR.Update(); ggBrakeOperationModeCtrl.Update(); ggWiperSw.Update(); ggMaxCurrentCtrl.UpdateValue((true == mvControlled->ShuntModeAllow ? (true == mvControlled->ShuntMode ? 1.f : 0.f) : (mvControlled->MotorOverloadRelayHighThreshold ? 1.f : 0.f))); ggMaxCurrentCtrl.Update(); // NBMX wrzesien 2003 - drzwi ggDoorLeftPermitButton.Update(lowvoltagepower); ggDoorRightPermitButton.Update(lowvoltagepower); ggDoorPermitPresetButton.Update(lowvoltagepower); ggDoorLeftButton.Update(lowvoltagepower); ggDoorRightButton.Update(lowvoltagepower); ggDoorLeftOnButton.Update(lowvoltagepower); ggDoorRightOnButton.Update(lowvoltagepower); ggDoorLeftOffButton.Update(lowvoltagepower); ggDoorRightOffButton.Update(lowvoltagepower); ggDoorAllOnButton.Update(lowvoltagepower); ggDoorAllOffButton.Update(lowvoltagepower); ggDoorSignallingButton.Update(lowvoltagepower); ggDoorStepButton.Update(lowvoltagepower); // NBMX dzwignia sprezarki ggCompressorButton.Update(); ggCompressorLocalButton.Update(); ggCompressorListButton.Update(); //--------- // hunter-080812: poprawka na ogrzewanie w elektrykach - usuniete uzaleznienie od przetwornicy if ((mvControlled->Heating == true) && (mvControlled->ConvOvldFlag == false)) btLampkaOgrzewanieSkladu.Turn(true); else btLampkaOgrzewanieSkladu.Turn(false); //---------- // lights auto const lightpower{(InstrumentLightType == 0 ? mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable : InstrumentLightType == 1 ? mvControlled->Mains : InstrumentLightType == 2 ? mvOccupied->Power110vIsAvailable : InstrumentLightType == 3 ? mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable : InstrumentLightType == 4 ? mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable : false)}; InstrumentLightActive = (InstrumentLightType == 3 ? true : // TODO: link the light state with the state of the master key InstrumentLightType == 4 ? (mvOccupied->iLights[end::front] != 0) || (mvOccupied->iLights[end::rear] != 0) : InstrumentLightActive); btInstrumentLight.Turn(InstrumentLightActive && lightpower); btDashboardLight.Turn(DashboardLightActive && lightpower); btTimetableLight.Turn(TimetableLightActive && lightpower); // guziki: ggMainOffButton.Update(); ggMainOnButton.Update(); ggMainButton.Update(); ggSecurityResetButton.Update(); ggSHPResetButton.Update(); ggReleaserButton.Update(); ggSpringBrakeOnButton.Update(); ggSpringBrakeOffButton.Update(); ggUniveralBrakeButton1.Update(); ggUniveralBrakeButton2.Update(); ggUniveralBrakeButton3.Update(); ggEPFuseButton.Update(); ggAntiSlipButton.Update(); ggSandButton.Update(); ggAutoSandButton.Update(); ggFuseButton.Update(); ggConverterFuseButton.Update(); ggStLinOffButton.Update(); ggRadioChannelSelector.Update(); ggRadioChannelPrevious.Update(); ggRadioChannelNext.Update(); ggRadioStop.Update(); ggRadioTest.Update(); ggRadioCall1.Update(); ggRadioCall3.Update(); ggRadioVolumeSelector.Update(); ggRadioVolumePrevious.Update(); ggRadioVolumeNext.Update(); ggDepartureSignalButton.Update(); /* ggPantFrontButton.Update(); ggPantRearButton.Update(); ggPantFrontButtonOff.Update(); ggPantRearButtonOff.Update(); */ ggPantAllDownButton.Update(); ggPantSelectedDownButton.Update(); ggPantSelectedButton.Update(); ggPantValvesButton.Update(); ggPantCompressorButton.Update(); ggPantCompressorValve.Update(); ggPantValvesOff.Update(); ggPantValvesUpdate.Update(); ggLightsButton.Update(); ggUpperLightButton.Update(); ggLeftLightButton.Update(); ggRightLightButton.Update(); ggLeftEndLightButton.Update(); ggRightEndLightButton.Update(); ggModernLightDimSw.Update(); // hunter-230112 ggRearUpperLightButton.Update(); ggRearLeftLightButton.Update(); ggRearRightLightButton.Update(); ggRearLeftEndLightButton.Update(); ggRearRightEndLightButton.Update(); ggDimHeadlightsButton.Update(); ggDimHeadlightsButton.Update(); //------------ ggConverterButton.Update(); ggConverterLocalButton.Update(); ggConverterOffButton.Update(); ggTrainHeatingButton.Update(); ggSignallingButton.Update(); ggNextCurrentButton.Update(); ggHornButton.Update(); ggHornLowButton.Update(); ggHornHighButton.Update(); ggWhistleButton.Update(); if (DynamicObject->Mechanik != nullptr) { ggHelperButton.UpdateValue(DynamicObject->Mechanik->HelperState); } ggHelperButton.Update(); ggSpeedControlIncreaseButton.Update(lowvoltagepower); ggSpeedControlDecreaseButton.Update(lowvoltagepower); ggSpeedControlPowerIncreaseButton.Update(lowvoltagepower); ggSpeedControlPowerDecreaseButton.Update(lowvoltagepower); for (auto &speedctrlbutton : ggSpeedCtrlButtons) { speedctrlbutton.Update(lowvoltagepower); } for (auto &universal : ggUniversals) { universal.Update(); } for (auto &item : ggInverterEnableButtons) { item.Update(); } for (auto &item : ggInverterDisableButtons) { item.Update(); } for (auto &item : ggInverterToggleButtons) { item.Update(); } for (auto &relayresetbutton : ggRelayResetButtons) { relayresetbutton.Update(); } // hunter-091012 ggInstrumentLightButton.Update(); ggDashboardLightButton.Update(); ggTimetableLightButton.Update(); ggCabLightDimButton.Update(); ggCompartmentLightsButton.Update(); ggCompartmentLightsOnButton.Update(); ggCompartmentLightsOffButton.Update(); ggBatteryButton.Update(); ggBatteryOnButton.Update(); ggBatteryOffButton.Update(); if ((ggCabActivationButton.SubModel != nullptr) && (ggCabActivationButton.type() != TGaugeType::push)) { ggCabActivationButton.UpdateValue(mvOccupied->IsCabMaster() ? 1.0 : 0.0); } ggCabActivationButton.Update(); ggWaterPumpBreakerButton.Update(); ggWaterPumpButton.Update(); ggWaterHeaterBreakerButton.Update(); ggWaterHeaterButton.Update(); ggWaterCircuitsLinkButton.Update(); ggFuelPumpButton.Update(); ggOilPumpButton.Update(); ggMotorBlowersFrontButton.Update(); ggMotorBlowersRearButton.Update(); ggMotorBlowersAllOffButton.Update(); // wyprowadzenie sygnałów dla haslera na PoKeys (zaznaczanie na taśmie) btHaslerBrakes.Turn(mvOccupied->BrakePress > 0.4); // ciśnienie w cylindrach btHaslerCurrent.Turn(mvOccupied->Im != 0.0); // prąd na silnikach // calculate current level of interior illumination { // TODO: organize it along with rest of train update in a more sensible arrangement // Ra: uzeleżnic od napięcia w obwodzie sterowania // hunter-091012: uzaleznienie jasnosci od przetwornicy int cabidx{0}; for (auto &cab : Cabine) { auto const cablightlevel = ((cab.bLight == false) ? 0.f : (cab.bLightDim == true) ? 0.4f : 1.f) * (mvOccupied->Power110vIsAvailable ? 1.f : 0.5f); if (cab.LightLevel != cablightlevel) { cab.LightLevel = cablightlevel; DynamicObject->set_cab_lights(cabidx, cab.LightLevel); } if (cabidx == iCabn) { DynamicObject->InteriorLightLevel = cablightlevel; } ++cabidx; } } // anti slip system activation, maintained while the control button is down if (mvOccupied->BrakeSystem != TBrakeSystem::ElectroPneumatic) { if (ggAntiSlipButton.GetDesiredValue() > 0.95) { mvControlled->AntiSlippingBrake(); } } // screens if (!FreeFlyModeFlag && simulation::Train == this) // don't bother if we're outside update_screens(Deltatime); // update direction relay if (prevBatState != mvOccupied->Power24vIsAvailable) SetupDirectionRelays(); if (prevDirection != mvOccupied->DirActive) UpdateDirectionRelays(); prevBatState = mvOccupied->Power24vIsAvailable; prevDirection = mvOccupied->DirActive; // sounds update_sounds(Deltatime); return true; //(DynamicObject->Update(dt)); } // koniec update void TTrain::UpdateDirectionRelays() { if (mvOccupied->DirActive < 0 && mvOccupied->Power24vIsAvailable) // wstecz Dynamic()->sDirectionRelayR.play(); if (mvOccupied->DirActive == 0 && mvOccupied->Power24vIsAvailable) // neutral Dynamic()->sDirectionRelayN.play(); if (mvOccupied->DirActive > 0 && mvOccupied->Power24vIsAvailable) // przod Dynamic()->sDirectionRelayD.play(); } void TTrain::SetupDirectionRelays() { if (mvOccupied->Power24vIsAvailable) { if (mvOccupied->DirActive < 0 && mvOccupied->Power24vIsAvailable) // wstecz Dynamic()->sDirectionRelayR.play(); if (mvOccupied->DirActive > 0 && mvOccupied->Power24vIsAvailable) // przod Dynamic()->sDirectionRelayD.play(); } else if (mvOccupied->DirActive != 0) // neutral Dynamic()->sDirectionRelayN.play(); } void TTrain::update_sounds(double const Deltatime) { double volume{0.0}; double const brakevolumescale{0.5}; // Winger-160404 - syczenie pomocniczego (luzowanie) if (m_lastlocalbrakepressure != -1.f) { // calculate rate of pressure drop in local brake cylinder, once it's been initialized auto const brakepressuredifference{mvOccupied->LocBrakePress - m_lastlocalbrakepressure}; m_localbrakepressurechange = std::lerp(m_localbrakepressurechange, 10 * (brakepressuredifference / Deltatime), 0.1f); } m_lastlocalbrakepressure = mvOccupied->LocBrakePress; // local brake, release if (rsSBHiss) { if ((m_localbrakepressurechange < -0.05f) && (mvOccupied->LocBrakePress > mvOccupied->BrakePress - 0.05)) { rsSBHiss->gain(std::clamp(rsSBHiss->m_amplitudeoffset + rsSBHiss->m_amplitudefactor * -m_localbrakepressurechange * 0.05, 0.0, 1.5)); rsSBHiss->play(sound_flags::exclusive | sound_flags::looping); } else { // don't stop the sound too abruptly volume = std::max(0.0, rsSBHiss->gain() - 0.1 * Deltatime); rsSBHiss->gain(volume); if (volume < 0.05) { rsSBHiss->stop(); } } } // local brake, engage if (rsSBHissU) { if (m_localbrakepressurechange > 0.05f) { rsSBHissU->gain(std::clamp(rsSBHissU->m_amplitudeoffset + rsSBHissU->m_amplitudefactor * m_localbrakepressurechange * 0.05, 0.0, 1.5)); rsSBHissU->play(sound_flags::exclusive | sound_flags::looping); } else { // don't stop the sound too abruptly volume = std::max(0.0, rsSBHissU->gain() - 0.1 * Deltatime); rsSBHissU->gain(volume); if (volume < 0.05) { rsSBHissU->stop(); } } } // McZapkie-280302 - syczenie // TODO: softer volume reduction than plain abrupt stop, perhaps as reusable wrapper? if ((mvOccupied->BrakeHandle == TBrakeHandle::FV4a) || (mvOccupied->BrakeHandle == TBrakeHandle::FVel6)) { // upuszczanie z PG if (rsHiss) { fPPress = std::lerp(fPPress, static_cast(mvOccupied->Handle->GetSound(s_fv4a_b)), 0.05f); volume = (fPPress > 0 ? rsHiss->m_amplitudefactor * fPPress * 0.25 + rsHiss->m_amplitudeoffset : 0); if (volume * brakevolumescale > 0.05) { rsHiss->gain(volume * brakevolumescale); rsHiss->play(sound_flags::exclusive | sound_flags::looping); } else { rsHiss->stop(); } } // napelnianie PG if (rsHissU) { fNPress = std::lerp(fNPress, static_cast(mvOccupied->Handle->GetSound(s_fv4a_u)), 0.25f); volume = (fNPress > 0 ? rsHissU->m_amplitudefactor * fNPress + rsHissU->m_amplitudeoffset : 0); if (volume * brakevolumescale > 0.05) { rsHissU->gain(volume * brakevolumescale); rsHissU->play(sound_flags::exclusive | sound_flags::looping); } else { rsHissU->stop(); } } // upuszczanie przy naglym if (rsHissE) { volume = mvOccupied->Handle->GetSound(s_fv4a_e) * rsHissE->m_amplitudefactor + rsHissE->m_amplitudeoffset; if (volume * brakevolumescale > 0.05) { rsHissE->gain(volume * brakevolumescale); rsHissE->play(sound_flags::exclusive | sound_flags::looping); } else { rsHissE->stop(); } } // upuszczanie sterujacego fala if (rsHissX) { volume = mvOccupied->Handle->GetSound(s_fv4a_x) * rsHissX->m_amplitudefactor + rsHissX->m_amplitudeoffset; if (volume * brakevolumescale > 0.05) { rsHissX->gain(volume * brakevolumescale); rsHissX->play(sound_flags::exclusive | sound_flags::looping); } else { rsHissX->stop(); } } // upuszczanie z czasowego if (rsHissT) { volume = mvOccupied->Handle->GetSound(s_fv4a_t) * rsHissT->m_amplitudefactor + +rsHissT->m_amplitudeoffset; if (volume * brakevolumescale > 0.05) { rsHissT->gain(volume * brakevolumescale); rsHissT->play(sound_flags::exclusive | sound_flags::looping); } else { rsHissT->stop(); } } } else { // jesli nie FV4a // upuszczanie z PG if (rsHiss) { fPPress = (4.0f * fPPress + std::max(0.0, mvOccupied->dpMainValve)) / (4.0f + 1.0f); volume = (fPPress > 0.0f ? 2.0 * rsHiss->m_amplitudefactor * fPPress + rsHiss->m_amplitudeoffset : 0.0); if (volume > 0.05) { rsHiss->gain(volume); rsHiss->play(sound_flags::exclusive | sound_flags::looping); } else { rsHiss->stop(); } } // napelnianie PG if (rsHissU) { fNPress = (4.0f * fNPress + std::min(0.0, mvOccupied->dpMainValve)) / (4.0f + 1.0f); volume = (fNPress < 0.0f ? -1.0 * rsHissU->m_amplitudefactor * fNPress + rsHissU->m_amplitudeoffset : 0.0); if (volume > 0.01) { rsHissU->gain(volume); rsHissU->play(sound_flags::exclusive | sound_flags::looping); } else { rsHissU->stop(); } } } // koniec nie FV4a // brakes if (rsBrake) { if ((mvOccupied->UnitBrakeForce > 10.0) && (mvOccupied->Vel > 0.05)) { auto const brakeforceratio{ std::clamp(mvOccupied->UnitBrakeForce / std::max(1.0, mvOccupied->BrakeForceR(1.0, mvOccupied->Vel) / (mvOccupied->NAxles * std::max(1, mvOccupied->NBpA))), 0.0, 1.0)}; // HACK: in external view mute the sound rather than stop it, in case there's an opening bookend it'd (re)play on sound restart after returning inside volume = (FreeFlyModeFlag ? 0.0 : rsBrake->m_amplitudeoffset + std::sqrt(brakeforceratio * std::lerp(0.4, 1.0, (mvOccupied->Vel / (1 + mvOccupied->Vmax)))) * rsBrake->m_amplitudefactor); rsBrake->pitch(rsBrake->m_frequencyoffset + mvOccupied->Vel * rsBrake->m_frequencyfactor); rsBrake->gain(volume); rsBrake->play(sound_flags::exclusive | sound_flags::looping); } else { rsBrake->stop(); } } // ambient sound // since it's typically ticking of the clock we can center it on tachometer or on middle of compartment bounding area if (rsFadeSound) { rsFadeSound->play(sound_flags::exclusive | sound_flags::looping); } if (dsbSlipAlarm) { // alarm przy poslizgu dla 181/182 - BOMBARDIER if ((mvControlled->SlippingWheels) && (DynamicObject->GetVelocity() > 1.0)) { dsbSlipAlarm->play(sound_flags::exclusive | sound_flags::looping); } else { dsbSlipAlarm->stop(); } } // dzwiek wiatru rozbijajacego sie o szyby w kabinie if (rsWindSound) { if (!FreeFlyModeFlag && !Global.CabWindowOpen && DynamicObject->GetVelocity() > 0.5) update_sounds_resonancenoise(*rsWindSound); else rsWindSound->stop(FreeFlyModeFlag); } // dzwiek rezonansu (taki drugi runningnoise w sumie) if (rsResonanceNoise) { if (!FreeFlyModeFlag && !Global.CabWindowOpen && DynamicObject->GetVelocity() > 0.5) { update_sounds_resonancenoise(*rsResonanceNoise); } else rsResonanceNoise->stop(FreeFlyModeFlag); } // szum w czasie jazdy if (rsRunningNoise) { if ((false == FreeFlyModeFlag) && (false == Global.CabWindowOpen) && (DynamicObject->GetVelocity() > 0.5)) { update_sounds_runningnoise(*rsRunningNoise); } else { // don't play the optional ending sound if the listener switches views rsRunningNoise->stop(true == FreeFlyModeFlag); } } // hunting oscillation noise if (rsHuntingNoise) { if ((false == FreeFlyModeFlag) && (false == Global.CabWindowOpen) && (DynamicObject->GetVelocity() > 0.5) && (DynamicObject->IsHunting)) { update_sounds_runningnoise(*rsHuntingNoise); // modify calculated sound volume by hunting amount auto const huntingamount = std::lerp( 0.0, 1.0, std::clamp((mvOccupied->Vel - DynamicObject->HuntingShake.fadein_begin) / (DynamicObject->HuntingShake.fadein_end - DynamicObject->HuntingShake.fadein_begin), 0.0, 1.0)); rsHuntingNoise->gain(rsHuntingNoise->gain() * huntingamount); } else { // don't play the optional ending sound if the listener switches views rsHuntingNoise->stop(true == FreeFlyModeFlag); } } // rain sound if (m_rainsound) { if ((false == FreeFlyModeFlag) && (false == Global.CabWindowOpen) && (Global.Weather == "rain:")) { if (m_rainsound->is_combined()) { m_rainsound->pitch(Global.Overcast - 1.0); } m_rainsound->gain(m_rainsound->m_amplitudeoffset + m_rainsound->m_amplitudefactor * 1.f); m_rainsound->play(sound_flags::exclusive | sound_flags::looping); } else { m_rainsound->stop(); } } if (dsbHasler) { if (fTachoCount >= 3.f) { auto const frequency{(true == dsbHasler->is_combined() ? fTachoVelocity * 0.01 : dsbHasler->m_frequencyoffset + dsbHasler->m_frequencyfactor)}; dsbHasler->pitch(frequency); dsbHasler->gain(dsbHasler->m_amplitudeoffset + dsbHasler->m_amplitudefactor); dsbHasler->play(sound_flags::exclusive | sound_flags::looping); } else if (fTachoCount < 1.f) { dsbHasler->stop(); } } // power-reliant sounds if (mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable) { // buzzer shp if (mvOccupied->SecuritySystem.is_cabsignal_beeping()) { if (dsbBuzzerShp && false == dsbBuzzerShp->is_playing()) { dsbBuzzerShp->pitch(dsbBuzzerShp->m_frequencyoffset + dsbBuzzerShp->m_frequencyfactor); dsbBuzzerShp->gain(dsbBuzzerShp->m_amplitudeoffset + dsbBuzzerShp->m_amplitudefactor); dsbBuzzerShp->play(sound_flags::looping); } } else { if (dsbBuzzerShp && true == dsbBuzzerShp->is_playing()) { dsbBuzzerShp->stop(); } } // McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa // hunter-091012: rozdzielenie alarmow if (mvOccupied->SecuritySystem.is_beeping()) { if (dsbBuzzer && false == dsbBuzzer->is_playing()) { dsbBuzzer->pitch(dsbBuzzer->m_frequencyoffset + dsbBuzzer->m_frequencyfactor); dsbBuzzer->gain(dsbBuzzer->m_amplitudeoffset + dsbBuzzer->m_amplitudefactor); dsbBuzzer->play(sound_flags::looping); #ifdef _WIN32 Console::BitsSet(1 << 14); // ustawienie bitu 16 na PoKeys #endif } } else { if (dsbBuzzer && true == dsbBuzzer->is_playing()) { dsbBuzzer->stop(); #ifdef _WIN32 Console::BitsClear(1 << 14); // ustawienie bitu 16 na PoKeys #endif } } // distance meter alert if (m_distancecounterclear) { auto const *owner{(DynamicObject->ctOwner != nullptr ? DynamicObject->ctOwner : DynamicObject->Mechanik)}; if (m_distancecounter > owner->fLength) { // play assigned sound if the train travelled its full length since meter activation // TBD: check all combinations of directions and active cab m_distancecounter = -1.f; // turn off the meter after its task is done m_distancecounterclear->pitch(m_distancecounterclear->m_frequencyoffset + m_distancecounterclear->m_frequencyfactor); m_distancecounterclear->gain(m_distancecounterclear->m_amplitudeoffset + m_distancecounterclear->m_amplitudefactor); m_distancecounterclear->play(sound_flags::exclusive); } } } else { // stop power-reliant sounds if power is cut if (dsbBuzzer) { if (true == dsbBuzzer->is_playing()) { dsbBuzzer->stop(); #ifdef _WIN32 Console::BitsClear(1 << 14); // ustawienie bitu 16 na PoKeys #endif } } if (dsbBuzzerShp && dsbBuzzerShp->is_playing()) { dsbBuzzerShp->stop(); } { } if (m_distancecounterclear) { m_distancecounterclear->stop(); } } update_sounds_radio(); } void TTrain::update_sounds_resonancenoise(sound_source &Sound) { // frequency calculation auto const normalizer{mvOccupied->Vmax * 0.01f}; auto const frequency{Sound.m_frequencyoffset + Sound.m_frequencyfactor * mvOccupied->Vel * normalizer}; // volume calculation auto volume = Sound.m_amplitudeoffset + Sound.m_amplitudefactor * std::lerp(mvOccupied->Vel / (1 + mvOccupied->Vmax), 1.0, 0.5); // scale base volume between 0.5-1.0 if (volume > 0.05) { Sound.pitch(frequency).gain(volume).play(sound_flags::exclusive | sound_flags::looping); } else { Sound.stop(); } } void TTrain::update_sounds_runningnoise(sound_source &Sound) { // frequency calculation auto const normalizer{(true == Sound.is_combined() ? mvOccupied->Vmax * 0.01f : 1.f)}; auto const frequency{Sound.m_frequencyoffset + Sound.m_frequencyfactor * mvOccupied->Vel * normalizer}; // volume calculation auto volume = Sound.m_amplitudeoffset + Sound.m_amplitudefactor * std::lerp(mvOccupied->Vel / (1 + mvOccupied->Vmax), 1.0, 0.5); // scale base volume between 0.5-1.0 if (std::abs(mvOccupied->nrot) > 0.01) { // hamulce wzmagaja halas auto const brakeforceratio{(std::clamp(mvOccupied->UnitBrakeForce / std::max(1.0, mvOccupied->BrakeForceR(1.0, mvOccupied->Vel) / (mvOccupied->NAxles * std::max(1, mvOccupied->NBpA))), 0.0, 1.0))}; volume *= 1 + 0.125 * brakeforceratio; } // scale volume by track quality // TODO: track quality and/or environment factors as separate subroutine volume *= std::lerp(0.8, 1.2, std::clamp(DynamicObject->MyTrack->iQualityFlag / 20.0, 0.0, 1.0)); // for single sample sounds muffle the playback at low speeds if (false == Sound.is_combined()) { volume *= std::lerp(0.0, 1.0, std::clamp(mvOccupied->Vel / 25.0, 0.0, 1.0)); } if (volume > 0.05) { Sound.pitch(frequency).gain(volume).play(sound_flags::exclusive | sound_flags::looping); } else { Sound.stop(); } } void TTrain::update_sounds_radio() { radio_message_played = false; if (false == m_radiomessages.empty()) { // erase completed radio messages from the list m_radiomessages.erase(std::remove_if(std::begin(m_radiomessages), std::end(m_radiomessages), [](auto const &source) { return (false == source.second->is_playing()); }), std::end(m_radiomessages)); } // adjust audibility of remaining messages based on current radio conditions auto const radioenabled{(true == mvOccupied->Radio) && (mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable)}; for (auto &message : m_radiomessages) { auto const volume{(true == radioenabled) && (Dynamic()->Mechanik != nullptr) && (message.first == RadioChannel()) ? m_radiovolume : 0.0}; message.second->gain(volume); radio_message_played |= (true == radioenabled) && (Dynamic()->Mechanik != nullptr) && (message.first == RadioChannel()); } // radiostop if (m_radiostop) { if ((true == radioenabled) && (true == mvOccupied->RadioStopFlag)) { m_radiostop->play(sound_flags::exclusive | sound_flags::looping); radio_message_played |= true; } else { m_radiostop->stop(); } } if (radio_message_played) { btLampkaRadioMessage.gain(m_radiovolume); } } 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}); } } void TTrain::add_distance(double const Distance) { auto const meterenabled{(m_distancecounter >= 0) && (mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable)}; if (true == meterenabled) { m_distancecounter += Distance * Occupied()->CabOccupied; } else { m_distancecounter = -1.f; } } 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; } void TTrain::radio_message(sound_source *Message, int const Channel) { auto const soundrange{Message->range()}; if ((soundrange > 0) && (glm::length2(Message->location() - glm::dvec3{DynamicObject->GetPosition()}) > sq(soundrange))) { // skip message playback if the receiver is outside of the emitter's range return; } // NOTE: we initiate playback of all sounds in range, in case the user switches on the radio or tunes to the right channel mid-play m_radiomessages.emplace_back(Channel, std::make_shared(m_radiosound)); // assign sound to the template and play it auto &message = *(m_radiomessages.back().second.get()); auto const radioenabled{(true == mvOccupied->Radio) && (mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable)}; auto const volume{(true == radioenabled) && (Dynamic()->Mechanik != nullptr) && (Channel == RadioChannel()) ? 1.0 : 0.0}; message.copy_sounds(*Message).gain(volume).play(); } // 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(); 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-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; } uint16_t TTrain::id() { if (vid == 0) { vid = ++simulation::prev_train_id; WriteLog("net: assigning id " + std::to_string(vid) + " to vehicle " + Dynamic()->name(), logtype::net); } return vid; } #include #include void train_table::updateAsync(double dt) { const int threads = std::max(1, Global.trainThreads); const size_t total = m_items.size(); const size_t chunkSize = (total + threads - 1) / threads; std::vector> futures; futures.reserve(threads); for (int i = 0; i < threads; ++i) { const size_t start = i * chunkSize; const size_t end = std::min(start + chunkSize, total); if (start >= end) break; // brak więcej danych futures.emplace_back(std::async(std::launch::async, [this, start, end, dt]() { for (size_t j = start; j < end; ++j) { TTrain *train = m_items[j]; if (train) train->Update(dt); } })); } // Poczekaj aż wszystkie wątki skończą for (auto &f : futures) f.get(); // Teraz kasowanie (tylko w głównym wątku) for (TTrain *train : m_items) { if (!train) continue; if (train->pending_delete) { purge(train->Dynamic()->name()); if (simulation::Train == train) simulation::Train = nullptr; } else if (simulation::Train != train && Global.network_servers.empty() && !Global.network_client) { purge(train->Dynamic()->name()); } } } void train_table::update(double dt) { for (TTrain *train : m_items) { if (!train) continue; train->Update(dt); if (train->pending_delete) { purge(train->Dynamic()->name()); if (simulation::Train == train) simulation::Train = nullptr; } // for single-player destroy non-player trains else if (simulation::Train != train && Global.network_servers.empty() && !Global.network_client) { purge(train->Dynamic()->name()); } } } TTrain *train_table::find_id(std::uint16_t const Id) const { if (Id == 0) { return nullptr; } for (TTrain *train : m_items) { if (!train) { continue; } if (train->id() == Id) { return train; } } return nullptr; }