mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 21:29:19 +02:00
fix(cab): the line contactors lamp goes out with the pressure switch (eu07/maszyna#470) The lamp of the open line contactors, which tells the start can be made, went out at 1 bar in the brake cylinders, while the pressure switch cuts the start off only at 2 bar (or below 3.6 bar in the main pipe), so with the brakes partly applied the start could be made with the lamp out. With a pressure switch fitted the lamp now follows it; without one it goes out at 1 bar as before. The same holds for the lamp of the second unit, which used to keep its last state between 1 bar and the cut-off. The condition is TMoverParameters::LineContactorsLampLit(). Tests: tests/unit/test_line_contactors.cpp. refactor: Sonar findings of PR 217 - horn_supply::pneumatic shadowed the pneumatic enumerator of the sound flags (cpp:S1117), it's horn_supply::air; the supplies read with using enum (S6177), - CouplerForce(): the automatic coupling refused or caught without another level of nested ifs (S134), the same outcome: too fast, the coupling is locked; caught, coupled and locked; otherwise left to try again, - CompressorCheck(): the power the compressor doesn't draw yet noted without a TODO (S1135), - tests: the previous crash damage setting in an in-class initializer (S3230), the loops which stop early with break rather than in their conditions (S886), the stand-ins of test::pair in a std::vector rather than an array (S5945), units in the comments of the example without /* */ (S1103). test: the cases the changes of the branch left without a test - Cntrl.: BDelay1 to BDelay4 reach the brake delays (now through std::format), the default start types of the ground and converter overload relays of a multiple unit and of other vehicles (out of the nested conditionals), - the compressor: fed from the low voltage it waits from the low voltage coming on; the pressure switch stops it above the cut-out pressure, without one the excess air is vented (the vented fraction rewritten), - the horns: a vehicle without any air sounds as before, the threshold pressure, an electric horn on 110 V, - the positions of a cab control below zero, and a control without position sounds. refactor(cab): the kind of the train heating switch as a flag Read once with the vehicle definition, rather than compared as text in each of the switch commands. feat(cab): train heating switch springing back (MaSzyna-EU07/maszyna#85) TrainHeating=[toggle]|impulse in the Switches: section of the vehicle definition makes the train heating switch an unstable one: it goes up to switch the heating on, down to switch it off, and back to the middle once let go, instead of staying where it was put. The position of such a switch isn't set from the heating state when the cab is entered. Tests: tests/unit/test_switches.cpp. feat(sound): mirror sounds (MaSzyna-EU07/maszyna#84, eu07/maszyna#322) The sound definitions of a vehicle (.mmd) take mirroropen: and mirrorclose:, played as a mirror sets off unfolding or folding, and the locations section mirrors: <offset>... end, the offsets along the vehicle of the mirror pairs, as for the doors (negative ahead of the centre), each pair at the sides of the vehicle. As the animation, only the mirrors at the occupied cab sound. The start of a move is mirror_moves::motion in vehicle/mirrors.h. Tests: tests/unit/test_mirrors.cpp. feat(cab): joint oil and fuel pump switch (MaSzyna-EU07/maszyna#81) A cab control oilfuelpump_sw: switches both the oil and the fuel pump at once, with the new command oilfuelpumptoggle (no key by default). As a two-state switch it turns both pumps off when both are on and both on otherwise; as an impulse switch it's held to start them, like the separate pump switches. Its position follows the pumps when the cab is entered. refactor(editor): text fields filled without strncpy (MaSzyna-EU07/maszyna#112) The last three strncpy calls Sonar flags (cpp:S5816), in the road and track editors, copy the text into the ImGui field buffers with std::string::copy and terminate it right after; the text gets cut to the buffer as before. fix: automatic coupler crashes, horns, compressor start delay, radio and cab lever sounds - automatic couplers don't catch above the safe collision speed (#86): with crash damage on they couple at a closing speed of 15 km/h at most, a faster hit locks the coupling until the vehicles part and the collision runs its course. On the way: CollisionVelocity() read a head-on 10 + 10 km/h as 14.1 km/h (quaternions turn by half the angle), it's now taken from the touching ends; CollisionDetect() took the other vehicle's speed the wrong way round for vehicles facing each other. Head-on hits now derail sooner, at the intended limit, - a single horn lever can't sound both tones at once (eu07/maszyna#392), - a skid ends once the vehicle stops on locked wheels (eu07/maszyna#479), - electronic destination signs are blanked at the end of the timetable (eu07/maszyna#446), - horns need the air or the voltage to sound (eu07/maszyna#290, #76): HornType=[Pneumatic]|Electric and Horn3Type in Cntrl.; a pneumatic horn needs 0.2 MPa in the main reservoir (or the main pipe without one), an electric one the low voltage; the lever stays where it was put, - compressor start delay (#67): CompressorStartDelay [s] in Brake., counted from the compressor getting its power, after the engine starts say; none by default, - the channel and volume buttons of a radio without power do nothing (eu07/maszyna#429); the power check of the radio is TMoverParameters::RadioPowered(), - a lever moved smoothly sounds the positions it passes (#83). Sonar findings in the functions changed: nesting, nested conditionals, commented-out code, std::format, references to const. Unit tests: test::pair for two vehicles running into each other, test_couplers, test_horn, test_wheelslip, test_compressor, test_radio, test_gauge_positions.
12429 lines
406 KiB
C++
12429 lines
406 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
/*
|
|
MaSzyna EU07 locomotive simulator
|
|
Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
|
|
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "vehicle/Train.h"
|
|
#include "vehicle/horn.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 <future>
|
|
#include <thread>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
/*
|
|
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.try_emplace(Gauge.SubModel, Label);
|
|
}
|
|
if (Gauge.SubModelOn != nullptr)
|
|
{
|
|
m_controlnames.try_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.contains(Control);
|
|
}
|
|
|
|
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<std::string>(true, "\n\r\t ,;[]")};
|
|
|
|
if (true == key.empty() || key == "}")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (key == "{")
|
|
{
|
|
script = Input.getToken<std::string>();
|
|
}
|
|
else if (key == "target:")
|
|
{
|
|
target = Input.getToken<std::string>();
|
|
}
|
|
else if (key == "updatetime:")
|
|
{
|
|
updatetime = Input.getToken<int>();
|
|
}
|
|
else if (key == "parameters:")
|
|
{
|
|
parameters = dictionary_source(Input.getToken<std::string>());
|
|
}
|
|
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<std::string>();
|
|
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 = static_cast<float>(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::oilfuelpumptoggle, &TTrain::OnCommand_oilfuelpumptoggle},
|
|
{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<std::string> const TTrain::fPress_labels = {
|
|
|
|
"ch1: ", "ch2: ", "ch3: ", "ch4: ", "ch5: ", "ch6: ", "ch7: ", "ch8: ", "ch9: ", "ch0: "};
|
|
|
|
TTrain::TTrain()
|
|
{
|
|
|
|
// McZapkie-240302 - przyda sie do tachometru
|
|
|
|
// asMessage="";
|
|
DynamicSet(nullptr); // ustawia wszystkie mv*
|
|
//-----
|
|
|
|
//
|
|
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 (auto &press : fPress[i])
|
|
press = 0.0;
|
|
bBrakes[i][0] = false;
|
|
bBrakes[i][1] = false;
|
|
}
|
|
}
|
|
|
|
TTrain::~TTrain() = default;
|
|
|
|
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 && 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<dictionary_source> TTrain::GetTrainState(dictionary_source const &Extraparameters)
|
|
{
|
|
|
|
if (mvOccupied == nullptr || mvControlled == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
auto dict = std::make_shared<dictionary_source>(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("dynamicbrake_pos", mvControlled->DynamicBrakeCtrlPos);
|
|
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};
|
|
auto const &brakevalve{*mvOccupied->Hamulec};
|
|
if (auto const &brakevalvetype{typeid(brakevalve)}; brakevalvetype == typeid(TLSt) || brakevalvetype == typeid(TEStED))
|
|
{
|
|
|
|
TBrake *temp_ham = mvOccupied->Hamulec.get();
|
|
bPN = static_cast<TLSt *>(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
|
|
std::array<char const *, 10> const TXTT{"fd", "fdt", "fdb", "pd", "pdt", "pdb", "itothv", "1", "2", "3"};
|
|
std::array<char const *, 10> const TXTC{"fr", "frt", "frb", "pr", "prt", "prb", "im", "vm", "ihv", "uhv"};
|
|
std::array<char const *, 10> const TXTD{"enrot", "nrot", "fill_des", "fill_real", "clutch_des", "clutch_real", "water_temp", "oil_press", "engine_temp", "retarder_fill"};
|
|
std::array<char const *, 7> const TXTP{"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 *vehicledriver{(DynamicObject->ctOwner != nullptr ? DynamicObject->ctOwner : DynamicObject->Mechanik)};
|
|
|
|
dict->insert("velocity_desired", vehicledriver->VelDesired);
|
|
dict->insert("velroad", vehicledriver->VelRoad);
|
|
dict->insert("vellimitlast", vehicledriver->VelLimitLast);
|
|
dict->insert("velsignallast", vehicledriver->VelSignalLast);
|
|
dict->insert("velsignalnext", vehicledriver->VelSignalNext);
|
|
dict->insert("velnext", vehicledriver->VelNext);
|
|
dict->insert("actualproximitydist", vehicledriver->ActualProximityDist);
|
|
// train data
|
|
vehicledriver->TrainTimetable().serialize(dict.get());
|
|
dict->insert("train_atpassengerstop", vehicledriver->IsAtPassengerStop);
|
|
dict->insert("train_length", vehicledriver->fLength);
|
|
// world state data
|
|
dict->insert("scenario", Global.SceneryFile);
|
|
dict->insert("hours", static_cast<int>(simulation::Time.data().wHour));
|
|
dict->insert("minutes", static_cast<int>(simulation::Time.data().wMinute));
|
|
dict->insert("seconds", static_cast<int>(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<std::uint8_t>(iCabn),
|
|
btHaslerBrakes.GetValue(),
|
|
btHaslerCurrent.GetValue(),
|
|
mvOccupied->SecuritySystem.is_beeping(),
|
|
btLampkaHVoltageB.GetValue(),
|
|
fTachoVelocity,
|
|
static_cast<float>(mvOccupied->Compressor),
|
|
static_cast<float>(mvOccupied->PipePress),
|
|
static_cast<float>(mvOccupied->BrakePress),
|
|
static_cast<float>(mvPantographUnit->PantPress),
|
|
fHVoltage,
|
|
{fHCurrent[mvControlled->TrainType & dt_EZT ? 0 : 1], fHCurrent[2], fHCurrent[3]},
|
|
ggLVoltage.GetValue(),
|
|
mvOccupied->DistCounter,
|
|
static_cast<std::uint8_t>(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<int>(static_cast<int>(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<int>(100.0 * mvOccupied->fBrakeCtrlPos)};
|
|
mvOccupied->BrakeLevelSet(Position);
|
|
|
|
if (static_cast<int>(100.0 * mvOccupied->fBrakeCtrlPos) == originalbrakeposition)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// sound feedback if the lever movement activates one of the switches
|
|
if (true == is_eztoer() && false == ((originalbrakeposition / 100 == 0 || originalbrakeposition / 100 >= 5) && (mvOccupied->BrakeCtrlPos == 0 || mvOccupied->BrakeCtrlPos >= 5)) && dsbPneumaticSwitch)
|
|
{
|
|
dsbPneumaticSwitch->play();
|
|
}
|
|
}
|
|
|
|
void TTrain::zero_charging_train_brake()
|
|
{
|
|
using enum TBrakeHandle;
|
|
|
|
if (mvOccupied->BrakeCtrlPos == -1 && DynamicObject->Controller != AIdriver && Global.iFeedbackMode < 3 &&
|
|
(mvOccupied->BrakeHandle == FVel6 || mvOccupied->BrakeHandle == MHZ_EN57 || mvOccupied->BrakeHandle == MHZ_K8P))
|
|
{
|
|
// Odskakiwanie hamulce EP
|
|
set_train_brake(0);
|
|
}
|
|
}
|
|
|
|
void TTrain::set_train_brake_speed(TDynamicObject *Vehicle, int const Speed)
|
|
{
|
|
|
|
// visual feedback
|
|
// TODO: add setting indicator to vehicle class, for external lever/indicator
|
|
if (true == Vehicle->MoverParameters->BrakeDelaySwitch(Speed) && Vehicle == DynamicObject)
|
|
{
|
|
if (ggBrakeProfileCtrl.SubModel != nullptr)
|
|
{
|
|
ggBrakeProfileCtrl.UpdateValue((mvOccupied->BrakeDelayFlag & bdelay_R) != 0 ? 2.0 : static_cast<float>(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 couplerindex{-2}; // scan for vehicle, not any specific coupler
|
|
auto *vehicle{DynamicObject->ABuScanNearestObject(pos, DynamicObject->GetTrack(), 1, 1500, couplerindex)};
|
|
if (vehicle == nullptr)
|
|
vehicle = DynamicObject->ABuScanNearestObject(pos, DynamicObject->GetTrack(), -1, 1500, couplerindex);
|
|
// 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)
|
|
{
|
|
|
|
// on press
|
|
if (Command.action == GLFW_PRESS && 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<float>(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<float>(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{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() && 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<int>(static_cast<int>(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 : static_cast<float>(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 : static_cast<float>(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<double>(
|
|
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)
|
|
{
|
|
|
|
if (Train->mvOccupied->BrakeHandle == TBrakeHandle::FV4a)
|
|
{
|
|
Train->mvOccupied->BrakeCtrlPos2 = std::clamp(Train->mvOccupied->BrakeCtrlPos2 - 0.01, -1.5, 2.0);
|
|
}
|
|
else
|
|
{
|
|
Train->mvOccupied->BrakeLevelAdd(0.01);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TTrain::OnCommand_trainbrakebasepressuredecrease(TTrain *Train, command_data const &Command)
|
|
{
|
|
|
|
if (Command.action != GLFW_RELEASE)
|
|
{
|
|
|
|
if (Train->mvOccupied->BrakeHandle == TBrakeHandle::FV4a)
|
|
{
|
|
Train->mvOccupied->BrakeCtrlPos2 = std::clamp(Train->mvOccupied->BrakeCtrlPos2 + 0.01, -1.5, 2.0);
|
|
}
|
|
else
|
|
{
|
|
Train->mvOccupied->BrakeLevelAdd(-0.01);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 const *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<int>(Train->ggEPFuseButton.type()) & static_cast<int>(TGaugeType::toggle)) != 0};
|
|
// command only works for bistable switch
|
|
if (Command.action == GLFW_PRESS && 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<int>(Train->ggEPFuseButton.type()) & static_cast<int>(TGaugeType::toggle)) != 0};
|
|
// command only works for bistable switch
|
|
if (Command.action == GLFW_PRESS && 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<int>(Train->ggEPFuseButton.type()) & static_cast<int>(TGaugeType::push)) != 0};
|
|
auto const istoggle{(static_cast<int>(Train->ggEPFuseButton.type()) & static_cast<int>(TGaugeType::toggle)) != 0};
|
|
|
|
if (Command.action == GLFW_PRESS)
|
|
{
|
|
if (istoggle)
|
|
{
|
|
// switch state
|
|
if (false == Train->mvOccupied->EpFuse)
|
|
{
|
|
// turn on
|
|
// audio feedback
|
|
if (Train->mvOccupied->EpFuseSwitch(true) && Train->dsbPneumaticSwitch)
|
|
{
|
|
Train->dsbPneumaticSwitch->play();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// turn off
|
|
Train->mvOccupied->EpFuseSwitch(false);
|
|
}
|
|
}
|
|
else if (ispush)
|
|
{
|
|
// potentially turn on
|
|
// audio feedback
|
|
if (Train->mvOccupied->EpFuseSwitch(true) && 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 && ispush)
|
|
{
|
|
// return the switch to neutral position
|
|
Train->ggEPFuseButton.UpdateValue(0.0f, Train->dsbSwitch);
|
|
}
|
|
}
|
|
|
|
void TTrain::OnCommand_trainbrakeoperationmodeincrease(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 && (Train->mvOccupied->BrakeOpModeFlag << 1 & Train->mvOccupied->BrakeOpModes) != 0)
|
|
{
|
|
// next mode
|
|
Train->mvOccupied->BrakeOpModeFlag <<= 1;
|
|
// visual feedback
|
|
Train->ggBrakeOperationModeCtrl.UpdateValue(Train->mvOccupied->BrakeOpModeFlag > 0 ? static_cast<float>(std::log2(Train->mvOccupied->BrakeOpModeFlag)) : 0); // audio fallback
|
|
}
|
|
}
|
|
|
|
void TTrain::OnCommand_trainbrakeoperationmodedecrease(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 && (Train->mvOccupied->BrakeOpModeFlag >> 1 & Train->mvOccupied->BrakeOpModes) != 0)
|
|
{
|
|
// previous mode
|
|
Train->mvOccupied->BrakeOpModeFlag >>= 1;
|
|
// visual feedback
|
|
Train->ggBrakeOperationModeCtrl.UpdateValue(Train->mvOccupied->BrakeOpModeFlag > 0 ? static_cast<float>(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(static_cast<float>(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(static_cast<float>(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;
|
|
}
|
|
|
|
// aktualizacja skrajnych pojazdów w składzie
|
|
if (Train->mvOccupied->DirectionForward() && 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;
|
|
}
|
|
|
|
// aktualizacja skrajnych pojazdów w składzie
|
|
if (Train->mvOccupied->DirectionBackward() && 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 && 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 && 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()
|
|
{
|
|
using enum operation_t;
|
|
|
|
auto const &presets{mvOccupied->PantsPreset.first};
|
|
auto const &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 ? enable : disable);
|
|
mvOccupied->OperatePantographValve(end::rear, rearstate ? enable : 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<int>(static_cast<int>(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)
|
|
{
|
|
|
|
|
|
if (auto const valveispresent{Train->ggPantCompressorValve.SubModel != nullptr || (Train->mvOccupied == Train->mvPantographUnit && Train->iCabn == 0)}; 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)
|
|
{
|
|
|
|
|
|
if (auto const valveispresent{Train->ggPantCompressorValve.SubModel != nullptr || (Train->mvOccupied == Train->mvPantographUnit && Train->iCabn == 0)}; 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
|
|
if (auto const switchispresent{Train->m_controlmapper.contains("pantcompressor_sw:") || (Train->mvOccupied == Train->mvPantographUnit && Train->iCabn == 0)}; 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 && Train->ggMainButton.type() != TGaugeType::toggle)
|
|
{
|
|
Train->ggMainButton.UpdateValue(0.5, Train->dsbSwitch);
|
|
}
|
|
|
|
if (Train->m_linebreakerstate == 1)
|
|
{
|
|
return;
|
|
} // already in the desired state
|
|
|
|
// 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->m_linebreakerstate == 2 && 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_oilfuelpumptoggle(TTrain *Train, command_data const &Command)
|
|
{
|
|
if (Command.action == GLFW_REPEAT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto *vehicle{Train->mvControlled};
|
|
if (Train->ggOilFuelPumpButton.type() == TGaugeType::push)
|
|
{
|
|
// impulse switch, as the separate ones: held to start both pumps
|
|
auto const pressed{Command.action == GLFW_PRESS};
|
|
Train->ggOilFuelPumpButton.UpdateValue(pressed ? 1.0 : 0.0, Train->dsbSwitch);
|
|
vehicle->FuelPumpSwitch(pressed);
|
|
vehicle->OilPumpSwitch(pressed);
|
|
return;
|
|
}
|
|
// two-state switch, only cares about press events
|
|
if (Command.action != GLFW_PRESS)
|
|
{
|
|
return;
|
|
}
|
|
// both pumps go off if both were on, otherwise both go on
|
|
auto const enable{false == (vehicle->FuelPump.is_enabled && vehicle->OilPump.is_enabled)};
|
|
Train->ggOilFuelPumpButton.UpdateValue(enable ? 1.0 : 0.0, Train->dsbSwitch);
|
|
vehicle->FuelPumpSwitch(enable);
|
|
vehicle->FuelPumpSwitchOff(false == enable);
|
|
vehicle->OilPumpSwitch(enable);
|
|
vehicle->OilPumpSwitchOff(false == enable);
|
|
}
|
|
|
|
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(static_cast<float>(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(static_cast<float>(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(static_cast<float>(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(static_cast<float>(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<int>(Command.command) - static_cast<int>(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(static_cast<float>(Train->mvOccupied->LightsPos - 1), Train->dsbSwitch);
|
|
}
|
|
else
|
|
{
|
|
Train->ggLightsButton.PutValue(static_cast<float>(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(static_cast<float>(Train->mvOccupied->LightsPos - 1), Train->dsbSwitch);
|
|
}
|
|
else
|
|
{
|
|
Train->ggLightsButton.PutValue(static_cast<float>(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] = static_cast<int>(Command.param1);
|
|
Train->mvOccupied->Lights[end::rear][17] = static_cast<int>(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;
|
|
if (auto dimCount = Train->mvOccupied->dimPositions.size(); 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(static_cast<float>(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<int>(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(static_cast<float>(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<TDynamicObject *>(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<TDynamicObject *>(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<int>(Train->ggCompartmentLightsButton.type()) & static_cast<int>(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:") && 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<int>(Train->ggCompartmentLightsButton.type()) & static_cast<int>(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:") && 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 && (Train->ggTrainHeatingButton.type() == TGaugeType::push || Train->mvOccupied->HeatingSwitchImpulse))
|
|
{
|
|
// 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);
|
|
}
|
|
else if (Command.action == GLFW_RELEASE && Train->mvOccupied->HeatingSwitchImpulse)
|
|
{
|
|
// the switch springs back to the middle
|
|
Train->ggTrainHeatingButton.UpdateValue(0.0, Train->dsbSwitch);
|
|
}
|
|
}
|
|
|
|
void TTrain::OnCommand_heatingdisable(TTrain *Train, command_data const &Command)
|
|
{
|
|
|
|
if (Command.action == GLFW_PRESS)
|
|
{
|
|
|
|
Train->mvOccupied->HeatingSwitch(false);
|
|
// visual feedback: an impulse switch is pushed the other way, down
|
|
auto position{0.0};
|
|
if (Train->mvOccupied->HeatingSwitchImpulse)
|
|
{
|
|
position = -1.0;
|
|
}
|
|
else if (Train->ggTrainHeatingButton.type() == TGaugeType::push)
|
|
{
|
|
position = 1.0;
|
|
}
|
|
Train->ggTrainHeatingButton.UpdateValue(position, Train->dsbSwitch);
|
|
}
|
|
else if (Command.action == GLFW_RELEASE && Train->mvOccupied->HeatingSwitchImpulse)
|
|
{
|
|
Train->ggTrainHeatingButton.UpdateValue(0.0, Train->dsbSwitch);
|
|
}
|
|
}
|
|
|
|
void TTrain::OnCommand_generictoggle(TTrain *Train, command_data const &Command)
|
|
{
|
|
|
|
auto const itemindex = static_cast<int>(Command.command) - static_cast<int>(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 && 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
|
|
|
|
if (auto const *vehicle{Train->find_nearest_consist_vehicle(Command.freefly, Command.location)}; 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<int>(Command.command) - static_cast<int>(user_command::speedcontrolbutton0);
|
|
|
|
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<int>(Command.command) - static_cast<int>(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<int>(Command.command) - static_cast<int>(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<int>(Command.command) - static_cast<int>(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 && 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 && 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(static_cast<float>(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(static_cast<float>(Train->mvOccupied->Doors.permit_preset), Train->dsbSwitch);
|
|
}
|
|
}
|
|
|
|
void TTrain::OnCommand_dooropenleft(TTrain *Train, command_data const &Command)
|
|
{
|
|
|
|
|
|
if (auto const remoteopencontrol{Train->mvOccupied->Doors.open_control == control_t::driver || Train->mvOccupied->Doors.open_control == control_t::mixed}; 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)
|
|
{
|
|
|
|
|
|
if (auto const remoteclosecontrol{Train->mvOccupied->Doors.close_control == control_t::driver || Train->mvOccupied->Doors.close_control == control_t::mixed}; 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)
|
|
{
|
|
|
|
|
|
if (auto const remoteopencontrol{Train->mvOccupied->Doors.open_control == control_t::driver || Train->mvOccupied->Doors.open_control == control_t::mixed}; 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)
|
|
{
|
|
|
|
|
|
if (auto const remoteclosecontrol{Train->mvOccupied->Doors.close_control == control_t::driver || Train->mvOccupied->Doors.close_control == control_t::mixed}; 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)
|
|
{
|
|
|
|
|
|
if (auto const remoteopencontrol{Train->mvOccupied->Doors.open_control == control_t::driver || Train->mvOccupied->Doors.open_control == control_t::mixed}; 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)
|
|
{
|
|
|
|
|
|
if (auto const remoteclosecontrol{Train->mvOccupied->Doors.close_control == control_t::driver || Train->mvOccupied->Doors.close_control == control_t::mixed}; 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 couplerindex{-1};
|
|
auto *vehicle{Train->DynamicObject->ABuScanNearestObject(Command.location, Train->DynamicObject->GetTrack(), 1, 1500, couplerindex)};
|
|
if (vehicle == nullptr)
|
|
vehicle = Train->DynamicObject->ABuScanNearestObject(Command.location, Train->DynamicObject->GetTrack(), -1, 1500, couplerindex);
|
|
|
|
if (couplerindex != -1 && vehicle != nullptr)
|
|
{
|
|
|
|
vehicle->couple(couplerindex);
|
|
}
|
|
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 couplerindex{-1};
|
|
auto *vehicle{Train->DynamicObject->ABuScanNearestObject(Command.location, Train->DynamicObject->GetTrack(), 1, 1500, couplerindex)};
|
|
if (vehicle == nullptr)
|
|
vehicle = Train->DynamicObject->ABuScanNearestObject(Command.location, Train->DynamicObject->GetTrack(), -1, 1500, couplerindex);
|
|
|
|
if (couplerindex != -1 && vehicle != nullptr)
|
|
{
|
|
|
|
vehicle->uncouple(couplerindex);
|
|
}
|
|
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<TDynamicObject *>(simulation::Region->find_vehicle(Command.location, 50, false, true))};
|
|
if (vehicle == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto const couplerindex =
|
|
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(couplerindex);
|
|
}
|
|
}
|
|
|
|
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<TDynamicObject *>(simulation::Region->find_vehicle(Command.location, 50, false, true))};
|
|
if (vehicle == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto const couplerindex =
|
|
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(couplerindex);
|
|
}
|
|
}
|
|
|
|
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, horn::low))
|
|
{
|
|
// turn on
|
|
Train->mvOccupied->WarningSignal = horn::pressed(Train->mvOccupied->WarningSignal, horn::low, Train->has_single_horn_lever());
|
|
// visual feedback
|
|
Train->ggHornButton.UpdateValue(-1.0);
|
|
Train->ggHornLowButton.UpdateValue(1.0);
|
|
}
|
|
}
|
|
else if (Command.action == GLFW_RELEASE)
|
|
{
|
|
// turn off
|
|
Train->mvOccupied->WarningSignal = horn::released(Train->mvOccupied->WarningSignal, horn::low);
|
|
// visual feedback
|
|
if (false == (Train->has_single_horn_lever() && TestFlag(Train->mvOccupied->WarningSignal, horn::high)))
|
|
{
|
|
// leave the single lever alone if it was moved to the other tone in the meantime
|
|
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, horn::high))
|
|
{
|
|
// turn on
|
|
Train->mvOccupied->WarningSignal = horn::pressed(Train->mvOccupied->WarningSignal, horn::high, Train->has_single_horn_lever());
|
|
// visual feedback
|
|
Train->ggHornButton.UpdateValue(1.0);
|
|
Train->ggHornHighButton.UpdateValue(1.0);
|
|
}
|
|
}
|
|
else if (Command.action == GLFW_RELEASE)
|
|
{
|
|
// turn off
|
|
Train->mvOccupied->WarningSignal = horn::released(Train->mvOccupied->WarningSignal, horn::high);
|
|
// visual feedback
|
|
if (false == (Train->has_single_horn_lever() && TestFlag(Train->mvOccupied->WarningSignal, horn::low)))
|
|
{
|
|
// leave the single lever alone if it was moved to the other tone in the meantime
|
|
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: works without the switch in the 3d model too, to allow system reset after receiving radio-stop signal
|
|
// 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)
|
|
{
|
|
// the channel buttons of a radio switched off or without power do nothing
|
|
if (Train->mvOccupied->RadioPowered())
|
|
{
|
|
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)
|
|
{
|
|
if (Train->mvOccupied->RadioPowered())
|
|
{
|
|
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(static_cast<float>(Train->RadioChannel() - 1));
|
|
}
|
|
}
|
|
|
|
void TTrain::OnCommand_radiostopsend(TTrain *Train, command_data const &Command)
|
|
{
|
|
|
|
if (Command.action == GLFW_PRESS)
|
|
{
|
|
if (Train->mvOccupied->RadioPowered())
|
|
{
|
|
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 (Train->mvOccupied->RadioPowered())
|
|
{
|
|
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 && Train->mvOccupied->RadioPowered())
|
|
{
|
|
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 && Train->mvOccupied->RadioPowered())
|
|
{
|
|
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 && Train->mvOccupied->RadioPowered())
|
|
{
|
|
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)
|
|
{
|
|
// so do the volume buttons
|
|
if (Train->mvOccupied->RadioPowered())
|
|
{
|
|
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)
|
|
{
|
|
if (Train->mvOccupied->RadioPowered())
|
|
{
|
|
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 = static_cast<float>(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)
|
|
{
|
|
if (auto const movedirection{1}; 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)
|
|
{
|
|
if (auto const movedirection{-1}; 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 && 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 && true == mvControlled->Mains)
|
|
{
|
|
// crude way to sync state of the linebreaker with ai-issued commands
|
|
m_linebreakerstate = 1;
|
|
}
|
|
if (m_linebreakerstate == 1 && 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 += static_cast<float>(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 && 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;
|
|
}
|
|
// 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 (m_linebreakerstate == 2 && (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 -= static_cast<float>(Deltatime);
|
|
if (doorpermittimer < 0.f)
|
|
{
|
|
mvOccupied->OperateDoors(static_cast<side>(idx), true);
|
|
}
|
|
}
|
|
|
|
// train measurement timer
|
|
if (trainLenghtMeasureTimer >= 0.f)
|
|
{
|
|
trainLenghtMeasureTimer -= static_cast<float>(Deltatime);
|
|
if (trainLenghtMeasureTimer < 0.f)
|
|
trainLenghtMeasureTimer = -1.f;
|
|
}
|
|
|
|
// battery timer
|
|
if (fBatteryTimer >= 0.f)
|
|
{
|
|
fBatteryTimer -= static_cast<float>(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<std::size_t>(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<std::size_t>(commanddata.command)].name + "]");
|
|
}
|
|
// pass the command to the assigned handler
|
|
lookup->second(this, commanddata);
|
|
}
|
|
}
|
|
|
|
UpdateCab();
|
|
|
|
// nie blokujemy AI
|
|
if (DynamicObject->Mechanik != nullptr && false == DynamicObject->Mechanik->AIControllFlag && (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 -= static_cast<float>(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<float>(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 = static_cast<float>(fTachoVelocity + (2.0 - LocalRandom(3) + LocalRandom(3)) * 0.5);
|
|
else if (fTachoVelocity < 5 && fTachoVelocity > 1)
|
|
fTachoVelocityJump = static_cast<float>(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 = static_cast<float>(std::min(maxtacho, fTachoCount + Deltatime * 3));
|
|
}
|
|
else if (fTachoCount > 0)
|
|
{
|
|
// schodz powoli - niektore haslery to ze 4 sekundy potrafia stukac
|
|
fTachoCount = static_cast<float>(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 = static_cast<float>(std::max(mvControlled->PantographVoltage,
|
|
mvControlled->GetTrainsetHighVoltage())); // Winger czy to nie jest zle?
|
|
}
|
|
// *mvControlled->Mains);
|
|
else
|
|
{
|
|
fHVoltage = static_cast<float>(mvControlled->EngineVoltage);
|
|
}
|
|
if (ShowNextCurrent)
|
|
{ // jeśli pokazywać drugi człon
|
|
if (mvSecond)
|
|
{ // o ile jest ten drugi
|
|
fHCurrent[0] = static_cast<float>(mvSecond->ShowCurrent(0) * 1.05);
|
|
fHCurrent[1] = static_cast<float>(mvSecond->ShowCurrent(1) * 1.05);
|
|
fHCurrent[2] = static_cast<float>(mvSecond->ShowCurrent(2) * 1.05);
|
|
fHCurrent[3] = static_cast<float>(mvSecond->ShowCurrent(3) * 1.05);
|
|
}
|
|
else
|
|
{
|
|
fHCurrent[0] = 0.0; // gdy nie ma człona
|
|
fHCurrent[1] = 0.0;
|
|
fHCurrent[2] = 0.0;
|
|
fHCurrent[3] = 0.0;
|
|
}
|
|
}
|
|
else
|
|
{ // normalne pokazywanie
|
|
fHCurrent[0] = static_cast<float>(mvControlled->ShowCurrent(0));
|
|
fHCurrent[1] = static_cast<float>(mvControlled->ShowCurrent(1));
|
|
fHCurrent[2] = static_cast<float>(mvControlled->ShowCurrent(2));
|
|
fHCurrent[3] = static_cast<float>(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] = static_cast<float>(p->MoverParameters->BrakePress);
|
|
fPress[i][1] = static_cast<float>(p->MoverParameters->PipePress);
|
|
fPress[i][2] = static_cast<float>(p->MoverParameters->ScndPipePress);
|
|
fPress[i][3] = static_cast<float>(p->MoverParameters->CntrlPipePress);
|
|
fPress[i][4] = static_cast<float>(p->MoverParameters->Hamulec->GetBRP());
|
|
fPress[i][5] = static_cast<float>((p->MoverParameters->TotalMass - p->MoverParameters->Mred) * 0.001);
|
|
fPress[i][6] = static_cast<float>(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] = static_cast<float>(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] = static_cast<float>(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] = static_cast<float>(p->MoverParameters->eimv[eimv_If]);
|
|
fEIMParams[1 + in][7] = static_cast<float>(p->MoverParameters->eimv[eimv_U]);
|
|
fEIMParams[1 + in][8] = static_cast<float>(p->MoverParameters->Itot); // p->MoverParameters->eimv[eimv_Ipoj];
|
|
fEIMParams[1 + in][9] = static_cast<float>(p->MoverParameters->EngineVoltage);
|
|
fEIMParams[0][6] += fEIMParams[1 + in][8];
|
|
bMains[in] = p->MoverParameters->Mains;
|
|
fCntVol[in] = static_cast<float>(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] = static_cast<float>(p->MoverParameters->enrot * 60);
|
|
fDieselParams[1 + in][1] = static_cast<float>(p->MoverParameters->nrot);
|
|
fDieselParams[1 + in][2] = static_cast<float>(p->MoverParameters->RList[p->MoverParameters->MainCtrlPos].R);
|
|
fDieselParams[1 + in][3] = static_cast<float>(p->MoverParameters->dizel_fill);
|
|
fDieselParams[1 + in][4] = static_cast<float>(p->MoverParameters->RList[p->MoverParameters->MainCtrlPos].Mn);
|
|
fDieselParams[1 + in][5] = static_cast<float>(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] = static_cast<float>(p->MoverParameters->hydro_R_Fill);
|
|
bMains[in] = p->MoverParameters->Mains;
|
|
fCntVol[in] = static_cast<float>(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] = 0;
|
|
fPress[i][1] = 0;
|
|
fPress[i][2] = 0;
|
|
fPress[i][3] = 0;
|
|
fPress[i][4] = 0;
|
|
fPress[i][5] = 0;
|
|
bDoors[i][0] = false;
|
|
bDoors[i][1] = false;
|
|
bDoors[i][2] = false;
|
|
bDoors[i][3] = false;
|
|
bDoors[i][4] = false;
|
|
bBrakes[i][0] = false;
|
|
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] = static_cast<float>(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] = static_cast<float>(fEIMParams[0][4] * mvControlled->eimv[eimv_Fful]);
|
|
fEIMParams[0][2] = static_cast<float>(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 += static_cast<float>(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 const *tmp{nullptr};
|
|
if (DynamicObject->NextConnected() && TestFlag(mvControlled->Couplers[end::rear].CouplingFlag, coupling::control) && mvOccupied->CabOccupied == 1)
|
|
tmp = DynamicObject->NextConnected();
|
|
if (DynamicObject->PrevConnected() && TestFlag(mvControlled->Couplers[end::front].CouplingFlag, coupling::control) && mvOccupied->CabOccupied == -1)
|
|
tmp = DynamicObject->PrevConnected();
|
|
if (tmp && tmp->MoverParameters->Power > 0)
|
|
{
|
|
if (ggI1B.SubModel)
|
|
{
|
|
ggI1B.UpdateValue(static_cast<float>(tmp->MoverParameters->ShowCurrent(1)));
|
|
ggI1B.Update();
|
|
}
|
|
if (ggI2B.SubModel)
|
|
{
|
|
ggI2B.UpdateValue(static_cast<float>(tmp->MoverParameters->ShowCurrent(2)));
|
|
ggI2B.Update();
|
|
}
|
|
if (ggI3B.SubModel)
|
|
{
|
|
ggI3B.UpdateValue(static_cast<float>(tmp->MoverParameters->ShowCurrent(3)));
|
|
ggI3B.Update();
|
|
}
|
|
if (ggItotalB.SubModel)
|
|
{
|
|
ggItotalB.UpdateValue(static_cast<float>(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(static_cast<float>(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(static_cast<float>(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(static_cast<float>(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(static_cast<float>(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] = static_cast<float>(mvControlled->ShowEngineRotation(1));
|
|
fEngine[2] = static_cast<float>(mvControlled->ShowEngineRotation(2));
|
|
}
|
|
|
|
else if (mvControlled->EngineType == TEngineType::DieselEngine)
|
|
{ // albo dla innego spalinowego
|
|
fEngine[1] = static_cast<float>(mvControlled->ShowEngineRotation(1));
|
|
fEngine[2] = static_cast<float>(mvControlled->ShowEngineRotation(2));
|
|
fEngine[3] = static_cast<float>(mvControlled->ShowEngineRotation(3));
|
|
if (ggMainGearStatus.SubModel)
|
|
{
|
|
if (mvControlled->Mains)
|
|
ggMainGearStatus.UpdateValue(static_cast<float>(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;
|
|
// 1% Vmax rezerwy, żeby 181/182 nie buczały po zahamowaniu, ale to proteza
|
|
if (veldiff < -0.01 && 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 || mvControlled->Mains);
|
|
btLampkaWylSzybkiOff.Turn(m_linebreakerstate != 2 && !mvControlled->Mains);
|
|
btLampkaMainBreakerReady.Turn(mvControlled->MainsInitTimeCountdown <= 0.0 && m_linebreakerstate != 2 && !mvControlled->Mains);
|
|
btLampkaMainBreakerBlinkingIfReady.Turn(m_linebreakerstate == 2 || mvControlled->Mains || mvControlled->MainsInitTimeCountdown < 0.0 && simulation::Time.data().wMilliseconds > 500);
|
|
|
|
btLampkaPrzetw.Turn(mvOccupied->Power110vIsAvailable);
|
|
btLampkaPrzetwOff.Turn(!mvOccupied->Power110vIsAvailable);
|
|
btLampkaNadmPrzetw.Turn(Dynamic()->Mechanik ? Dynamic()->Mechanik->IsAnyConverterOverloadRelayOpen : mvControlled->ConvOvldFlag);
|
|
|
|
btLampkaOpory.Turn(mvControlled->StLinFlag && mvControlled->ResistorsFlagCheck());
|
|
btLampkaBezoporowa.Turn(mvControlled->ResistorsFlagCheck() || mvControlled->MainCtrlActualPos == 0); // do EU04
|
|
btLampkaStyczn.Turn(mvControlled->LineContactorsLampLit()); // mozna prowadzic rozruch
|
|
btLampkaPrzekRozn.Turn(!mvControlled->GroundRelay && !mvControlled->ControlPressureSwitch && mvControlled->BrakePress < 1.0); // relay is off and needs a reset
|
|
btLampkaNadmSil.Turn(mvControlled->FuseFlagCheck() && !mvControlled->ControlPressureSwitch && mvControlled->BrakePress < 1.0); // relay is off and needs a reset
|
|
btLampkaUkrotnienie.Turn(TestFlag(mvControlled->Couplers[mvControlled->CabOccupied == 1 ? rear : front].CouplingFlag, control));
|
|
btLampkaHamPosp.Turn(TestFlag(mvOccupied->Hamulec->GetBrakeStatus(), 1)); // lampka drugiego stopnia hamowania
|
|
// TODO: youBy wyciągnąć flagę wysokiego stopnia
|
|
btLampkaNadmWent.Turn(mvControlled->RventRot < 5.0 && mvControlled->ResistorsFlagCheck()); // hunter-121211: lampka zanikowo-pradowego wentylatorow
|
|
btLampkaWysRozr.Turn(mvControlled->Imax >= mvControlled->ImaxHi);
|
|
|
|
btLampkaNapNastHam.Turn(mvControlled->DirActive != 0); // napiecie na nastawniku hamulcowym
|
|
btLampkaSprezarka.Turn(mvControlled->CompressorFlag); // mutopsitka dziala
|
|
btLampkaSprezarkaOff.Turn(!mvControlled->CompressorFlag);
|
|
btLampkaFuelPumpOff.Turn(!mvControlled->FuelPump.is_active);
|
|
|
|
// boczniki
|
|
// Ra: w SU45 boczniki wchodzą na ScndCtrlPos, a nie na ScndCtrlActualPos
|
|
// - pokićkał ktoś?
|
|
int scp = mvControlled->RList[mvControlled->MainCtrlPos].ScndAct;
|
|
scp = scp == 255 ? 0 : scp; // Ra: whatta hella is this?
|
|
btLampkaBoczniki.Turn(!mvControlled->DelayCtrlFlag && mvControlled->ScndCtrlActualPos > 0 || scp > 0);
|
|
btLampkaBocznik1.Turn(!mvControlled->DelayCtrlFlag && mvControlled->ScndCtrlPos > 0 || mvControlled->ScndCtrlActualPos > 0 || scp > 0);
|
|
btLampkaBocznik2.Turn(!mvControlled->DelayCtrlFlag && mvControlled->ScndCtrlPos > 1 || mvControlled->ScndCtrlActualPos > 1 || scp > 1);
|
|
btLampkaBocznik3.Turn(!mvControlled->DelayCtrlFlag && mvControlled->ScndCtrlPos > 2 || mvControlled->ScndCtrlActualPos > 2 || scp > 2);
|
|
btLampkaBocznik4.Turn(!mvControlled->DelayCtrlFlag && mvControlled->ScndCtrlPos > 3 || mvControlled->ScndCtrlActualPos > 3 || scp > 3);
|
|
|
|
// biegi dla motoraka
|
|
btLampkaGear1.Turn(mvControlled->ScndCtrlActualPos == 1 && mvControlled->dizel_engagestate > 0); // bieg 1
|
|
btLampkaGear2.Turn(mvControlled->ScndCtrlActualPos == 2 && mvControlled->dizel_engagestate > 0); // bieg 2
|
|
btLampkaHydroLockup.Turn(mvControlled->hydro_TC_Lockup); // zblokowanie skrzyni z silnikiem
|
|
|
|
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 && !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);
|
|
btLampkaPrzekRozn.Turn(false);
|
|
btLampkaNadmSil.Turn(false);
|
|
btLampkaUkrotnienie.Turn(false);
|
|
btLampkaHamPosp.Turn(false);
|
|
btLampkaBoczniki.Turn(false);
|
|
btLampkaBocznik1.Turn(false);
|
|
btLampkaBocznik2.Turn(false);
|
|
btLampkaBocznik3.Turn(false);
|
|
btLampkaBocznik4.Turn(false);
|
|
btLampkaGear1.Turn(false);
|
|
btLampkaGear2.Turn(false);
|
|
btLampkaHydroLockup.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 const *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
|
|
|
|
btLampkaStycznB.Turn(mover->LineContactorsLampLit()); // 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<double>(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 ? static_cast<float>(negativePart) :
|
|
mvControlled->CoupledCtrl ? static_cast<float>(double(mvControlled->MainCtrlPos + mvControlled->ScndCtrlPos)) :
|
|
static_cast<float>(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(static_cast<float>(double(mvControlled->MainCtrlPos + mvControlled->ScndCtrlPos)), dsbNastawnikJazdy);
|
|
}
|
|
else
|
|
{
|
|
ggMainCtrl.UpdateValue(static_cast<float>(double(mvControlled->MainCtrlPos)), dsbNastawnikJazdy);
|
|
}
|
|
ggMainCtrl.Update();
|
|
}
|
|
if (ggMainCtrlAct.SubModel != nullptr)
|
|
{
|
|
if (mvControlled->CoupledCtrl)
|
|
ggMainCtrlAct.UpdateValue(static_cast<float>(double(mvControlled->MainCtrlActualPos + mvControlled->ScndCtrlActualPos)));
|
|
else
|
|
ggMainCtrlAct.UpdateValue(static_cast<float>(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(static_cast<float>(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(static_cast<float>(double(mvControlled->DirActive)), dsbReverserKey);
|
|
}
|
|
else
|
|
{
|
|
ggDirKey.UpdateValue(static_cast<float>(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(static_cast<float>(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)
|
|
{
|
|
// 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(static_cast<float>(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(static_cast<float>(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();
|
|
ggOilFuelPumpButton.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 && 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 = static_cast<float>(std::lerp(m_localbrakepressurechange, 10 * (brakepressuredifference / Deltatime), 0.1f));
|
|
}
|
|
m_lastlocalbrakepressure = static_cast<float>(mvOccupied->LocBrakePress);
|
|
// local brake, release
|
|
if (rsSBHiss)
|
|
{
|
|
if (m_localbrakepressurechange < -0.05f && mvOccupied->LocBrakePress > mvOccupied->BrakePress - 0.05)
|
|
{
|
|
rsSBHiss->gain(static_cast<float>(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(static_cast<float>(volume));
|
|
if (volume < 0.05)
|
|
{
|
|
rsSBHiss->stop();
|
|
}
|
|
}
|
|
}
|
|
// local brake, engage
|
|
if (rsSBHissU)
|
|
{
|
|
if (m_localbrakepressurechange > 0.05f)
|
|
{
|
|
rsSBHissU->gain(static_cast<float>(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(static_cast<float>(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<float>(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(static_cast<float>(volume * brakevolumescale));
|
|
rsHiss->play(sound_flags::exclusive | sound_flags::looping);
|
|
}
|
|
else
|
|
{
|
|
rsHiss->stop();
|
|
}
|
|
}
|
|
// napelnianie PG
|
|
if (rsHissU)
|
|
{
|
|
fNPress = std::lerp(fNPress, static_cast<float>(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(static_cast<float>(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(static_cast<float>(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(static_cast<float>(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(static_cast<float>(volume * brakevolumescale));
|
|
rsHissT->play(sound_flags::exclusive | sound_flags::looping);
|
|
}
|
|
else
|
|
{
|
|
rsHissT->stop();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// jesli nie FV4a
|
|
// upuszczanie z PG
|
|
if (rsHiss)
|
|
{
|
|
fPPress = static_cast<float>((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(static_cast<float>(volume));
|
|
rsHiss->play(sound_flags::exclusive | sound_flags::looping);
|
|
}
|
|
else
|
|
{
|
|
rsHiss->stop();
|
|
}
|
|
}
|
|
// napelnianie PG
|
|
if (rsHissU)
|
|
{
|
|
fNPress = static_cast<float>((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(static_cast<float>(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(static_cast<float>(rsBrake->m_frequencyoffset + mvOccupied->Vel * rsBrake->m_frequencyfactor));
|
|
rsBrake->gain(static_cast<float>(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(static_cast<float>(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(static_cast<float>(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 soundfrequency{(true == dsbHasler->is_combined() ? fTachoVelocity * 0.01 : dsbHasler->m_frequencyoffset + dsbHasler->m_frequencyfactor)};
|
|
dsbHasler->pitch(static_cast<float>(soundfrequency));
|
|
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 && 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) const
|
|
{
|
|
// frequency calculation
|
|
auto const normalizer{mvOccupied->Vmax * 0.01f};
|
|
auto const soundfrequency{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(static_cast<float>(soundfrequency)).gain(static_cast<float>(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 soundfrequency{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(static_cast<float>(soundfrequency)).gain(static_cast<float>(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
|
|
std::erase_if(m_radiomessages, [](auto const &source) { return false == source.second->is_playing(); });
|
|
}
|
|
// adjust audibility of remaining messages based on current radio conditions
|
|
auto const radioenabled{mvOccupied->RadioPowered()};
|
|
for (auto const &[messagechannel, messagesound] : m_radiomessages)
|
|
{
|
|
auto const volume{true == radioenabled && Dynamic()->Mechanik != nullptr && messagechannel == RadioChannel() ? m_radiovolume : 0.0};
|
|
messagesound->gain(static_cast<float>(volume));
|
|
radio_message_played |= true == radioenabled && Dynamic()->Mechanik != nullptr && messagechannel == 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 += static_cast<float>(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) && 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)
|
|
{
|
|
using enum sound_placement;
|
|
using enum sound_type;
|
|
// initialize sounds so potential entries from previous vehicle don't stick around
|
|
std::unordered_map<std::string, std::tuple<std::optional<sound_source> &, sound_placement, float, sound_type, int, double>> internalsounds = {
|
|
{"ctrl:", {dsbNastawnikJazdy, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"ctrlscnd:", {dsbNastawnikBocz, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"reverserkey:", {dsbReverserKey, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"buzzer:", {dsbBuzzer, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"buzzershp:", {dsbBuzzerShp, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"radiostop:", {m_radiostop, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"slipalarm:", {dsbSlipAlarm, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"distancecounter:", {m_distancecounterclear, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"tachoclock:", {dsbHasler, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"switch:", {dsbSwitch, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"pneumaticswitch:", {dsbPneumaticSwitch, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"airsound:", {rsHiss, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, sound_parameters::amplitude, 100.0}},
|
|
{"airsound2:", {rsHissU, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, sound_parameters::amplitude, 100.0}},
|
|
{"airsound3:", {rsHissE, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, sound_parameters::amplitude, 100.0}},
|
|
{"airsound4:", {rsHissX, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, sound_parameters::amplitude, 100.0}},
|
|
{"airsound5:", {rsHissT, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, sound_parameters::amplitude, 100.0}},
|
|
{"localbrakesound:", {rsSBHiss, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, sound_parameters::amplitude, 100.0}},
|
|
{"localbrakesound2:", {rsSBHissU, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, sound_parameters::amplitude, 100.0}},
|
|
{"brakesound:", {rsBrake, internal, -1, single, sound_parameters::amplitude | sound_parameters::frequency, 100.0}},
|
|
{"fadesound:", {rsFadeSound, internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE, single, 0, 100.0}},
|
|
{"runningnoise:", {rsRunningNoise, internal, EU07_SOUND_GLOBALRANGE, single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax}},
|
|
{"resonancenoise:", {rsResonanceNoise, internal, EU07_SOUND_GLOBALRANGE, single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax}},
|
|
{"windsound:", {rsWindSound, internal, EU07_SOUND_GLOBALRANGE, single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax}},
|
|
{"huntingnoise:", {rsHuntingNoise, internal, EU07_SOUND_GLOBALRANGE, single, sound_parameters::amplitude | sound_parameters::frequency, mvOccupied->Vmax}},
|
|
{"rainsound:", {m_rainsound, internal, -1, single, 0, 100.0}},
|
|
};
|
|
for (auto &[soundname, soundsource] : internalsounds)
|
|
{
|
|
std::get<std::optional<sound_source> &>(soundsource).reset();
|
|
}
|
|
// NOTE: since radiosound is an incomplete template not using std::optional it gets a special treatment
|
|
m_radiosound.owner(DynamicObject);
|
|
CabSoundLocations.clear();
|
|
|
|
cParser parser(asFileName, cParser::buffer_FILE, DynamicObject->asBaseDir, true, std::vector<std::string>(), true);
|
|
// NOTE: yaml-style comments are disabled until conflict in use of # is resolved
|
|
// parser.addCommentStyle( "#", "\n" );
|
|
std::string token;
|
|
do
|
|
{
|
|
token = "";
|
|
parser.getTokens();
|
|
parser >> token;
|
|
} while (token != "" && token != "internaldata:");
|
|
|
|
if (token == "internaldata:")
|
|
{
|
|
|
|
do
|
|
{
|
|
token = "";
|
|
parser.getTokens();
|
|
parser >> token;
|
|
|
|
auto lookup{internalsounds.find(token)};
|
|
if (lookup == internalsounds.end())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
auto const soundconfig{lookup->second};
|
|
sound_source sound{std::get<sound_placement>(soundconfig), std::get<float>(soundconfig)};
|
|
sound.deserialize(parser, std::get<sound_type>(soundconfig), std::get<int>(soundconfig), static_cast<int>(std::get<double>(soundconfig)));
|
|
sound.owner(DynamicObject);
|
|
std::get<std::optional<sound_source> &>(soundconfig) = sound;
|
|
} while (token != "");
|
|
|
|
// assign default samples to sound emitters which weren't included in the config file
|
|
if (!m_rainsound)
|
|
{
|
|
sound_source rainsound;
|
|
rainsound.deserialize("rainsound_default", 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 /= static_cast<float>(1 + mvOccupied->Vmax);
|
|
}
|
|
if (rsResonanceNoise)
|
|
{
|
|
rsResonanceNoise->m_frequencyfactor /= static_cast<float>(1 + mvOccupied->Vmax);
|
|
}
|
|
if (rsWindSound)
|
|
{
|
|
rsWindSound->m_frequencyfactor /= static_cast<float>(1 + mvOccupied->Vmax);
|
|
}
|
|
if (rsRunningNoise)
|
|
{
|
|
rsRunningNoise->m_frequencyfactor /= static_cast<float>(1 + mvOccupied->Vmax);
|
|
}
|
|
if (rsHuntingNoise)
|
|
{
|
|
rsHuntingNoise->m_frequencyfactor /= static_cast<float>(1 + mvOccupied->Vmax);
|
|
}
|
|
}
|
|
std::vector<std::reference_wrapper<std::optional<sound_source>>> sounds = {
|
|
dsbReverserKey, dsbNastawnikJazdy, dsbNastawnikBocz, dsbSwitch, dsbPneumaticSwitch, rsHiss, rsHissU, rsHissE, rsHissX, rsHissT, rsSBHiss,
|
|
rsSBHissU, rsFadeSound, rsRunningNoise, rsResonanceNoise, rsWindSound, rsHuntingNoise, dsbHasler, dsbBuzzer, dsbBuzzerShp, dsbSlipAlarm, m_distancecounterclear,
|
|
m_rainsound, m_radiostop};
|
|
for (auto &sound : sounds)
|
|
{
|
|
if (sound.get())
|
|
{
|
|
CabSoundLocations.emplace_back(sound, sound.get()->offset());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
|
|
{
|
|
m_controlmapper.clear();
|
|
// clear python screens
|
|
m_screens.clear();
|
|
// reset sound positions
|
|
auto const nullvector{glm::vec3()};
|
|
std::vector<std::reference_wrapper<std::optional<sound_source>>> sounds = {
|
|
dsbReverserKey, dsbNastawnikJazdy, dsbNastawnikBocz, dsbSwitch, dsbPneumaticSwitch, rsHiss, rsHissU, rsHissE, rsHissX, rsHissT, rsSBHiss,
|
|
rsSBHissU, rsFadeSound, rsRunningNoise, rsResonanceNoise, rsWindSound, rsHuntingNoise, dsbHasler, dsbBuzzer, dsbBuzzerShp, dsbSlipAlarm, m_distancecounterclear,
|
|
m_rainsound, m_radiostop};
|
|
for (auto const &sound : sounds)
|
|
{
|
|
if (sound.get())
|
|
{
|
|
sound.get()->offset(nullvector);
|
|
}
|
|
}
|
|
m_radiosound.offset(nullvector);
|
|
for (auto const &[soundsource, soundoffset] : CabSoundLocations)
|
|
{
|
|
if (soundsource.get() && soundsource.get()->offset() == nullvector)
|
|
{
|
|
soundsource.get()->offset(soundoffset);
|
|
}
|
|
}
|
|
// 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;
|
|
default:
|
|
break;
|
|
}
|
|
iCabn = cabindex;
|
|
|
|
std::string cabstr("cab" + std::to_string(cabindex) + "definition:");
|
|
|
|
cParser parser(asFileName, cParser::buffer_FILE, DynamicObject->asBaseDir, true, std::vector<std::string>(), true);
|
|
// NOTE: yaml-style comments are disabled until conflict in use of # is resolved
|
|
// parser.addCommentStyle( "#", "\n" );
|
|
std::string token;
|
|
do
|
|
{
|
|
// szukanie kabiny
|
|
token = "";
|
|
parser.getTokens();
|
|
parser >> token;
|
|
} while (token != "" && token != cabstr);
|
|
|
|
if (token == cabstr)
|
|
{
|
|
// jeśli znaleziony wpis kabiny
|
|
Cabine[cabindex].Load(parser);
|
|
// NOTE: the position and angle definitions depend on strict entry order
|
|
// TODO: refactor into more flexible arrangement
|
|
parser.getTokens();
|
|
parser >> token;
|
|
if (token == std::string("driver" + std::to_string(cabindex) + "angle:"))
|
|
{
|
|
// camera view angle
|
|
parser.getTokens(2, false);
|
|
// angle is specified in degrees but internally stored in radians
|
|
glm::vec2 viewangle;
|
|
parser >> viewangle.y // yaw first, then pitch
|
|
>> viewangle.x;
|
|
pMechViewAngle = glm::radians(viewangle);
|
|
|
|
Global.pCamera.Angle.x = pMechViewAngle.x;
|
|
Global.pCamera.Angle.y = pMechViewAngle.y;
|
|
|
|
parser.getTokens();
|
|
parser >> token;
|
|
}
|
|
if (token == std::string("driver" + std::to_string(cabindex) + "pos:"))
|
|
{
|
|
// pozycja poczatkowa maszynisty
|
|
parser.getTokens(3, false);
|
|
parser >> pMechOffset.x >> pMechOffset.y >> pMechOffset.z;
|
|
pMechSittingPosition = pMechOffset;
|
|
|
|
parser.getTokens();
|
|
parser >> token;
|
|
}
|
|
// ABu: pozycja siedzaca mechanika
|
|
if (token == std::string("driver" + std::to_string(cabindex) + "sitpos:"))
|
|
{
|
|
// ABu 180404 pozycja siedzaca maszynisty
|
|
parser.getTokens(3, false);
|
|
parser >> pMechSittingPosition.x >> pMechSittingPosition.y >> pMechSittingPosition.z;
|
|
|
|
parser.getTokens();
|
|
parser >> token;
|
|
}
|
|
// else parse=false;
|
|
do
|
|
{
|
|
if (parse == true)
|
|
{
|
|
token = "";
|
|
parser.getTokens();
|
|
parser >> token;
|
|
}
|
|
else
|
|
{
|
|
parse = true;
|
|
}
|
|
// inicjacja kabiny
|
|
// Ra 2014-08: zmieniamy zasady - zamiast przypisywać submodel do
|
|
// istniejących obiektów animujących
|
|
// będziemy teraz uaktywniać obiekty animujące z tablicy i podawać im
|
|
// submodel oraz wskaźnik na parametr
|
|
if (token == std::string("cab" + std::to_string(cabindex) + "model:"))
|
|
{
|
|
// model kabiny
|
|
parser.getTokens();
|
|
parser >> token;
|
|
std::ranges::replace(token, '\\', '/');
|
|
if (token != "none")
|
|
{
|
|
// bieżąca sciezka do tekstur to dynamic/...
|
|
Global.asCurrentTexturePath = DynamicObject->asBaseDir;
|
|
// szukaj kabinę jako oddzielny model
|
|
// name can contain leading slash, erase it to avoid creation of double slashes when the name is combined with current directory
|
|
replace_slashes(token);
|
|
erase_leading_slashes(token);
|
|
if (token[0] == '/')
|
|
{
|
|
token.erase(0, 1);
|
|
}
|
|
TModel3d *kabina = TModelsManager::GetModel(DynamicObject->asBaseDir + token, true, true, Global.network_servers.empty() && !Global.network_client ? 0 : id());
|
|
// z powrotem defaultowa sciezka do tekstur
|
|
Global.asCurrentTexturePath = paths::textures;
|
|
// if (DynamicObject->mdKabina!=k)
|
|
if (kabina != nullptr)
|
|
{
|
|
DynamicObject->mdKabina = kabina; // nowa kabina
|
|
}
|
|
//(mdKabina) może zostać to samo po przejściu do innego członu bez
|
|
// zmiany kabiny, przy powrocie musi być wiązanie ponowne
|
|
// else
|
|
// break; //wyjście z pętli, bo model zostaje bez zmian
|
|
}
|
|
else if (cabindex == 1)
|
|
{
|
|
// model tylko, gdy nie ma kabiny 1
|
|
// McZapkie-170103: szukaj elementy kabiny w glownym modelu
|
|
DynamicObject->mdKabina = DynamicObject->mdModel;
|
|
}
|
|
clear_cab_controls();
|
|
}
|
|
/*
|
|
if (nullptr == DynamicObject->mdKabina)
|
|
{
|
|
// don't bother with other parts until the cab is initialised
|
|
continue;
|
|
}
|
|
*/
|
|
else if (true == initialize_gauge(parser, token, cabindex))
|
|
{
|
|
// matched the token, grab the next one
|
|
continue;
|
|
}
|
|
else if (true == initialize_button(parser, token, cabindex))
|
|
{
|
|
// matched the token, grab the next one
|
|
continue;
|
|
}
|
|
// TODO: add "pydestination:"
|
|
else if (token == "pyscreen:")
|
|
{
|
|
screen_entry screen;
|
|
screen.deserialize(parser);
|
|
if (false == screen.script.empty() && substr_path(screen.script).empty())
|
|
{
|
|
screen.script = DynamicObject->asBaseDir + screen.script;
|
|
}
|
|
|
|
ITexture *tex = nullptr;
|
|
TSubModel *submodel = nullptr;
|
|
if (screen.target != "none")
|
|
{
|
|
submodel = DynamicObject->mdKabina ? DynamicObject->mdKabina->GetFromName(screen.target) :
|
|
DynamicObject->mdLowPolyInt ? DynamicObject->mdLowPolyInt->GetFromName(screen.target) :
|
|
nullptr;
|
|
if (submodel == nullptr)
|
|
{
|
|
WriteLog("Python Screen: submodel " + screen.target + " not found - Ignoring screen");
|
|
continue;
|
|
}
|
|
auto const material{submodel->GetMaterial()};
|
|
if (material <= 0)
|
|
{
|
|
// sub model nie posiada tekstury lub tekstura wymienna - nie obslugiwana
|
|
WriteLog("Python Screen: invalid texture id " + std::to_string(material) + " - Ignoring screen");
|
|
continue;
|
|
}
|
|
|
|
tex = &GfxRenderer->Texture(GfxRenderer->Material(material)->GetTexture(0));
|
|
}
|
|
else
|
|
{
|
|
// TODO: fix leak
|
|
tex = ITexture::null_texture();
|
|
tex->make_stub();
|
|
}
|
|
|
|
tex->create(true); // make the surface static so it doesn't get destroyed by garbage
|
|
// collector if the user spends long time outside cab
|
|
// TBD, TODO: keep texture handles around, so we can undo the static switch when the
|
|
// user changes cabs?
|
|
auto rt = std::make_shared<python_rt>();
|
|
rt->shared_tex = tex;
|
|
|
|
// record renderer and material binding for future update requests
|
|
m_screens.emplace_back(screen);
|
|
m_screens.back().rt = rt;
|
|
|
|
m_screens.back().touch_list = std::make_shared<std::vector<glm::vec2>>();
|
|
if (submodel)
|
|
submodel->screen_touch_list = m_screens.back().touch_list;
|
|
|
|
if (Global.python_displaywindows)
|
|
m_screens.back().viewer = std::make_unique<python_screen_viewer>(rt, m_screens.back().touch_list, m_screens.back().script);
|
|
}
|
|
else if (token == "pyscreenupdatetime:")
|
|
{
|
|
parser.getTokens();
|
|
parser >> ScreenUpdateRate;
|
|
}
|
|
// btLampkaUnknown`"unknown",mdKabina,false);
|
|
} while (token != ""
|
|
// TODO: enable full per-cab deserialization when/if .mmd files get proper per-cab switch configuration
|
|
// && ( token != "cab1definition:" )
|
|
// && ( token != "cab2definition:" )
|
|
&& token != "cab0definition:");
|
|
for (auto &screen : m_screens)
|
|
{
|
|
if (screen.updatetime > 0)
|
|
{
|
|
screen.updatetime = std::max((int)screen.updatetime, Global.PythonScreenUpdateRate) * 0.001;
|
|
}
|
|
if (screen.updatetime == 0)
|
|
{
|
|
screen.updatetime = std::max(Global.PythonScreenUpdateRate, ScreenUpdateRate) * 0.001;
|
|
}
|
|
if (screen.updatetime < -1)
|
|
{
|
|
screen.updatetime = -screen.updatetime * 0.001;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
/*
|
|
if (DynamicObject->mdKabina)
|
|
{
|
|
*/
|
|
// configure placement of sound emitters which aren't bound with any device model, and weren't placed manually
|
|
auto const caboffset{glm::dvec3{(Cabine[cabindex].CabPos1 + Cabine[cabindex].CabPos2) * 0.5f} + glm::dvec3{0, 1, 0}};
|
|
// NOTE: since radiosound is an incomplete template not using std::optional it gets a special treatment
|
|
if (m_radiosound.offset() == nullvector)
|
|
{
|
|
m_radiosound.offset(btLampkaRadio.model_offset());
|
|
}
|
|
if (m_radiosound.offset() == nullvector)
|
|
{
|
|
m_radiosound.offset(caboffset);
|
|
}
|
|
std::vector<std::pair<std::reference_wrapper<std::optional<sound_source>>, glm::vec3>> soundlocations = {
|
|
{dsbReverserKey, ggDirKey.model_offset()},
|
|
{dsbNastawnikJazdy, ggJointCtrl.model_offset()},
|
|
{dsbNastawnikJazdy, ggMainCtrl.model_offset()}, // NOTE: fallback for vehicles without universal controller
|
|
{dsbNastawnikBocz, ggScndCtrl.model_offset()},
|
|
{dsbSwitch, caboffset},
|
|
{dsbPneumaticSwitch, caboffset},
|
|
{rsHiss, ggBrakeCtrl.model_offset()},
|
|
{rsHissU, ggBrakeCtrl.model_offset()},
|
|
{rsHissE, ggBrakeCtrl.model_offset()},
|
|
{rsHissX, ggBrakeCtrl.model_offset()},
|
|
{rsHissT, ggBrakeCtrl.model_offset()},
|
|
{rsSBHiss, ggLocalBrake.model_offset()},
|
|
{rsSBHiss, ggBrakeCtrl.model_offset()}, // NOTE: fallback if the local brake model can't be located
|
|
{rsSBHissU, ggLocalBrake.model_offset()},
|
|
{rsSBHissU, ggBrakeCtrl.model_offset()}, // NOTE: fallback if the local brake model can't be located
|
|
{rsFadeSound, caboffset},
|
|
{rsRunningNoise, caboffset},
|
|
{rsResonanceNoise, caboffset},
|
|
{rsWindSound, caboffset},
|
|
{rsHuntingNoise, caboffset},
|
|
{dsbHasler, caboffset},
|
|
{dsbBuzzer, btLampkaCzuwaka.model_offset()},
|
|
{dsbBuzzerShp, btLampkaCzuwaka.model_offset()},
|
|
{dsbSlipAlarm, caboffset},
|
|
{m_distancecounterclear, btLampkaCzuwaka.model_offset()},
|
|
{m_rainsound, caboffset},
|
|
{m_radiostop, m_radiosound.offset()},
|
|
};
|
|
for (auto const &[soundsource, soundoffset] : soundlocations)
|
|
{
|
|
if (soundsource.get() && soundsource.get()->offset() == nullvector)
|
|
{
|
|
soundsource.get()->offset(soundoffset);
|
|
}
|
|
}
|
|
// second pass, in case some items received no positioning due to missing submodels etc
|
|
for (auto const &[soundsource, soundoffset] : soundlocations)
|
|
{
|
|
if (soundsource.get() && soundsource.get()->offset() == nullvector)
|
|
{
|
|
soundsource.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
|
|
mvControlled = d ? DynamicObject->MoverParameters : nullptr; // albo silnikowy w EZT
|
|
mvOccupied = mvControlled;
|
|
|
|
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 = 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 = 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)
|
|
{
|
|
|
|
if (auto const soundrange{Message->range()}; 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<sound_source>(m_radiosound));
|
|
// assign sound to the template and play it
|
|
auto &message = *m_radiomessages.back().second.get();
|
|
auto const radioenabled{mvOccupied->RadioPowered()};
|
|
auto const volume{true == radioenabled && Dynamic()->Mechanik != nullptr && Channel == RadioChannel() ? 1.0 : 0.0};
|
|
message.copy_sounds(*Message).gain(static_cast<float>(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();
|
|
ggOilFuelPumpButton.Clear();
|
|
ggMotorBlowersFrontButton.Clear();
|
|
ggMotorBlowersRearButton.Clear();
|
|
ggMotorBlowersAllOffButton.Clear();
|
|
|
|
btLampkaPrzetw.Clear();
|
|
btLampkaPrzetwB.Clear();
|
|
btLampkaPrzetwBOff.Clear();
|
|
btLampkaPrzekRozn.Clear();
|
|
btLampkaPrzekRoznPom.Clear();
|
|
btLampkaUkrotnienie.Clear();
|
|
btLampkaHamPosp.Clear();
|
|
btLampkaWylSzybkiOff.Clear();
|
|
btLampkaWylSzybkiB.Clear();
|
|
btLampkaWylSzybkiBOff.Clear();
|
|
btLampkaMainBreakerReady.Clear();
|
|
btLampkaMainBreakerBlinkingIfReady.Clear();
|
|
btLampkaBezoporowa.Clear();
|
|
btLampkaBezoporowaB.Clear();
|
|
btLampkaMaxSila.Clear();
|
|
btLampkaPrzekrMaxSila.Clear();
|
|
btLampkaRadio.Clear();
|
|
btLampkaRadioMessage.Clear();
|
|
btLampkaRadioStop.Clear();
|
|
btLampkaHamulecReczny.Clear();
|
|
btLampkaBlokadaDrzwi.Clear();
|
|
btLampkaDoorLockOff.Clear();
|
|
for (auto &universal : btUniversals)
|
|
{
|
|
universal.Clear();
|
|
}
|
|
btInstrumentLight.Clear();
|
|
btDashboardLight.Clear();
|
|
btTimetableLight.Clear();
|
|
btLampkaWentZaluzje.Clear();
|
|
btLampkaDoorLeft.Clear();
|
|
btLampkaDoorRight.Clear();
|
|
btLampkaDepartureSignal.Clear();
|
|
btLampkaRezerwa.Clear();
|
|
btLampkaBoczniki.Clear();
|
|
btLampkaBocznik1.Clear();
|
|
btLampkaBocznik2.Clear();
|
|
btLampkaBocznik3.Clear();
|
|
btLampkaBocznik4.Clear();
|
|
btLampkaGear1.Clear();
|
|
btLampkaGear2.Clear();
|
|
btLampkaHydroLockup.Clear();
|
|
btLampkaRadiotelefon.Clear();
|
|
btLampkaHamienie.Clear();
|
|
btLampkaBrakingOff.Clear();
|
|
btLampkaED.Clear();
|
|
btLampkaBrakeProfileG.Clear();
|
|
btLampkaBrakeProfileP.Clear();
|
|
btLampkaBrakeProfileR.Clear();
|
|
btLampkaSpringBrakeActive.Clear();
|
|
btLampkaSpringBrakeInactive.Clear();
|
|
btLampkaSprezarka.Clear();
|
|
btLampkaSprezarkaB.Clear();
|
|
btLampkaSprezarkaOff.Clear();
|
|
btLampkaSprezarkaBOff.Clear();
|
|
btLampkaFuelPumpOff.Clear();
|
|
btLampkaNapNastHam.Clear();
|
|
btLampkaOporyB.Clear();
|
|
btLampkaStycznB.Clear();
|
|
btLampkaHamowanie1zes.Clear();
|
|
btLampkaHamowanie2zes.Clear();
|
|
btLampkaNadmPrzetwB.Clear();
|
|
btLampkaHVoltageB.Clear();
|
|
btLampkaForward.Clear();
|
|
btLampkaBackward.Clear();
|
|
btLampkaNeutral.Clear();
|
|
// light indicators
|
|
btLampkaUpperLight.Clear();
|
|
btLampkaLeftLight.Clear();
|
|
btLampkaRightLight.Clear();
|
|
btLampkaLeftEndLight.Clear();
|
|
btLampkaRightEndLight.Clear();
|
|
btLampkaRearUpperLight.Clear();
|
|
btLampkaRearLeftLight.Clear();
|
|
btLampkaRearRightLight.Clear();
|
|
btLampkaRearLeftEndLight.Clear();
|
|
btLampkaRearRightEndLight.Clear();
|
|
// others
|
|
btLampkaMalfunction.Clear();
|
|
btLampkaMalfunctionB.Clear();
|
|
btLampkaMotorBlowers.Clear();
|
|
btLampkaCoolingFans.Clear();
|
|
btLampkaTempomat.Clear();
|
|
btLampkaDistanceCounter.Clear();
|
|
|
|
ggLeftLightButton.Clear();
|
|
ggRightLightButton.Clear();
|
|
ggUpperLightButton.Clear();
|
|
ggDimHeadlightsButton.Clear();
|
|
ggModernLightDimSw.Clear();
|
|
ggLeftEndLightButton.Clear();
|
|
ggRightEndLightButton.Clear();
|
|
ggLightsButton.Clear();
|
|
// hunter-230112
|
|
ggRearLeftLightButton.Clear();
|
|
ggRearRightLightButton.Clear();
|
|
ggRearUpperLightButton.Clear();
|
|
ggRearLeftEndLightButton.Clear();
|
|
ggRearRightEndLightButton.Clear();
|
|
}
|
|
|
|
// NOTE: we can get rid of this function once we have per-cab persistent state
|
|
void TTrain::set_cab_controls(int const Cab)
|
|
{
|
|
// switches
|
|
// battery
|
|
ggBatteryButton.PutValue(ggBatteryButton.type() == TGaugeType::push ? 0.5f : mvOccupied->Power24vIsAvailable ? 1.f : 0.f);
|
|
// activation
|
|
ggCabActivationButton.PutValue(ggCabActivationButton.type() == TGaugeType::push ? 0.5f : mvOccupied->IsCabMaster() ? 1.f : 0.f);
|
|
// line breaker
|
|
if (ggMainButton.SubModel != nullptr)
|
|
{ // instead of single main button there can be on/off pair
|
|
ggMainButton.PutValue(ggMainButton.type() == TGaugeType::push ? 0.5f : m_linebreakerstate > 0 ? 1.f : 0.f);
|
|
}
|
|
|
|
if (ggModernLightDimSw.SubModel != nullptr)
|
|
{
|
|
mvOccupied->modernDimmerPosition = mvOccupied->modernDimmerDefaultPosition;
|
|
ggModernLightDimSw.PutValue(static_cast<float>(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(static_cast<float>((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(static_cast<float>(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(static_cast<float>(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(static_cast<float>(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(static_cast<float>(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() && false == mvControlled->HeatingSwitchImpulse)
|
|
{
|
|
ggTrainHeatingButton.PutValue(mvControlled->Heating ? 1.f : 0.f);
|
|
}
|
|
// brake acting time
|
|
if (ggBrakeProfileCtrl.SubModel != nullptr)
|
|
{
|
|
ggBrakeProfileCtrl.PutValue((mvOccupied->BrakeDelayFlag & bdelay_R) != 0 ? 2.f : static_cast<float>(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(static_cast<float>(mvOccupied->wiperSwitchPos));
|
|
}
|
|
|
|
if (ggBrakeOperationModeCtrl.SubModel != nullptr)
|
|
{
|
|
ggBrakeOperationModeCtrl.PutValue(mvOccupied->BrakeOpModeFlag > 0 ? static_cast<float>(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);
|
|
}
|
|
// joint oil and fuel pump
|
|
if (ggOilFuelPumpButton.type() != TGaugeType::push)
|
|
{
|
|
ggOilFuelPumpButton.PutValue(mvControlled->FuelPump.is_enabled && 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 ([[maybe_unused]] auto const &item : ggInverterToggleButtons) // for each button
|
|
{
|
|
int itemindex = itemstart;
|
|
itemstart++;
|
|
TDynamicObject *p = DynamicObject->GetFirstDynamic(mvOccupied->CabOccupied < 0 ? end::rear : end::front, flag);
|
|
while (p)
|
|
{
|
|
if (p->MoverParameters->eimc[eimc_p_Pmax] > 1)
|
|
{
|
|
if (itemindex < p->MoverParameters->InvertersNo)
|
|
{
|
|
// visual feedback
|
|
ggInverterToggleButtons[itemstart - 1].PutValue(p->MoverParameters->Inverters[itemindex].Activate ? 1.0 : 0.0);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
itemindex -= p->MoverParameters->InvertersNo;
|
|
}
|
|
}
|
|
p = kier ? p->Next(flag) : p->Prev(flag);
|
|
}
|
|
}
|
|
|
|
// we reset all indicators, as they're set during the update pass
|
|
// TODO: when cleaning up break setting indicator state into a separate function, so we can reuse it
|
|
}
|
|
|
|
// initializes a button matching provided label. returns: true if the label was found, false otherwise
|
|
// TODO: refactor the cabin controls into some sensible structure
|
|
bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int const Cabindex)
|
|
{
|
|
|
|
std::unordered_map<std::string, TButton &> const lights = {
|
|
{"i-maxft:", btLampkaMaxSila},
|
|
{"i-maxftt:", btLampkaPrzekrMaxSila},
|
|
{"i-radio:", btLampkaRadio},
|
|
{"i-radiomessage:", btLampkaRadioMessage},
|
|
{"i-radiostop:", btLampkaRadioStop},
|
|
{"i-manual_brake:", btLampkaHamulecReczny},
|
|
{"i-door_blocked:", btLampkaBlokadaDrzwi},
|
|
{"i-door_blockedoff:", btLampkaDoorLockOff},
|
|
{"i-slippery:", btLampkaPoslizg},
|
|
{"i-contactors:", btLampkaStyczn},
|
|
{"i-conv_ovld:", btLampkaNadmPrzetw},
|
|
{"i-converter:", btLampkaPrzetw},
|
|
{"i-converteroff:", btLampkaPrzetwOff},
|
|
{"i-converterb:", btLampkaPrzetwB},
|
|
{"i-converterboff:", btLampkaPrzetwBOff},
|
|
{"i-diff_relay:", btLampkaPrzekRozn},
|
|
{"i-diff_relay2:", btLampkaPrzekRoznPom},
|
|
{"i-motor_ovld:", btLampkaNadmSil},
|
|
{"i-train_controll:", btLampkaUkrotnienie},
|
|
{"i-brake_delay_r:", btLampkaHamPosp},
|
|
{"i-mainbreaker:", btLampkaWylSzybki},
|
|
{"i-mainbreakerb:", btLampkaWylSzybkiB},
|
|
{"i-mainbreakeroff:", btLampkaWylSzybkiOff},
|
|
{"i-mainbreakerboff:", btLampkaWylSzybkiBOff},
|
|
{"i-mainbreakerready:", btLampkaMainBreakerReady},
|
|
{"i-mainbreakerblinking:", btLampkaMainBreakerBlinkingIfReady},
|
|
{"i-vent_ovld:", btLampkaNadmWent},
|
|
{"i-comp_ovld:", btLampkaNadmSpr},
|
|
// overheat indicator lamps
|
|
{"i-oil_overheat:", btLampkaOilOverheat},
|
|
{"i-water_overheat:", btLampkaWaterOverheat},
|
|
{"i-wateraux_overheat:", btLampkaWaterAuxOverheat},
|
|
{"i-engine_overheat:", btLampkaEngineOverheat},
|
|
{"i-resistors:", btLampkaOpory},
|
|
{"i-no_resistors:", btLampkaBezoporowa},
|
|
{"i-no_resistors_b:", btLampkaBezoporowaB},
|
|
{"i-highcurrent:", btLampkaWysRozr},
|
|
{"i-vent_trim:", btLampkaWentZaluzje},
|
|
{"i-motorblowers:", btLampkaMotorBlowers},
|
|
{"i-coolingfans:", btLampkaCoolingFans},
|
|
{"i-tempomat:", btLampkaTempomat},
|
|
{"i-distancecounter:", btLampkaDistanceCounter},
|
|
{"i-trainheating:", btLampkaOgrzewanieSkladu},
|
|
{"i-security_aware:", btLampkaCzuwaka},
|
|
{"i-security_cabsignal:", btLampkaSHP},
|
|
{"i-security_aware_cabsignal:", btLampkaCzuwakaSHP},
|
|
{"i-door_left:", btLampkaDoorLeft},
|
|
{"i-door_right:", btLampkaDoorRight},
|
|
{"i-departure_signal:", btLampkaDepartureSignal},
|
|
{"i-reserve:", btLampkaRezerwa},
|
|
{"i-scnd:", btLampkaBoczniki},
|
|
{"i-scnd1:", btLampkaBocznik1},
|
|
{"i-scnd2:", btLampkaBocznik2},
|
|
{"i-scnd3:", btLampkaBocznik3},
|
|
{"i-scnd4:", btLampkaBocznik4},
|
|
{"i-gear1:", btLampkaGear1},
|
|
{"i-gear2:", btLampkaGear2},
|
|
{"i-hydrolockup:", btLampkaHydroLockup},
|
|
{"i-braking:", btLampkaHamienie},
|
|
{"i-brakingoff:", btLampkaBrakingOff},
|
|
{"i-dynamicbrake:", btLampkaED},
|
|
{"i-brakeprofileg:", btLampkaBrakeProfileG},
|
|
{"i-brakeprofilep:", btLampkaBrakeProfileP},
|
|
{"i-brakeprofiler:", btLampkaBrakeProfileR},
|
|
{"i-springbrakeactive:", btLampkaSpringBrakeActive},
|
|
{"i-springbrakeinactive:", btLampkaSpringBrakeInactive},
|
|
{"i-braking-ezt:", btLampkaHamowanie1zes},
|
|
{"i-braking-ezt2:", btLampkaHamowanie2zes},
|
|
{"i-compressor:", btLampkaSprezarka},
|
|
{"i-compressorb:", btLampkaSprezarkaB},
|
|
{"i-compressoroff:", btLampkaSprezarkaOff},
|
|
{"i-compressorboff:", btLampkaSprezarkaBOff},
|
|
{"i-fuelpumpoff:", btLampkaFuelPumpOff},
|
|
{"i-voltbrake:", btLampkaNapNastHam},
|
|
{"i-resistorsb:", btLampkaOporyB},
|
|
{"i-contactorsb:", btLampkaStycznB},
|
|
{"i-conv_ovldb:", btLampkaNadmPrzetwB},
|
|
{"i-hvoltageb:", btLampkaHVoltageB},
|
|
{"i-malfunction:", btLampkaMalfunction},
|
|
{"i-malfunctionb:", btLampkaMalfunctionB},
|
|
{"i-forward:", btLampkaForward},
|
|
{"i-backward:", btLampkaBackward},
|
|
{"i-neutral:", btLampkaNeutral},
|
|
{"i-upperlight:", btLampkaUpperLight},
|
|
{"i-leftlight:", btLampkaLeftLight},
|
|
{"i-rightlight:", btLampkaRightLight},
|
|
{"i-leftend:", btLampkaLeftEndLight},
|
|
{"i-rightend:", btLampkaRightEndLight},
|
|
{"i-rearupperlight:", btLampkaRearUpperLight},
|
|
{"i-rearleftlight:", btLampkaRearLeftLight},
|
|
{"i-rearrightlight:", btLampkaRearRightLight},
|
|
{"i-rearleftend:", btLampkaRearLeftEndLight},
|
|
{"i-rearrightend:", btLampkaRearRightEndLight},
|
|
{"i-dashboardlight:", btDashboardLight},
|
|
{"i-timetablelight:", btTimetableLight},
|
|
{"i-universal0:", btUniversals[0]},
|
|
{"i-universal1:", btUniversals[1]},
|
|
{"i-universal2:", btUniversals[2]},
|
|
{"i-universal3:", btUniversals[3]},
|
|
{"i-universal4:", btUniversals[4]},
|
|
{"i-universal5:", btUniversals[5]},
|
|
{"i-universal6:", btUniversals[6]},
|
|
{"i-universal7:", btUniversals[7]},
|
|
{"i-universal8:", btUniversals[8]},
|
|
{"i-universal9:", btUniversals[9]},
|
|
{"i-cabactived:", btCabActived},
|
|
{"i-aklvents:", btAKLVents},
|
|
{"i-compressorany:", btCompressors},
|
|
{"i-edenabled", btEDenabled},
|
|
};
|
|
{
|
|
auto lookup = lights.find(Label);
|
|
if (lookup != lights.end())
|
|
{
|
|
lookup->second.Load(Parser, DynamicObject);
|
|
return true;
|
|
}
|
|
}
|
|
// TODO: move viable dedicated lights to the automatic light array
|
|
std::unordered_map<std::string, bool const *> const autolights = {
|
|
{"i-doors:", &m_doors},
|
|
{"i-doorpermit_left:", &m_doorspermitleft},
|
|
{"i-doorpermit_right:", &m_doorspermitright},
|
|
{"i-doorpermit_any:", &m_doorpermits},
|
|
{"i-doorstep:", &mvOccupied->Doors.step_enabled},
|
|
{"i-mainpipelock:", &mvOccupied->LockPipe},
|
|
{"i-battery:", &mvOccupied->Power24vIsAvailable},
|
|
{"i-cablight:", &Cabine[iCabn].bLight},
|
|
};
|
|
{
|
|
auto lookup = autolights.find(Label);
|
|
if (lookup != autolights.end())
|
|
{
|
|
auto &button = Cabine[Cabindex].Button(-1); // pierwsza wolna lampka
|
|
button.Load(Parser, DynamicObject);
|
|
button.AssignBool(lookup->second);
|
|
return true;
|
|
}
|
|
}
|
|
// custom lights
|
|
if (Label == "i-instrumentlight:")
|
|
{
|
|
btInstrumentLight.Load(Parser, DynamicObject);
|
|
InstrumentLightType = 0;
|
|
}
|
|
else if (Label == "i-instrumentlight_m:")
|
|
{
|
|
btInstrumentLight.Load(Parser, DynamicObject);
|
|
InstrumentLightType = 1;
|
|
}
|
|
else if (Label == "i-instrumentlight_c:")
|
|
{
|
|
btInstrumentLight.Load(Parser, DynamicObject);
|
|
InstrumentLightType = 2;
|
|
}
|
|
else if (Label == "i-instrumentlight_a:")
|
|
{
|
|
btInstrumentLight.Load(Parser, DynamicObject);
|
|
InstrumentLightType = 3;
|
|
}
|
|
else if (Label == "i-instrumentlight_l:")
|
|
{
|
|
btInstrumentLight.Load(Parser, DynamicObject);
|
|
InstrumentLightType = 4;
|
|
}
|
|
else if (Label == "i-doors:")
|
|
{
|
|
int i = Parser.getToken<int>() - 1;
|
|
auto &button = Cabine[Cabindex].Button(-1); // pierwsza wolna lampka
|
|
button.Load(Parser, DynamicObject);
|
|
button.AssignBool(bDoors[0] + 3 * i);
|
|
}
|
|
else
|
|
{
|
|
// failed to match the label
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// initializes a gauge matching provided label. returns: true if the label was found, false otherwise
|
|
// TODO: refactor the cabin controls into some sensible structure
|
|
bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int const Cabindex)
|
|
{
|
|
|
|
std::unordered_map<std::string, TGauge &> const gauges = {{"jointctrl:", ggJointCtrl},
|
|
{"mainctrl:", ggMainCtrl},
|
|
{"scndctrl:", ggScndCtrl},
|
|
{"dirkey:", ggDirKey},
|
|
{"brakectrl:", ggBrakeCtrl},
|
|
{"localbrake:", ggLocalBrake},
|
|
{"alarmchain:", ggAlarmChain},
|
|
{"brakeprofile_sw:", ggBrakeProfileCtrl},
|
|
{"brakeprofileg_sw:", ggBrakeProfileG},
|
|
{"brakeprofiler_sw:", ggBrakeProfileR},
|
|
{"brakeopmode_sw:", ggBrakeOperationModeCtrl},
|
|
{"maxcurrent_sw:", ggMaxCurrentCtrl},
|
|
{"main_off_bt:", ggMainOffButton},
|
|
{"main_on_bt:", ggMainOnButton},
|
|
{"security_reset_bt:", ggSecurityResetButton},
|
|
{"shp_reset_bt:", ggSHPResetButton},
|
|
{"releaser_bt:", ggReleaserButton},
|
|
{"springbrakeon_bt:", ggSpringBrakeOnButton},
|
|
{"springbrakeoff_bt:", ggSpringBrakeOffButton},
|
|
{"universalbrake1_bt:", ggUniveralBrakeButton1},
|
|
{"universalbrake2_bt:", ggUniveralBrakeButton2},
|
|
{"universalbrake3_bt:", ggUniveralBrakeButton3},
|
|
{"epbrake_bt:", ggEPFuseButton},
|
|
{"sand_bt:", ggSandButton},
|
|
{"autosandallow_sw:", ggAutoSandButton},
|
|
{"antislip_bt:", ggAntiSlipButton},
|
|
{"horn_bt:", ggHornButton},
|
|
{"hornlow_bt:", ggHornLowButton},
|
|
{"hornhigh_bt:", ggHornHighButton},
|
|
{"whistle_bt:", ggWhistleButton},
|
|
{"helper_bt:", ggHelperButton},
|
|
{"fuse_bt:", ggFuseButton},
|
|
{"converterfuse_bt:", ggConverterFuseButton},
|
|
{"stlinoff_bt:", ggStLinOffButton},
|
|
{"doorpermitpreset_sw:", ggDoorPermitPresetButton},
|
|
{"door_left_sw:", ggDoorLeftButton},
|
|
{"door_right_sw:", ggDoorRightButton},
|
|
{"doorlefton_sw:", ggDoorLeftOnButton},
|
|
{"doorrighton_sw:", ggDoorRightOnButton},
|
|
{"doorleftoff_sw:", ggDoorLeftOffButton},
|
|
{"doorrightoff_sw:", ggDoorRightOffButton},
|
|
{"doorallon_sw:", ggDoorAllOnButton},
|
|
{"departure_signal_bt:", ggDepartureSignalButton},
|
|
{"upperlight_sw:", ggUpperLightButton},
|
|
{"leftlight_sw:", ggLeftLightButton},
|
|
{"rightlight_sw:", ggRightLightButton},
|
|
{"dimheadlights_sw:", ggDimHeadlightsButton},
|
|
{"leftend_sw:", ggLeftEndLightButton},
|
|
{"rightend_sw:", ggRightEndLightButton},
|
|
{"lights_sw:", ggLightsButton},
|
|
{"moderndimmer_sw:", ggModernLightDimSw},
|
|
{"rearupperlight_sw:", ggRearUpperLightButton},
|
|
{"rearleftlight_sw:", ggRearLeftLightButton},
|
|
{"rearrightlight_sw:", ggRearRightLightButton},
|
|
{"rearleftend_sw:", ggRearLeftEndLightButton},
|
|
{"rearrightend_sw:", ggRearRightEndLightButton},
|
|
{"compressor_sw:", ggCompressorButton},
|
|
{"compressorlocal_sw:", ggCompressorLocalButton},
|
|
{"compressorlist_sw:", ggCompressorListButton},
|
|
{"converter_sw:", ggConverterButton},
|
|
{"converterlocal_sw:", ggConverterLocalButton},
|
|
{"converteroff_sw:", ggConverterOffButton},
|
|
{"main_sw:", ggMainButton},
|
|
{"waterpumpbreaker_sw:", ggWaterPumpBreakerButton},
|
|
{"waterpump_sw:", ggWaterPumpButton},
|
|
{"waterheaterbreaker_sw:", ggWaterHeaterBreakerButton},
|
|
{"waterheater_sw:", ggWaterHeaterButton},
|
|
{"water1tempb:", ggWater1TempB},
|
|
{"watercircuitslink_sw:", ggWaterCircuitsLinkButton},
|
|
{"fuelpump_sw:", ggFuelPumpButton},
|
|
{"oilpump_sw:", ggOilPumpButton},
|
|
{"oilfuelpump_sw:", ggOilFuelPumpButton},
|
|
{"oilpressb:", ggOilPressB},
|
|
{"motorblowersfront_sw:", ggMotorBlowersFrontButton},
|
|
{"motorblowersrear_sw:", ggMotorBlowersRearButton},
|
|
{"motorblowersalloff_sw:", ggMotorBlowersAllOffButton},
|
|
{"radiochannel_sw:", ggRadioChannelSelector},
|
|
{"radiochannelprev_sw:", ggRadioChannelPrevious},
|
|
{"radiochannelnext_sw:", ggRadioChannelNext},
|
|
{"radiostop_sw:", ggRadioStop},
|
|
{"radiotest_sw:", ggRadioTest},
|
|
{"radiocall1_sw:", ggRadioCall1},
|
|
{"radiocall3_sw:", ggRadioCall3},
|
|
{"radiovolume_sw:", ggRadioVolumeSelector},
|
|
{"radiovolumeprev_sw:", ggRadioVolumePrevious},
|
|
{"radiovolumenext_sw:", ggRadioVolumeNext},
|
|
/*
|
|
{ "pantfront_sw:", ggPantFrontButton },
|
|
{ "pantrear_sw:", ggPantRearButton },
|
|
{ "pantfrontoff_sw:", ggPantFrontButtonOff },
|
|
{ "pantrearoff_sw:", ggPantRearButtonOff },
|
|
*/
|
|
{"pantalloff_sw:", ggPantAllDownButton},
|
|
{"pantselected_sw:", ggPantSelectedButton},
|
|
{"pantselectedoff_sw:", ggPantSelectedDownButton},
|
|
{"pantvalves_sw:", ggPantValvesButton},
|
|
{"pantcompressor_sw:", ggPantCompressorButton},
|
|
{"pantcompressorvalve_sw:", ggPantCompressorValve},
|
|
{"trainheating_sw:", ggTrainHeatingButton},
|
|
{"signalling_sw:", ggSignallingButton},
|
|
{"door_signalling_sw:", ggDoorSignallingButton},
|
|
{"nextcurrent_sw:", ggNextCurrentButton},
|
|
{"instrumentlight_sw:", ggInstrumentLightButton},
|
|
{"dashboardlight_sw:", ggDashboardLightButton},
|
|
{"timetablelight_sw:", ggTimetableLightButton},
|
|
{"cablightdim_sw:", ggCabLightDimButton},
|
|
{"compartmentlights_sw:", ggCompartmentLightsButton},
|
|
{"compartmentlightson_sw:", ggCompartmentLightsOnButton},
|
|
{"compartmentlightsoff_sw:", ggCompartmentLightsOffButton},
|
|
{"battery_sw:", ggBatteryButton},
|
|
{"batteryon_sw:", ggBatteryOnButton},
|
|
{"batteryoff_sw:", ggBatteryOffButton},
|
|
{"cabactivation_sw:", ggCabActivationButton},
|
|
{"distancecounter_sw:", ggDistanceCounterButton},
|
|
{"relayreset1_bt:", ggRelayResetButtons[0]},
|
|
{"relayreset2_bt:", ggRelayResetButtons[1]},
|
|
{"relayreset3_bt:", ggRelayResetButtons[2]},
|
|
{"universal0:", ggUniversals[0]},
|
|
{"universal1:", ggUniversals[1]},
|
|
{"universal2:", ggUniversals[2]},
|
|
{"universal3:", ggUniversals[3]},
|
|
{"universal4:", ggUniversals[4]},
|
|
{"universal5:", ggUniversals[5]},
|
|
{"universal6:", ggUniversals[6]},
|
|
{"universal7:", ggUniversals[7]},
|
|
{"universal8:", ggUniversals[8]},
|
|
{"universal9:", ggUniversals[9]},
|
|
{"universal10:", ggUniversals[10]},
|
|
{"universal11:", ggUniversals[11]},
|
|
{"universal12:", ggUniversals[12]},
|
|
{"universal13:", ggUniversals[13]},
|
|
{"universal14:", ggUniversals[14]},
|
|
{"universal15:", ggUniversals[15]},
|
|
{"universal16:", ggUniversals[16]},
|
|
{"universal17:", ggUniversals[17]},
|
|
{"universal18:", ggUniversals[18]},
|
|
{"universal19:", ggUniversals[19]},
|
|
{"universal20:", ggUniversals[20]},
|
|
{"universal21:", ggUniversals[21]},
|
|
{"universal22:", ggUniversals[22]},
|
|
{"universal23:", ggUniversals[23]},
|
|
{"universal24:", ggUniversals[24]},
|
|
{"universal25:", ggUniversals[25]},
|
|
{"universal26:", ggUniversals[26]},
|
|
{"universal27:", ggUniversals[27]},
|
|
{"universal28:", ggUniversals[28]},
|
|
{"universal29:", ggUniversals[29]},
|
|
{"inverterenable1_bt:", ggInverterEnableButtons[0]},
|
|
{"inverterenable2_bt:", ggInverterEnableButtons[1]},
|
|
{"inverterenable3_bt:", ggInverterEnableButtons[2]},
|
|
{"inverterenable4_bt:", ggInverterEnableButtons[3]},
|
|
{"inverterenable5_bt:", ggInverterEnableButtons[4]},
|
|
{"inverterenable6_bt:", ggInverterEnableButtons[5]},
|
|
{"inverterenable7_bt:", ggInverterEnableButtons[6]},
|
|
{"inverterenable8_bt:", ggInverterEnableButtons[7]},
|
|
{"inverterenable9_bt:", ggInverterEnableButtons[8]},
|
|
{"inverterenable10_bt:", ggInverterEnableButtons[9]},
|
|
{"inverterenable11_bt:", ggInverterEnableButtons[10]},
|
|
{"inverterenable12_bt:", ggInverterEnableButtons[11]},
|
|
{"inverterdisable1_bt:", ggInverterDisableButtons[0]},
|
|
{"inverterdisable2_bt:", ggInverterDisableButtons[1]},
|
|
{"inverterdisable3_bt:", ggInverterDisableButtons[2]},
|
|
{"inverterdisable4_bt:", ggInverterDisableButtons[3]},
|
|
{"inverterdisable5_bt:", ggInverterDisableButtons[4]},
|
|
{"inverterdisable6_bt:", ggInverterDisableButtons[5]},
|
|
{"inverterdisable7_bt:", ggInverterDisableButtons[6]},
|
|
{"inverterdisable8_bt:", ggInverterDisableButtons[7]},
|
|
{"inverterdisable9_bt:", ggInverterDisableButtons[8]},
|
|
{"inverterdisable10_bt:", ggInverterDisableButtons[9]},
|
|
{"inverterdisable11_bt:", ggInverterDisableButtons[10]},
|
|
{"inverterdisable12_bt:", ggInverterDisableButtons[11]},
|
|
{"invertertoggle1_bt:", ggInverterToggleButtons[0]},
|
|
{"invertertoggle2_bt:", ggInverterToggleButtons[1]},
|
|
{"invertertoggle3_bt:", ggInverterToggleButtons[2]},
|
|
{"invertertoggle4_bt:", ggInverterToggleButtons[3]},
|
|
{"invertertoggle5_bt:", ggInverterToggleButtons[4]},
|
|
{"invertertoggle6_bt:", ggInverterToggleButtons[5]},
|
|
{"invertertoggle7_bt:", ggInverterToggleButtons[6]},
|
|
{"invertertoggle8_bt:", ggInverterToggleButtons[7]},
|
|
{"invertertoggle9_bt:", ggInverterToggleButtons[8]},
|
|
{"invertertoggle10_bt:", ggInverterToggleButtons[9]},
|
|
{"invertertoggle11_bt:", ggInverterToggleButtons[10]},
|
|
{"invertertoggle12_bt:", ggInverterToggleButtons[11]},
|
|
{"pantvalvesupdate_bt:", ggPantValvesUpdate},
|
|
{"pantvalvesoff_bt:", ggPantValvesOff},
|
|
{"wipers_sw:", ggWiperSw}};
|
|
{
|
|
auto const lookup{gauges.find(Label)};
|
|
if (lookup != gauges.end())
|
|
{
|
|
lookup->second.Load(Parser, DynamicObject);
|
|
m_controlmapper.insert(lookup->second, lookup->first);
|
|
return true;
|
|
}
|
|
}
|
|
// dedicated gauges with state-driven optional submodel
|
|
// TODO: move viable gauges here
|
|
// TODO: convert dedicated gauges to auto-allocated ones, replace dedicated references in command handlers to mapper lookups
|
|
std::unordered_map<std::string, std::tuple<TGauge &, bool const *>> const stategauges = {
|
|
{"tempomat_sw:", {ggScndCtrlButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"tempomatoff_sw:", {ggScndCtrlOffButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedinc_bt:", {ggSpeedControlIncreaseButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speeddec_bt:", {ggSpeedControlDecreaseButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedctrlpowerinc_bt:", {ggSpeedControlPowerIncreaseButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedctrlpowerdec_bt:", {ggSpeedControlPowerDecreaseButton, &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedbutton0:", {ggSpeedCtrlButtons[0], &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedbutton1:", {ggSpeedCtrlButtons[1], &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedbutton2:", {ggSpeedCtrlButtons[2], &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedbutton3:", {ggSpeedCtrlButtons[3], &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedbutton4:", {ggSpeedCtrlButtons[4], &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedbutton5:", {ggSpeedCtrlButtons[5], &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedbutton6:", {ggSpeedCtrlButtons[6], &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedbutton7:", {ggSpeedCtrlButtons[7], &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedbutton8:", {ggSpeedCtrlButtons[8], &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"speedbutton9:", {ggSpeedCtrlButtons[9], &mvOccupied->SpeedCtrlUnit.IsActive}},
|
|
{"doorleftpermit_sw:", {ggDoorLeftPermitButton, &m_doorspermitleft}},
|
|
{"doorrightpermit_sw:", {ggDoorRightPermitButton, &m_doorspermitright}},
|
|
{"dooralloff_sw:", {ggDoorAllOffButton, &m_doors}},
|
|
{"doorstep_sw:", {ggDoorStepButton, &mvOccupied->Doors.step_enabled}},
|
|
{"dirforward_bt:", {ggDirForwardButton, &m_dirforward}},
|
|
{"dirneutral_bt:", {ggDirNeutralButton, &m_dirneutral}},
|
|
{"dirbackward_bt:", {ggDirBackwardButton, &m_dirbackward}},
|
|
};
|
|
{
|
|
auto const lookup{stategauges.find(Label)};
|
|
if (lookup != stategauges.end())
|
|
{
|
|
auto &gauge{std::get<TGauge &>(lookup->second)};
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignState(std::get<bool const *>(lookup->second));
|
|
m_controlmapper.insert(gauge, lookup->first);
|
|
return true;
|
|
}
|
|
}
|
|
// TODO: move viable dedicated gauges to the automatic array
|
|
std::unordered_map<std::string, bool *> const autoboolgauges = {
|
|
{"doormode_sw:", &mvOccupied->Doors.remote_only},
|
|
{"coolingfans_sw:", &mvControlled->RVentForceOn},
|
|
{"pantfront_sw:", &mvPantographUnit->Pantographs[end::front].valve.is_enabled},
|
|
{"pantrear_sw:", &mvPantographUnit->Pantographs[end::rear].valve.is_enabled},
|
|
{"pantfrontoff_sw:", &mvPantographUnit->Pantographs[end::front].valve.is_disabled},
|
|
{"pantrearoff_sw:", &mvPantographUnit->Pantographs[end::rear].valve.is_disabled},
|
|
{"radio_sw:", &mvOccupied->Radio},
|
|
{"cablight_sw:", &Cabine[iCabn].bLight},
|
|
{"springbraketoggle_bt:", &mvOccupied->SpringBrake.Activate},
|
|
{"couplingdisconnect_sw:", &m_couplingdisconnect},
|
|
{"couplingdisconnectback_sw:", &m_couplingdisconnectback},
|
|
{"mirrors_sw:", &mvOccupied->MirrorForbidden},
|
|
};
|
|
{
|
|
auto lookup = autoboolgauges.find(Label);
|
|
if (lookup != autoboolgauges.end())
|
|
{
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna lampka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignBool(lookup->second);
|
|
m_controlmapper.insert(gauge, lookup->first);
|
|
return true;
|
|
}
|
|
}
|
|
// TODO: move viable dedicated gauges to the automatic array
|
|
std::unordered_map<std::string, int *> const autointgauges = {
|
|
{"manualbrake:", &mvOccupied->ManualBrakePos},
|
|
{"pantselect_sw:", &mvOccupied->PantsPreset.second[cab_to_end()]},
|
|
};
|
|
{
|
|
auto lookup = autointgauges.find(Label);
|
|
if (lookup != autointgauges.end())
|
|
{
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna lampka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignInt(lookup->second);
|
|
m_controlmapper.insert(gauge, lookup->first);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// ABu 090305: uniwersalne przyciski lub inne rzeczy
|
|
if (Label == "mainctrlact:")
|
|
{
|
|
ggMainCtrlAct.Load(Parser, DynamicObject);
|
|
}
|
|
// SEKCJA WSKAZNIKOW
|
|
else if (Label == "tachometer:" || Label == "tachometerb:")
|
|
{
|
|
// predkosciomierz wskaźnikowy z szarpaniem
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(&fTachoVelocityJump);
|
|
// bind tachometer sound location to the meter
|
|
if (dsbHasler && dsbHasler->offset() == glm::vec3())
|
|
{
|
|
dsbHasler->offset(gauge.model_offset());
|
|
}
|
|
}
|
|
else if (Label == "tachometern:")
|
|
{
|
|
// predkosciomierz wskaźnikowy bez szarpania
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(&fTachoVelocity);
|
|
// bind tachometer sound location to the meter
|
|
if (dsbHasler && dsbHasler->offset() == glm::vec3())
|
|
{
|
|
dsbHasler->offset(gauge.model_offset());
|
|
}
|
|
}
|
|
else if (Label == "tachometerd:")
|
|
{
|
|
// predkosciomierz cyfrowy
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(&fTachoVelocity);
|
|
// bind tachometer sound location to the meter
|
|
if (dsbHasler && dsbHasler->offset() == glm::vec3())
|
|
{
|
|
dsbHasler->offset(gauge.model_offset());
|
|
}
|
|
}
|
|
else if (Label == "hvcurrent1:" || Label == "hvcurrent1b:")
|
|
{
|
|
// 1szy amperomierz
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(fHCurrent + 1);
|
|
}
|
|
else if (Label == "hvcurrent2:" || Label == "hvcurrent2b:")
|
|
{
|
|
// 2gi amperomierz
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(fHCurrent + 2);
|
|
}
|
|
else if (Label == "hvcurrent3:" || Label == "hvcurrent3b:")
|
|
{
|
|
// 3ci amperomierz
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałska
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(fHCurrent + 3);
|
|
}
|
|
else if (Label == "hvcurrent:" || Label == "hvcurrentb:")
|
|
{
|
|
// amperomierz calkowitego pradu
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(fHCurrent);
|
|
}
|
|
else if (Label == "eimscreen:")
|
|
{
|
|
// amperomierz calkowitego pradu
|
|
int i;
|
|
int 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;
|
|
int j;
|
|
Parser.getTokens(2, false);
|
|
Parser >> i >> j;
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject, 0.1);
|
|
gauge.AssignFloat(&fPress[std::clamp(i, 1, 20) - 1][std::clamp(j, 0, 3)]);
|
|
}
|
|
else if (Label == "brakepress:" || Label == "brakepressb:")
|
|
{
|
|
// manometr cylindrow hamulcowych
|
|
// Ra 2014-08: przeniesione do TCab
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject, 0.1);
|
|
gauge.AssignDouble(&mvOccupied->BrakePress);
|
|
}
|
|
else if (Label == "pipepress:" || Label == "pipepressb:")
|
|
{
|
|
// manometr przewodu hamulcowego
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject, 0.1);
|
|
gauge.AssignDouble(&mvOccupied->PipePress);
|
|
}
|
|
else if (Label == "scndpress:")
|
|
{
|
|
// manometr przewodu hamulcowego
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject, 0.1);
|
|
gauge.AssignDouble(&mvOccupied->ScndPipePress);
|
|
}
|
|
else if (Label == "limpipepress:")
|
|
{
|
|
// manometr zbiornika sterujacego zaworu maszynisty
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject, 0.1);
|
|
gauge.AssignDouble(&m_brakehandlecp);
|
|
}
|
|
else if (Label == "cntrlpress:")
|
|
{
|
|
// manometr zbiornika kontrolnego/rorz�du
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject, 0.1);
|
|
gauge.AssignDouble(&mvPantographUnit->PantPress);
|
|
}
|
|
else if (Label == "springbrakepress:")
|
|
{
|
|
// manometr cylindra hamulca sprężynowego
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject, 0.1);
|
|
gauge.AssignDouble(&mvOccupied->SpringBrake.SBP);
|
|
}
|
|
else if (Label == "epctrlvalue:")
|
|
{
|
|
// wskazowka sterowania sila hamulca ep
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject, 0.1);
|
|
gauge.AssignDouble(&mvOccupied->EpForce);
|
|
}
|
|
else if (Label == "compressor:" || Label == "compressorb:")
|
|
{
|
|
// manometr sprezarki/zbiornika glownego
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject, 0.1);
|
|
gauge.AssignDouble(&mvOccupied->Compressor);
|
|
}
|
|
else if (Label == "oilpress:")
|
|
{
|
|
// oil pressure
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(&mvControlled->OilPump.pressure);
|
|
}
|
|
else if (Label == "oiltemp:")
|
|
{
|
|
// oil temperature
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(&mvControlled->dizel_heat.To);
|
|
}
|
|
else if (Label == "water1temp:")
|
|
{
|
|
// main circuit water temperature
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(&mvControlled->dizel_heat.temperatura1);
|
|
}
|
|
else if (Label == "water2temp:")
|
|
{
|
|
// auxiliary circuit water temperature
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject);
|
|
gauge.AssignFloat(&mvControlled->dizel_heat.temperatura2);
|
|
}
|
|
else if (Label == "pantpress:")
|
|
{
|
|
// pantograph tank pressure
|
|
auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka
|
|
gauge.Load(Parser, DynamicObject, 0.1);
|
|
gauge.AssignDouble(&mvPantographUnit->PantPress);
|
|
}
|
|
// yB - dla drugiej sekcji
|
|
else if (Label == "hvbcurrent1:")
|
|
{
|
|
// 1szy amperomierz
|
|
ggI1B.Load(Parser, DynamicObject);
|
|
}
|
|
else if (Label == "hvbcurrent2:")
|
|
{
|
|
// 2gi amperomierz
|
|
ggI2B.Load(Parser, DynamicObject);
|
|
}
|
|
else if (Label == "hvbcurrent3:")
|
|
{
|
|
// 3ci amperomierz
|
|
ggI3B.Load(Parser, DynamicObject);
|
|
}
|
|
else if (Label == "hvbcurrent:")
|
|
{
|
|
// amperomierz calkowitego pradu
|
|
ggItotalB.Load(Parser, DynamicObject);
|
|
}
|
|
//*************************************************************
|
|
else if (Label == "clock:")
|
|
{
|
|
// zegar analogowy
|
|
if (Parser.getToken<std::string>() == "analog" && DynamicObject->mdKabina)
|
|
{
|
|
// McZapkie-300302: zegarek
|
|
ggClockSInd.Init(DynamicObject->mdKabina->GetFromName("ClockShand"), nullptr, TGaugeAnimation::gt_Rotate, static_cast<float>(1.0 / 60.0));
|
|
ggClockMInd.Init(DynamicObject->mdKabina->GetFromName("ClockMhand"), nullptr, TGaugeAnimation::gt_Rotate, static_cast<float>(1.0 / 60.0));
|
|
ggClockHInd.Init(DynamicObject->mdKabina->GetFromName("ClockHhand"), nullptr, TGaugeAnimation::gt_Rotate, static_cast<float>(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;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
// updates trains in range [Start, End)
|
|
void update_trains(std::deque<TTrain *> const &Trains, std::size_t const Start, std::size_t const End, double const Dt)
|
|
{
|
|
for (std::size_t j = Start; j < End; ++j)
|
|
{
|
|
if (Trains[j])
|
|
Trains[j]->Update(Dt);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
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<std::jthread> workers;
|
|
std::vector<std::future<void>> results; // przekazują wyjątki z wątków do głównego wątku
|
|
workers.reserve(threads);
|
|
results.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
|
|
|
|
std::packaged_task<void()> task([this, start, end, dt]() { update_trains(m_items, start, end, dt); });
|
|
results.emplace_back(task.get_future());
|
|
workers.emplace_back(std::move(task));
|
|
}
|
|
|
|
// Poczekaj aż wszystkie wątki skończą
|
|
for (auto &worker : workers)
|
|
worker.join();
|
|
|
|
for (auto &result : results)
|
|
result.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;
|
|
}
|