18
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-15 02:29:07 +01:00

feat: bistable hold coil lever of the line breaker (eu07/maszyna#364)

main_off_bt declared with "type: toggle" in the cab's .mmd is now the
line breaker's hold coil lever of the ET22 and EP09: it stays where it is
put. Off, it opens the line breaker and keeps it from closing (also
cancelling a closing armed by a held main_on_bt); back on, it only feeds
the coil again. The single keyboard key puts the lever back on before
closing, and a line breaker closed by the AI or through the control
cable puts the lever back on to match.

TGauge now remembers whether type: was given, since every control
defaults to toggle. Without type: on main_off_bt (all vehicles today)
nothing changes.

Data (SVN, not part of this commit): add "type: toggle" to main_off_bt:
in dynamic/pkp/et22_v3/*.mmd and dynamic/pkp/ep09_v1, ep09_v2/*.mmd.
Older exes ignore it.
This commit is contained in:
jerrrrycho
2026-11-07 18:10:59 +01:00
parent 939e42b611
commit d7c66fa7bc
5 changed files with 112 additions and 2 deletions

View File

@@ -0,0 +1,30 @@
#include "stdafx.h"
#include "doctest.h"
#include "vehicle/holdcoillever.h"
// the lever of the line breaker's hold coil on the ET22 and the EP09 stays where it's put (eu07/maszyna#364), when the
// cab declares its main_off_bt control with "type: toggle"; otherwise main_off_bt is the push button it always was
TEST_CASE("hold coil lever: only a main_off_bt declared as a toggle switch is the bistable lever (eu07/maszyna#364)") {
CHECK(hold_coil_lever::is_bistable(true, true));
// declared as a push button
CHECK_FALSE(hold_coil_lever::is_bistable(true, false));
// no type given: a control defaults to the toggle type, which existing cabs relied on for the push button
CHECK_FALSE(hold_coil_lever::is_bistable(false, true));
}
TEST_CASE("hold coil lever: operated, it goes over to the other position and stays there") {
// on: the hold coil fed, the line breaker can close
CHECK_FALSE(hold_coil_lever::cuts_off(0.f));
auto const off{hold_coil_lever::operated(0.f)};
CHECK(off == doctest::Approx(1.f));
CHECK(hold_coil_lever::cuts_off(off));
// operated again it goes back on
auto const on{hold_coil_lever::operated(off)};
CHECK(on == doctest::Approx(0.f));
CHECK_FALSE(hold_coil_lever::cuts_off(on));
// caught on the way between the positions it goes to the far one
CHECK(hold_coil_lever::operated(0.3f) == doctest::Approx(1.f));
CHECK(hold_coil_lever::operated(0.7f) == doctest::Approx(0.f));
}

View File

@@ -249,6 +249,7 @@ TGauge::Load_mapping( cParser &Input, TGauge::scratch_data &Scratchpad ) {
gaugetype == "delayed" ? push_delayed :
gaugetype == "pushtoggle" ? pushtoggle :
toggle; // "toggle" and default
m_typedeclared = true;
}
else if( key == "soundinc:" ) {
m_soundfxincrease.deserialize( Input, sound_type::single );

View File

@@ -58,6 +58,9 @@ public:
// returns offset of submodel associated with the button from the model centre
glm::vec3 model_offset() const;
TGaugeType type() const;
// whether the definition of the control declared its type, rather than left it at the default
bool type_declared() const {
return m_typedeclared; }
inline
bool is_push() const {
return ( static_cast<int>( type() ) & static_cast<int>( TGaugeType::push ) ) != 0; }
@@ -87,6 +90,7 @@ private:
// members
TGaugeAnimation m_animation { TGaugeAnimation::gt_Unknown }; // typ ruchu
TGaugeType m_type { TGaugeType::toggle }; // switch type
bool m_typedeclared { false }; // the type was given with type: in the definition
float m_friction { 0.f }; // hamowanie przy zliżaniu się do zadanej wartości
float m_targetvalue { 0.f }; // wartość docelowa
float m_value { 0.f }; // wartość obecna

View File

@@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "vehicle/Train.h"
#include "vehicle/horn.h"
#include "vehicle/holdcoillever.h"
#include "utilities/Globals.h"
#include "simulation/simulation.h"
@@ -3646,6 +3647,15 @@ void TTrain::OnCommand_pantographcompressoractivate(TTrain *Train, command_data
}
}
namespace
{
// whether the cab's main_off_bt is the bistable lever of the line breaker's hold coil (eu07/maszyna#364)
bool is_hold_coil_lever(TGauge const &Button)
{
return Button.SubModel != nullptr && hold_coil_lever::is_bistable(Button.type_declared(), Button.type() == TGaugeType::toggle);
}
} // namespace
void TTrain::OnCommand_linebreakertoggle(TTrain *Train, command_data const &Command)
{
@@ -3655,6 +3665,11 @@ void TTrain::OnCommand_linebreakertoggle(TTrain *Train, command_data const &Comm
if (Train->m_linebreakerstate == 0)
{
// ...to close the circuit
if (is_hold_coil_lever(Train->ggMainOffButton) && hold_coil_lever::cuts_off(Train->ggMainOffButton.GetDesiredValue()))
{
// the one key works both controls: the hold coil lever goes back on first, or the line breaker couldn't close
Train->ggMainOffButton.UpdateValue(0.0, Train->dsbSwitch);
}
// NOTE: bit of a dirty shortcut here
OnCommand_linebreakerclose(Train, Command);
}
@@ -3694,7 +3709,18 @@ void TTrain::OnCommand_linebreakeropen(TTrain *Train, command_data const &Comman
if (Command.action == GLFW_PRESS)
{
// visual feedback
if (Train->ggMainOffButton.SubModel != nullptr)
if (is_hold_coil_lever(Train->ggMainOffButton))
{
// the bistable lever goes over to its other position and stays there
auto const position{hold_coil_lever::operated(Train->ggMainOffButton.GetDesiredValue())};
Train->ggMainOffButton.UpdateValue(position, Train->dsbSwitch);
if (false == hold_coil_lever::cuts_off(position))
{
// the hold coil is fed again, which by itself doesn't close the line breaker
return;
}
}
else if (Train->ggMainOffButton.SubModel != nullptr)
{
Train->ggMainOffButton.UpdateValue(1.0, Train->dsbSwitch);
}
@@ -3731,8 +3757,9 @@ void TTrain::OnCommand_linebreakeropen(TTrain *Train, command_data const &Comman
{
Train->ggMainOnButton.UpdateValue(0.0, Train->dsbSwitch);
}
if (Train->ggMainOffButton.SubModel != nullptr)
if (Train->ggMainOffButton.SubModel != nullptr && false == is_hold_coil_lever(Train->ggMainOffButton))
{
// (the bistable hold coil lever stays where it was put)
Train->ggMainOffButton.UpdateValue(0.0, Train->dsbSwitch);
}
// and the two-state switch too, for good measure
@@ -8365,6 +8392,20 @@ bool TTrain::Update(double const Deltatime)
// because the state of the line breaker isn't changed to match, we need to do it here manually
m_linebreakerstate = 0;
}
if (is_hold_coil_lever(ggMainOffButton) && hold_coil_lever::cuts_off(ggMainOffButton.GetDesiredValue()))
{
if (true == mvControlled->Mains)
{
// the line breaker was closed with the hold coil lever off, by the AI or through the control cable:
// put the lever back on to match, like the line breaker state above
ggMainOffButton.UpdateValue(0.0);
}
else if (m_linebreakerstate == 2)
{
// the hold coil was cut off while the closing button was held, the line breaker won't close on its release
m_linebreakerstate = 0;
}
}
if ((ggMainButton.SubModel != nullptr && ggMainButton.GetDesiredValue() > 0.95) ||
ggMainOnButton.SubModel != nullptr && ggMainOnButton.GetDesiredValue() > 0.95 ||

34
vehicle/holdcoillever.h Normal file
View File

@@ -0,0 +1,34 @@
/*
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/.
*/
#pragma once
// the lever of the line breaker's hold coil (eu07/maszyna#364), as on the ET22 and the EP09: the cab's main_off_bt
// control declared with "type: toggle" stays in the position it's put in. Position 0 keeps the hold coil fed, so the
// line breaker can be closed; position 1 cuts the coil off, which opens the line breaker and keeps it from closing
// until the lever is put back. Without the declared type main_off_bt stays the push button it always was, which
// opens the line breaker and springs back.
namespace hold_coil_lever {
// whether the main_off_bt control is the bistable lever, from the type its definition declares, if it declares one
inline bool is_bistable(bool const Typedeclared, bool const Toggletype) {
return Typedeclared && Toggletype;
}
// whether the lever in the given position cuts off the hold coil
inline bool cuts_off(float const Position) {
return Position > 0.5f;
}
// the position the lever goes to when it's operated: the other one
inline float operated(float const Position) {
return cuts_off(Position) ? 0.f : 1.f;
}
} // namespace hold_coil_lever