16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-23 16:49:18 +02:00

build 210424. soundproofing enhancement, battery sound, heating command propagation, memory cell command retrieval logic enhancement, coupling mode logic enhancement, motor overload relay threshold cab control animation fix, cab control fallback sound activation fixes,

This commit is contained in:
tmj-fstate
2021-04-24 19:37:16 +02:00
parent 66bd63336b
commit 3775d20e0c
17 changed files with 276 additions and 112 deletions

View File

@@ -1676,14 +1676,19 @@ TController::TableUpdateEvent( double &Velocity, TCommandType &Command, TSpeedPo
else if( Point.evEvent->is_command() ) { else if( Point.evEvent->is_command() ) {
// jeśli prędkość jest zerowa, a komórka zawiera komendę // jeśli prędkość jest zerowa, a komórka zawiera komendę
eSignNext = Point.evEvent; // dla informacji eSignNext = Point.evEvent; // dla informacji
if( true == TestFlag( iDrivigFlags, moveStopHere ) ) { // make sure the command isn't for someone else
// jeśli ma stać, dostaje komendę od razu // TBD, TODO: revise this check for bank/loose_shunt; we might want to ignore obstacles with no owner in these modes
Command = TCommandType::cm_Command; // komenda z komórki, do wykonania po zatrzymaniu if( Point.fDist < Obstacle.distance ) {
} if( true == TestFlag( iDrivigFlags, moveStopHere ) ) {
else if( Point.fDist <= fMaxProximityDist ) { // jeśli ma stać, dostaje komendę od razu
// jeśli ma dociągnąć, to niech dociąga Command = TCommandType::cm_Command; // komenda z komórki, do wykonania po zatrzymaniu
// (moveStopCloser dotyczy dociągania do W4, nie semafora) }
Command = TCommandType::cm_Command; // komenda z komórki, do wykonania po zatrzymaniu // NOTE: human drivers are likely to stop wherever and expect things to still work, so we give them more leeway
else if( Point.fDist <= ( AIControllFlag ? fMaxProximityDist : std::max( 250.0, fMaxProximityDist ) ) ) {
// jeśli ma dociągnąć, to niech dociąga
// (moveStopCloser dotyczy dociągania do W4, nie semafora)
Command = TCommandType::cm_Command; // komenda z komórki, do wykonania po zatrzymaniu
}
} }
} }
} }
@@ -1928,9 +1933,13 @@ TController::~TController()
// zamiana kodu rozkazu na opis // zamiana kodu rozkazu na opis
std::string TController::Order2Str(TOrders Order) const { std::string TController::Order2Str(TOrders Order) const {
if( Order & Change_direction ) { if( ( ( Order & Change_direction ) != 0 )
&& ( Order != Change_direction ) ) {
// może być nałożona na inną i wtedy ma priorytet // może być nałożona na inną i wtedy ma priorytet
return "Change_direction"; return
Order2Str( Change_direction )
+ " + "
+ Order2Str( static_cast<TOrders>( Order & ~Change_direction ) );
} }
switch( Order ) { switch( Order ) {
case Wait_for_orders: return "Wait_for_orders"; case Wait_for_orders: return "Wait_for_orders";
@@ -1953,11 +1962,15 @@ std::array<char, 64> orderbuffer;
std::string TController::OrderCurrent() const std::string TController::OrderCurrent() const
{ // pobranie aktualnego rozkazu celem wyświetlenia { // pobranie aktualnego rozkazu celem wyświetlenia
auto const order { OrderCurrentGet() }; auto const order { OrderCurrentGet() };
// generally prioritize direction change...
if( order & Change_direction ) { if( order & Change_direction ) {
return locale::strings[ locale::string::driver_scenario_changedirection ]; // ...but not in the middle of coupling operation
if( false == TestFlag( iDrivigFlags, moveConnect ) ) {
return locale::strings[ locale::string::driver_scenario_changedirection ];
}
} }
switch( OrderList[ OrderPos ] ) { switch( static_cast<TOrders>( order & ~Change_direction ) ) {
case Wait_for_orders: { return locale::strings[ locale::string::driver_scenario_waitfororders ]; } case Wait_for_orders: { return locale::strings[ locale::string::driver_scenario_waitfororders ]; }
case Prepare_engine: { return locale::strings[ locale::string::driver_scenario_prepareengine ]; } case Prepare_engine: { return locale::strings[ locale::string::driver_scenario_prepareengine ]; }
case Release_engine: { return locale::strings[ locale::string::driver_scenario_releaseengine ]; } case Release_engine: { return locale::strings[ locale::string::driver_scenario_releaseengine ]; }
@@ -4927,17 +4940,20 @@ TController::PrepareDirection() {
locale::string::driver_hint_directionbackward ); locale::string::driver_hint_directionbackward );
} }
void TController::JumpToNextOrder() void TController::JumpToNextOrder( bool const Ignoremergedchangedirection )
{ // wykonanie kolejnej komendy z tablicy rozkazów { // wykonanie kolejnej komendy z tablicy rozkazów
if (OrderList[OrderPos] != Wait_for_orders) if (OrderList[OrderPos] != Wait_for_orders)
{ {
if (OrderList[OrderPos] & Change_direction) // jeśli zmiana kierunku if( ( ( OrderList[ OrderPos ] & Change_direction ) != 0 ) // jeśli zmiana kierunku
if (OrderList[OrderPos] != Change_direction) // ale nałożona na coś && ( OrderList[ OrderPos ] != Change_direction ) ) { // ale nałożona na coś
{
OrderList[OrderPos] = TOrders(OrderList[OrderPos] & ~Change_direction); // usunięcie zmiany kierunku z innej komendy if( false == Ignoremergedchangedirection ) {
// usunięcie zmiany kierunku z innej komendy
OrderList[ OrderPos ] = TOrders( OrderList[ OrderPos ] & ~Change_direction );
OrderCheck(); OrderCheck();
return; return;
} }
}
if (OrderPos < maxorders - 1) if (OrderPos < maxorders - 1)
++OrderPos; ++OrderPos;
else else
@@ -6701,9 +6717,9 @@ TController::UpdateConnect() {
// podłączanie do składu // podłączanie do składu
if (iDrivigFlags & moveConnect) { if (iDrivigFlags & moveConnect) {
// sprzęgi sprawdzamy w pierwszej kolejności, bo jak połączony, to koniec // sprzęgi sprawdzamy w pierwszej kolejności, bo jak połączony, to koniec
auto *vehicle { pVehicles[ end::front ] }; auto *vehicle { iCouplingVehicle.value().first };
auto const couplingend { iCouplingVehicle.value().second };
auto *vehicleparameters { vehicle->MoverParameters }; auto *vehicleparameters { vehicle->MoverParameters };
auto const couplingend { ( vehicle->DirectionGet() > 0 ? end::front : end::rear ) };
if( vehicleparameters->Couplers[ couplingend ].CouplingFlag != iCoupler ) { if( vehicleparameters->Couplers[ couplingend ].CouplingFlag != iCoupler ) {
auto const &neighbour { vehicleparameters->Neighbours[ couplingend ] }; auto const &neighbour { vehicleparameters->Neighbours[ couplingend ] };
if( neighbour.vehicle != nullptr ) { if( neighbour.vehicle != nullptr ) {
@@ -6736,14 +6752,24 @@ TController::UpdateConnect() {
CheckVehicles( Connect ); // sprawdzić światła nowego składu CheckVehicles( Connect ); // sprawdzić światła nowego składu
iCoupler = 0; // dalsza jazda manewrowa już bez łączenia iCoupler = 0; // dalsza jazda manewrowa już bez łączenia
iCouplingVehicle.reset();
iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania
JumpToNextOrder(); // wykonanie następnej komendy JumpToNextOrder( true ); // wykonanie następnej komendy
} }
} // moveConnect } // moveConnect
else { else {
// początek podczepiania, z wyłączeniem sprawdzania fTrackBlock
if( Obstacle.distance <= 20.0 ) { if( Obstacle.distance <= 20.0 ) {
// początek podczepiania, z wyłączeniem sprawdzania fTrackBlock if( !iCouplingVehicle ) {
iDrivigFlags |= moveConnect; // write down which vehicle should be coupled with the target consist,
// so we don't lose track of it if the user does something unexpected
iCouplingVehicle = {
pVehicles[ end::front ],
( pVehicles[ end::front ]->DirectionGet() > 0 ?
end::front :
end::rear ) };
iDrivigFlags |= moveConnect;
}
} }
} }
} }
@@ -6885,9 +6911,7 @@ TController::UpdateDisconnect() {
// 5th stage: clean up and move on to next order // 5th stage: clean up and move on to next order
if( iDirection == iDirectionOrder ) { if( iDirection == iDirectionOrder ) {
iDrivigFlags &= ~movePress; // koniec dociskania iDrivigFlags &= ~movePress; // koniec dociskania
while( ( OrderCurrentGet() & Disconnect ) != 0 ) { JumpToNextOrder( true ); // zmieni światła
JumpToNextOrder(); // zmieni światła
}
TableClear(); // skanowanie od nowa TableClear(); // skanowanie od nowa
iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem
} }
@@ -6957,6 +6981,11 @@ TController::handle_orders() {
break; break;
} }
default: { default: {
// special case, change_direction potentially received during partially completed coupling operation
// this is unlikely to happen for the AI, but can happen for a (slower) human user
if( TestFlag( iDrivigFlags, moveConnect ) ) {
UpdateConnect();
}
break; break;
} }
} // switch OrderList[OrderPos] } // switch OrderList[OrderPos]
@@ -7949,27 +7978,31 @@ TController::check_route_ahead( double const Range ) {
void void
TController::check_route_behind( double const Range ) { TController::check_route_behind( double const Range ) {
if( VelNext != 0.0 ) { return; } if( VelNext != 0.0 ) { return; } // not if we can move ahead
if( TestFlag( iDrivigFlags, moveConnect ) ) { return; } // not if we're in middle of coupling operation
if( !( OrderCurrentGet() & ~( Shunt | Loose_shunt | Connect ) ) ) { if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Connect ) ) == 0 ) {
// jedzie w Shunt albo Connect, albo Wait_for_orders // jedzie w Shunt albo Connect, albo Wait_for_orders
// w trybie Connect skanować do tyłu tylko jeśli przed kolejnym sygnałem nie ma taboru do podłączenia // w trybie Connect skanować do tyłu tylko jeśli przed kolejnym sygnałem nie ma taboru do podłączenia
if( ( ( OrderCurrentGet() & Connect ) == 0 ) auto const couplinginprogress {
|| ( Obstacle.distance > std::min( 2000.0, FirstSemaphorDist ) ) ) { ( TestFlag( OrderCurrentGet(), Connect ) )
auto const comm { BackwardScan( Range ) }; && ( ( Obstacle.distance < std::min( 2000.0, FirstSemaphorDist ) )
if( comm != TCommandType::cm_Unknown ) { || ( TestFlag( iDrivigFlags, moveConnect ) ) ) };
// jeśli w drugą można jechać if( couplinginprogress ) { return; }
// należy sprawdzać odległość od znalezionego sygnalizatora,
// aby w przypadku prędkości 0.1 wyciągnąć najpierw skład za sygnalizator auto const comm { BackwardScan( Range ) };
// i dopiero wtedy zmienić kierunek jazdy, oczekując podania prędkości >0.5 if( comm != TCommandType::cm_Unknown ) {
if( comm == TCommandType::cm_Command ) { // jeśli w drugą można jechać
// jeśli komenda Shunt to ją odbierz bez przemieszczania się (np. odczep wagony po dopchnięciu do końca toru) // należy sprawdzać odległość od znalezionego sygnalizatora,
iDrivigFlags |= moveStopHere; // aby w przypadku prędkości 0.1 wyciągnąć najpierw skład za sygnalizator
} // i dopiero wtedy zmienić kierunek jazdy, oczekując podania prędkości >0.5
iDirectionOrder = -iDirection; // zmiana kierunku jazdy if( comm == TCommandType::cm_Command ) {
// zmiana kierunku bez psucia kolejnych komend // jeśli komenda Shunt to ją odbierz bez przemieszczania się (np. odczep wagony po dopchnięciu do końca toru)
OrderList[ OrderPos ] = TOrders( OrderCurrentGet() | Change_direction ); iDrivigFlags |= moveStopHere;
} }
iDirectionOrder = -iDirection; // zmiana kierunku jazdy
// zmiana kierunku bez psucia kolejnych komend
OrderList[ OrderPos ] = TOrders( OrderCurrentGet() | Change_direction );
} }
} }
} }

View File

@@ -383,6 +383,7 @@ private:
int iDirection = 0; // kierunek jazdy względem sprzęgów pojazdu, w którym siedzi AI (1=przód,-1=tył) int iDirection = 0; // kierunek jazdy względem sprzęgów pojazdu, w którym siedzi AI (1=przód,-1=tył)
int iDirectionOrder = 0; //żadany kierunek jazdy (służy do zmiany kierunku) int iDirectionOrder = 0; //żadany kierunek jazdy (służy do zmiany kierunku)
std::optional<int> iDirectionBackup; // consist direction to be restored after coupling/uncoupling and similar direction-changing operations std::optional<int> iDirectionBackup; // consist direction to be restored after coupling/uncoupling and similar direction-changing operations
std::optional<std::pair<TDynamicObject*,int>> iCouplingVehicle; // <vehicle, coupler> to be coupled with another consist in current coupling operation
int iVehicleCount = -2; // wartość neutralna // ilość pojazdów do odłączenia albo zabrania ze składu (-1=wszystkie) int iVehicleCount = -2; // wartość neutralna // ilość pojazdów do odłączenia albo zabrania ze składu (-1=wszystkie)
int iCoupler = 0; // maska sprzęgu, jaką należy użyć przy łączeniu (po osiągnięciu trybu Connect), 0 gdy jazda bez łączenia int iCoupler = 0; // maska sprzęgu, jaką należy użyć przy łączeniu (po osiągnięciu trybu Connect), 0 gdy jazda bez łączenia
int iDriverFailCount = 0; // licznik błędów AI int iDriverFailCount = 0; // licznik błędów AI
@@ -446,7 +447,7 @@ public:
std::string OrderCurrent() const; std::string OrderCurrent() const;
private: private:
void RecognizeCommand(); // odczytuje komende przekazana lokomotywie void RecognizeCommand(); // odczytuje komende przekazana lokomotywie
void JumpToNextOrder(); void JumpToNextOrder( bool const Skipmergedchangedirection = false );
void JumpToFirstOrder(); void JumpToFirstOrder();
void OrderPush( TOrders NewOrder ); void OrderPush( TOrders NewOrder );
void OrderNext( TOrders NewOrder ); void OrderNext( TOrders NewOrder );

View File

@@ -4122,6 +4122,14 @@ void TDynamicObject::RenderSounds() {
sHeater.stop(); sHeater.stop();
} }
// battery sound
if( MoverParameters->Battery ) {
m_batterysound.play( sound_flags::exclusive | sound_flags::looping );
}
else {
m_batterysound.stop();
}
// brake system and braking sounds: // brake system and braking sounds:
// brake cylinder piston // brake cylinder piston
@@ -5763,6 +5771,12 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
sHeater.owner( this ); sHeater.owner( this );
} }
else if( token == "battery:" ) {
// train heating device
m_batterysound.deserialize( parser, sound_type::single );
m_batterysound.owner( this );
}
else if( token == "turbo:" ) { else if( token == "turbo:" ) {
// pliki z turbogeneratorem // pliki z turbogeneratorem
m_powertrainsounds.engine_turbo.deserialize( parser, sound_type::multipart, sound_parameters::range ); m_powertrainsounds.engine_turbo.deserialize( parser, sound_type::multipart, sound_parameters::range );
@@ -6484,7 +6498,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
// other engine compartment sounds // other engine compartment sounds
auto const nullvector { glm::vec3() }; auto const nullvector { glm::vec3() };
std::vector<sound_source *> enginesounds = { std::vector<sound_source *> enginesounds = {
&sConverter, &sCompressor, &sCompressorIdle, &sSmallCompressor, &sHeater &sConverter, &sCompressor, &sCompressorIdle, &sSmallCompressor, &sHeater, &m_batterysound
}; };
for( auto sound : enginesounds ) { for( auto sound : enginesounds ) {
if( sound->offset() == nullvector ) { if( sound->offset() == nullvector ) {

View File

@@ -480,6 +480,7 @@ private:
sound_source sCompressorIdle { sound_placement::engine }; sound_source sCompressorIdle { sound_placement::engine };
sound_source sSmallCompressor { sound_placement::engine }; sound_source sSmallCompressor { sound_placement::engine };
sound_source sHeater { sound_placement::engine }; sound_source sHeater { sound_placement::engine };
sound_source m_batterysound { sound_placement::engine };
// braking sounds // braking sounds
sound_source dsbPneumaticRelay { sound_placement::external }; sound_source dsbPneumaticRelay { sound_placement::external };
sound_source rsBrake { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // moved from cab sound_source rsBrake { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // moved from cab

View File

@@ -538,10 +538,10 @@ updatevalues_event::run_() {
auto const location { targetcell->location() }; auto const location { targetcell->location() };
for( auto vehicle : targetcell->Track->Dynamics ) { for( auto vehicle : targetcell->Track->Dynamics ) {
if( vehicle->Mechanik ) { if( vehicle->Mechanik ) {
WriteLog( " Vehicle: [" + vehicle->name() + "]" );
targetcell->PutCommand( targetcell->PutCommand(
vehicle->Mechanik, vehicle->Mechanik,
&location ); &location );
WriteLog( " Vehicle: [" + vehicle->name() + "]" );
} }
} }
} }
@@ -775,10 +775,9 @@ putvalues_event::run_() {
m_input.location.z, m_input.location.z,
m_input.location.y }; m_input.location.y };
std::string vehiclename;
if( m_activator->Mechanik ) { if( m_activator->Mechanik ) {
// przekazanie rozkazu do AI // przekazanie rozkazu do AI
vehiclename = m_activator->Mechanik->Vehicle()->name(); WriteLog( " Vehicle: [" + m_activator->Mechanik->Vehicle()->name() + "]" );
m_activator->Mechanik->PutCommand( m_activator->Mechanik->PutCommand(
m_input.data_text, m_input.data_text,
m_input.data_value_1, m_input.data_value_1,
@@ -790,7 +789,7 @@ putvalues_event::run_() {
// send the command to consist owner, // send the command to consist owner,
// we're acting on presumption there's hardly ever need to issue command to unmanned vehicle // we're acting on presumption there's hardly ever need to issue command to unmanned vehicle
// and the intended recipient moved between vehicles after the event was queued // and the intended recipient moved between vehicles after the event was queued
vehiclename = m_activator->ctOwner->Vehicle()->name(); WriteLog( " Vehicle: [" + m_activator->ctOwner->Vehicle()->name() + "]" );
m_activator->ctOwner->PutCommand( m_activator->ctOwner->PutCommand(
m_input.data_text, m_input.data_text,
m_input.data_value_1, m_input.data_value_1,
@@ -799,15 +798,13 @@ putvalues_event::run_() {
} }
else { else {
// przekazanie do pojazdu // przekazanie do pojazdu
vehiclename = m_activator->name(); WriteLog( " Vehicle: [" + m_activator->name() +"]" );
m_activator->MoverParameters->PutCommand( m_activator->MoverParameters->PutCommand(
m_input.data_text, m_input.data_text,
m_input.data_value_1, m_input.data_value_1,
m_input.data_value_2, m_input.data_value_2,
loc ); loc );
} }
WriteLog( " Vehicle: [" + vehiclename + "]" );
} }
// export_as_text() subclass details // export_as_text() subclass details
@@ -944,10 +941,10 @@ copyvalues_event::run_() {
auto const location { targetcell->location() }; auto const location { targetcell->location() };
for( auto vehicle : targetcell->Track->Dynamics ) { for( auto vehicle : targetcell->Track->Dynamics ) {
if( vehicle->Mechanik ) { if( vehicle->Mechanik ) {
WriteLog( " Vehicle: [" + vehicle->name() + "]" );
targetcell->PutCommand( targetcell->PutCommand(
vehicle->Mechanik, vehicle->Mechanik,
&location ); &location );
WriteLog( " Vehicle: [" + vehicle->name() + "]" );
} }
} }
} }

View File

@@ -141,8 +141,32 @@ void TGauge::Load( cParser &Parser, TDynamicObject const *Owner, double const mu
>> endscale; >> endscale;
} }
// new, variable length section // new, variable length section
while( true == Load_mapping( Parser ) ) { {
; // all work done by while() scratch_data scratchpad;
while( true == Load_mapping( Parser, scratchpad ) ) {
; // all work done by while()
}
// post-deserialization cleanup
// set provided custom soundproofing to assigned sounds (for sounds without their own custom soundproofing)
if( scratchpad.soundproofing ) {
if( !m_soundfxincrease.soundproofing() ) {
m_soundfxincrease.soundproofing() = scratchpad.soundproofing;
}
if( !m_soundfxdecrease.soundproofing() ) {
m_soundfxdecrease.soundproofing() = scratchpad.soundproofing;
}
if( !m_soundfxon.soundproofing() ) {
m_soundfxon.soundproofing() = scratchpad.soundproofing;
}
if( !m_soundfxoff.soundproofing() ) {
m_soundfxoff.soundproofing() = scratchpad.soundproofing;
}
for( auto &soundfxrecord : m_soundfxvalues ) {
if( !soundfxrecord.second.soundproofing() ) {
soundfxrecord.second.soundproofing() = scratchpad.soundproofing;
}
}
}
} }
} }
@@ -207,7 +231,7 @@ void TGauge::Load( cParser &Parser, TDynamicObject const *Owner, double const mu
}; };
bool bool
TGauge::Load_mapping( cParser &Input ) { TGauge::Load_mapping( cParser &Input, TGauge::scratch_data &Scratchpad ) {
// token can be a key or block end // token can be a key or block end
auto const key { Input.getToken<std::string>( true, "\n\r\t ,;" ) }; auto const key { Input.getToken<std::string>( true, "\n\r\t ,;" ) };
@@ -236,8 +260,21 @@ TGauge::Load_mapping( cParser &Input ) {
else if( key == "soundoff:" ) { else if( key == "soundoff:" ) {
m_soundfxoff.deserialize( Input, sound_type::single ); m_soundfxoff.deserialize( Input, sound_type::single );
} }
else if( key.compare( 0, std::min<std::size_t>( key.size(), 5 ), "sound" ) == 0 ) { else if( key == "soundproofing:" ) {
// sounds assigned to specific gauge values, defined by key soundFoo: where Foo = value // custom soundproofing in format [ p1, p2, p3, p4, p5, p6 ]
Input.getTokens( 6, false, "\n\r\t ,;[]" );
std::array<float, 6> soundproofing;
Input
>> soundproofing[ 0 ]
>> soundproofing[ 1 ]
>> soundproofing[ 2 ]
>> soundproofing[ 3 ]
>> soundproofing[ 4 ]
>> soundproofing[ 5 ];
Scratchpad.soundproofing = soundproofing;
}
else if( starts_with( key, "sound" ) ) {
// sounds assigned to specific gauge values, defined by key soundX: where X = value
auto const indexstart { key.find_first_of( "-1234567890" ) }; auto const indexstart { key.find_first_of( "-1234567890" ) };
auto const indexend { key.find_first_not_of( "-1234567890", indexstart ) }; auto const indexend { key.find_first_not_of( "-1234567890", indexstart ) };
if( indexstart != std::string::npos ) { if( indexstart != std::string::npos ) {
@@ -290,13 +327,19 @@ TGauge::UpdateValue( float fNewDesired, sound_source *Fallbacksound ) {
// HACK: crude way to discern controls with continuous and quantized value range // HACK: crude way to discern controls with continuous and quantized value range
if( currentvalue < fNewDesired ) { if( currentvalue < fNewDesired ) {
// shift up // shift up
m_soundfxincrease.play( m_soundtype ); if( false == m_soundfxincrease.empty() ) {
m_soundfxincrease.play( m_soundtype );
return;
}
} }
else if( currentvalue > fNewDesired ) { else if( currentvalue > fNewDesired ) {
// shift down // shift down
m_soundfxdecrease.play( m_soundtype ); if( false == m_soundfxdecrease.empty() ) {
m_soundfxdecrease.play( m_soundtype );
return;
}
} }
else if( Fallbacksound != nullptr ) { if( Fallbacksound != nullptr ) {
// ...and if that fails too, try the provided fallback sound from legacy system // ...and if that fails too, try the provided fallback sound from legacy system
Fallbacksound->play( m_soundtype ); Fallbacksound->play( m_soundtype );
} }

View File

@@ -71,10 +71,14 @@ public:
*SubModelOn { nullptr }; // optional submodel visible when the state input is set *SubModelOn { nullptr }; // optional submodel visible when the state input is set
private: private:
// types
struct scratch_data {
std::optional< std::array<float, 6> > soundproofing;
};
// methods // methods
// imports member data pair from the config file // imports member data pair from the config file
bool bool
Load_mapping( cParser &Input ); Load_mapping( cParser &Input, TGauge::scratch_data &Scratchpad );
void void
UpdateValue( float fNewDesired, sound_source *Fallbacksound ); UpdateValue( float fNewDesired, sound_source *Fallbacksound );
float float

View File

@@ -1046,7 +1046,7 @@ public:
double EngineHeatingRPM { 0.0 }; // guaranteed engine revolutions with heating enabled double EngineHeatingRPM { 0.0 }; // guaranteed engine revolutions with heating enabled
double LightPower = 0.0; /*moc pobierana na ogrzewanie/oswietlenie*/ double LightPower = 0.0; /*moc pobierana na ogrzewanie/oswietlenie*/
double BatteryVoltage = 0.0; /*Winger - baterie w elektrykach*/ double BatteryVoltage = 0.0; /*Winger - baterie w elektrykach*/
bool Battery = false; /*Czy sa zalavzone baterie*/ bool Battery = false; /*Czy sa zalaczone baterie*/
start_t BatteryStart = start_t::manual; start_t BatteryStart = start_t::manual;
bool EpFuse = true; /*Czy sa zalavzone baterie*/ bool EpFuse = true; /*Czy sa zalavzone baterie*/
bool Signalling = false; /*Czy jest zalaczona sygnalizacja hamowania ostatniego wagonu*/ bool Signalling = false; /*Czy jest zalaczona sygnalizacja hamowania ostatniego wagonu*/
@@ -1805,8 +1805,10 @@ public:
bool ConverterSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl przetwornicy*/ bool ConverterSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl przetwornicy*/
bool CompressorSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl sprezarki*/ bool CompressorSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl sprezarki*/
bool ChangeCompressorPreset( int const Change, range_t const Notify = range_t::consist ); bool ChangeCompressorPreset( int const Change, range_t const Notify = range_t::consist );
bool HeatingSwitch( bool const State, range_t const Notify = range_t::consist );
void HeatingSwitch_( bool const State );
/*-funkcje typowe dla lokomotywy elektrycznej*/ /*-funkcje typowe dla lokomotywy elektrycznej*/
void LowVoltagePowerCheck( double const Deltatime ); void LowVoltagePowerCheck( double const Deltatime );
void MainsCheck( double const Deltatime ); void MainsCheck( double const Deltatime );
void PowerCouplersCheck( double const Deltatime, coupling const Coupling ); void PowerCouplersCheck( double const Deltatime, coupling const Coupling );

View File

@@ -647,10 +647,12 @@ bool TMoverParameters::ChangeCab(int direction)
// rozruch wysoki (true) albo niski (false) // rozruch wysoki (true) albo niski (false)
bool bool
TMoverParameters::CurrentSwitch(bool const State) { TMoverParameters::CurrentSwitch(bool const State) {
// Ra: przeniosłem z Train.cpp, nie wiem czy ma to sens
if (MaxCurrentSwitch(State)) { if( MaxCurrentSwitch( State ) ) {
if (TrainType != dt_EZT) if( TrainType != dt_EZT ) {
return (MinCurrentSwitch(State)); ( MinCurrentSwitch( State ) );
}
return true;
} }
// TBD, TODO: split off shunt mode toggle into a separate command? It doesn't make much sense to have these two together like that // TBD, TODO: split off shunt mode toggle into a separate command? It doesn't make much sense to have these two together like that
// dla 2Ls150 // dla 2Ls150
@@ -3641,6 +3643,33 @@ bool TMoverParameters::ChangeCompressorPreset( int const State, range_t const No
return ( CompressorListPos != initialstate ); return ( CompressorListPos != initialstate );
} }
bool TMoverParameters::HeatingSwitch( bool const State, range_t const Notify ) {
bool const initialstate { HeatingAllow };
HeatingSwitch_( State );
if( Notify != range_t::local ) {
// pass the command to other vehicles
// TBD: pass the requested state, or the actual state?
SendCtrlToNext(
"HeatingSwitch",
( State ? 1 : 0 ),
CabActive,
( Notify == range_t::unit ?
coupling::control | coupling::permanent :
coupling::control ) );
}
return ( HeatingAllow != initialstate );
}
void TMoverParameters::HeatingSwitch_( bool const State ) {
// TBD, TODO: activation dependencies?
HeatingAllow = State;
}
// ************************************************************************************************* // *************************************************************************************************
// Q: 20160711 // Q: 20160711
// zwiększenie nastawy hamulca // zwiększenie nastawy hamulca
@@ -6550,12 +6579,14 @@ bool TMoverParameters::MaxCurrentSwitch(bool State)
if (State && (Imax == ImaxLo) && (RList[MainCtrlPos].Bn < 2) && if (State && (Imax == ImaxLo) && (RList[MainCtrlPos].Bn < 2) &&
!((TrainType == dt_ET42) && (MainCtrlPos > 0))) !((TrainType == dt_ET42) && (MainCtrlPos > 0)))
{ {
Imax = ImaxHi; Imax = ImaxHi;
MCS = true; MCS = true;
if (CabActive != 0) if (CabActive != 0)
SendCtrlToNext("MaxCurrentSwitch", 1, CabActive); SendCtrlToNext("MaxCurrentSwitch", 1, CabActive);
} }
if (!State) if (!State)
if (Imax == ImaxHi) if (Imax == ImaxHi)
if (!((TrainType == dt_ET42) && (MainCtrlPos > 0))) if (!((TrainType == dt_ET42) && (MainCtrlPos > 0)))
{ {
@@ -11771,6 +11802,11 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C
MainSwitch_( CValue1 > 0.0 ); MainSwitch_( CValue1 > 0.0 );
OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype );
} }
else if (Command == "HeatingSwitch")
{
HeatingSwitch_( CValue1 > 0.0 );
OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype );
}
else if (Command == "Direction") else if (Command == "Direction")
{ {
DirActive = static_cast<int>(floor(CValue1)); DirActive = static_cast<int>(floor(CValue1));

View File

@@ -4859,7 +4859,7 @@ void TTrain::OnCommand_heatingenable( TTrain *Train, command_data const &Command
if( Command.action == GLFW_PRESS ) { if( Command.action == GLFW_PRESS ) {
Train->mvControlled->HeatingAllow = true; Train->mvOccupied->HeatingSwitch( true );
// visual feedback // visual feedback
Train->ggTrainHeatingButton.UpdateValue( 1.0, Train->dsbSwitch ); Train->ggTrainHeatingButton.UpdateValue( 1.0, Train->dsbSwitch );
} }
@@ -4869,7 +4869,7 @@ void TTrain::OnCommand_heatingdisable( TTrain *Train, command_data const &Comman
if( Command.action == GLFW_PRESS ) { if( Command.action == GLFW_PRESS ) {
Train->mvControlled->HeatingAllow = false; Train->mvOccupied->HeatingSwitch( false );
// visual feedback // visual feedback
Train->ggTrainHeatingButton.UpdateValue( Train->ggTrainHeatingButton.UpdateValue(
( Train->ggTrainHeatingButton.type() == TGaugeType::push ? ( Train->ggTrainHeatingButton.type() == TGaugeType::push ?
@@ -7101,7 +7101,7 @@ bool TTrain::Update( double const Deltatime )
} }
ggMainCtrl.Update(); ggMainCtrl.Update();
} }
if (ggMainCtrlAct.SubModel) if (ggMainCtrlAct.SubModel != nullptr )
{ {
if (mvControlled->CoupledCtrl) if (mvControlled->CoupledCtrl)
ggMainCtrlAct.UpdateValue( ggMainCtrlAct.UpdateValue(
@@ -7110,7 +7110,7 @@ bool TTrain::Update( double const Deltatime )
ggMainCtrlAct.UpdateValue(double(mvControlled->MainCtrlActualPos)); ggMainCtrlAct.UpdateValue(double(mvControlled->MainCtrlActualPos));
ggMainCtrlAct.Update(); ggMainCtrlAct.Update();
} }
if (ggScndCtrl.SubModel) { if (ggScndCtrl.SubModel != nullptr ) {
// Ra: od byte odejmowane boolean i konwertowane potem na double? // Ra: od byte odejmowane boolean i konwertowane potem na double?
ggScndCtrl.UpdateValue( ggScndCtrl.UpdateValue(
double( mvControlled->ScndCtrlPos double( mvControlled->ScndCtrlPos
@@ -7118,26 +7118,34 @@ bool TTrain::Update( double const Deltatime )
dsbNastawnikBocz ); dsbNastawnikBocz );
ggScndCtrl.Update(); ggScndCtrl.Update();
} }
if( ggScndCtrlButton.is_toggle() ) { if( ggScndCtrlButton.SubModel != nullptr ) {
ggScndCtrlButton.UpdateValue( if( ggScndCtrlButton.is_toggle() ) {
( ( mvControlled->ScndCtrlPos > 0 ) ? 1.f : 0.f ), ggScndCtrlButton.UpdateValue(
dsbSwitch ); ( ( mvControlled->ScndCtrlPos > 0 ) ? 1.f : 0.f ),
dsbSwitch );
}
ggScndCtrlButton.Update( lowvoltagepower );
} }
ggScndCtrlButton.Update( lowvoltagepower ); if( ggScndCtrlOffButton.SubModel != nullptr ) {
ggScndCtrlOffButton.Update( lowvoltagepower ); ggScndCtrlOffButton.Update( lowvoltagepower );
ggDistanceCounterButton.Update(); }
if (ggDirKey.SubModel) { if( ggDistanceCounterButton.SubModel != nullptr ) {
if (mvControlled->TrainType != dt_EZT) ggDistanceCounterButton.Update();
}
if (ggDirKey.SubModel != nullptr ) {
if( mvControlled->TrainType != dt_EZT ) {
ggDirKey.UpdateValue( ggDirKey.UpdateValue(
double(mvControlled->DirActive), double( mvControlled->DirActive ),
dsbReverserKey); dsbReverserKey );
else }
else {
ggDirKey.UpdateValue( ggDirKey.UpdateValue(
double(mvControlled->DirActive) + double(mvControlled->Imin == mvControlled->IminHi), double( mvControlled->DirActive ) + double( mvControlled->Imin == mvControlled->IminHi ),
dsbReverserKey); dsbReverserKey );
}
ggDirKey.Update(); ggDirKey.Update();
} }
if (ggBrakeCtrl.SubModel) if (ggBrakeCtrl.SubModel != nullptr )
{ {
#ifdef _WIN32 #ifdef _WIN32
if (DynamicObject->Mechanik ? if (DynamicObject->Mechanik ?
@@ -9437,7 +9445,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con
{ "universal7:", ggUniversals[ 7 ] }, { "universal7:", ggUniversals[ 7 ] },
{ "universal8:", ggUniversals[ 8 ] }, { "universal8:", ggUniversals[ 8 ] },
{ "universal9:", ggUniversals[ 9 ] } { "universal9:", ggUniversals[ 9 ] }
}; };
{ {
auto const lookup { gauges.find( Label ) }; auto const lookup { gauges.find( Label ) };
if( lookup != gauges.end() ) { if( lookup != gauges.end() ) {

View File

@@ -1147,24 +1147,24 @@ TController::cue_action( locale::string const Action, float const Actionparamete
// consist heating // consist heating
case locale::string::driver_hint_consistheatingon: { case locale::string::driver_hint_consistheatingon: {
if( AIControllFlag ) { if( AIControllFlag ) {
mvControlling->HeatingAllow = true; mvOccupied->HeatingSwitch( true );
} }
remove_hint( locale::string::driver_hint_consistheatingoff ); remove_hint( locale::string::driver_hint_consistheatingoff );
hint( hint(
Action, Action,
[this](float const Parameter) -> bool { [this](float const Parameter) -> bool {
return ( mvControlling->HeatingAllow == true ); } ); return ( mvOccupied->HeatingAllow == true ); } );
break; break;
} }
case locale::string::driver_hint_consistheatingoff: { case locale::string::driver_hint_consistheatingoff: {
if( AIControllFlag ) { if( AIControllFlag ) {
mvControlling->HeatingAllow = false; mvOccupied->HeatingSwitch( false );
} }
remove_hint( locale::string::driver_hint_consistheatingon ); remove_hint( locale::string::driver_hint_consistheatingon );
hint( hint(
Action, Action,
[this](float const Parameter) -> bool { [this](float const Parameter) -> bool {
return ( mvControlling->HeatingAllow == false ); } ); return ( mvOccupied->HeatingAllow == false ); } );
break; break;
} }

View File

@@ -1088,7 +1088,7 @@ debug_panel::update_section_ai( std::vector<text_line> &Output ) {
// biezaca komenda dla AI // biezaca komenda dla AI
auto textline = auto textline =
"Current order: [" + std::to_string( mechanik.OrderPos ) + "] " "Current order: [" + std::to_string( mechanik.OrderPos ) + "] "
+ mechanik.OrderCurrent(); + mechanik.Order2Str( mechanik.OrderCurrentGet() );
if( mechanik.fStopTime < 0.0 ) { if( mechanik.fStopTime < 0.0 ) {
textline += "\n stop time: " + to_string( std::abs( mechanik.fStopTime ), 1 ); textline += "\n stop time: " + to_string( std::abs( mechanik.fStopTime ), 1 );

View File

@@ -171,10 +171,23 @@ sound_source::deserialize_mapping( cParser &Input ) {
else if( key == "soundend:" ) { else if( key == "soundend:" ) {
sound( sound_id::end ).buffer = audio::renderer.fetch_buffer( deserialize_random_set( Input, "\n\r\t ,;" ) ); sound( sound_id::end ).buffer = audio::renderer.fetch_buffer( deserialize_random_set( Input, "\n\r\t ,;" ) );
} }
else if( key.compare( 0, std::min<std::size_t>( key.size(), 5 ), "sound" ) == 0 ) { else if( key == "soundproofing:" ) {
// custom soundproofing in format [ p1, p2, p3, p4, p5, p6 ]
Input.getTokens( 6, false, "\n\r\t ,;[]" );
std::array<float, 6> soundproofing;
Input
>> soundproofing[ 0 ]
>> soundproofing[ 1 ]
>> soundproofing[ 2 ]
>> soundproofing[ 3 ]
>> soundproofing[ 4 ]
>> soundproofing[ 5 ];
m_soundproofing = soundproofing;
}
else if( starts_with( key, "sound" ) ) {
// sound chunks, defined with key soundX where X = activation threshold // sound chunks, defined with key soundX where X = activation threshold
auto const indexstart { key.find_first_of( "1234567890" ) }; auto const indexstart { key.find_first_of( "-1234567890" ) };
auto const indexend { key.find_first_not_of( "1234567890", indexstart ) }; auto const indexend { key.find_first_not_of( "-1234567890", indexstart ) };
if( indexstart != std::string::npos ) { if( indexstart != std::string::npos ) {
// NOTE: we'll sort the chunks at the end of deserialization // NOTE: we'll sort the chunks at the end of deserialization
m_soundchunks.emplace_back( m_soundchunks.emplace_back(
@@ -202,10 +215,10 @@ sound_source::deserialize_mapping( cParser &Input ) {
Input.getToken<float>( false, "\n\r\t ,;" ), Input.getToken<float>( false, "\n\r\t ,;" ),
0.0f, 1.0f ); 0.0f, 1.0f );
} }
else if( key.compare( 0, std::min<std::size_t>( key.size(), 5 ), "pitch" ) == 0 ) { else if( starts_with( key, "pitch" ) ) {
// sound chunk pitch, defined with key pitchX where X = activation threshold // sound chunk pitch, defined with key pitchX where X = activation threshold
auto const indexstart { key.find_first_of( "1234567890" ) }; auto const indexstart { key.find_first_of( "-1234567890" ) };
auto const indexend { key.find_first_not_of( "1234567890", indexstart ) }; auto const indexend { key.find_first_not_of( "-1234567890", indexstart ) };
if( indexstart != std::string::npos ) { if( indexstart != std::string::npos ) {
auto const index { std::stoi( key.substr( indexstart, indexend - indexstart ) ) }; auto const index { std::stoi( key.substr( indexstart, indexend - indexstart ) ) };
auto const pitch { Input.getToken<float>( false, "\n\r\t ,;" ) }; auto const pitch { Input.getToken<float>( false, "\n\r\t ,;" ) };
@@ -962,12 +975,14 @@ sound_source::update_soundproofing() {
auto const isambient { ( ( m_placement == sound_placement::external ) && ( m_owner == nullptr ) && ( m_range < -1 ) ? 1 : 0 ) }; auto const isambient { ( ( m_placement == sound_placement::external ) && ( m_owner == nullptr ) && ( m_range < -1 ) ? 1 : 0 ) };
auto const placement { ( isambient ? sound_placement::external_ambient : m_placement ) }; auto const placement { ( isambient ? sound_placement::external_ambient : m_placement ) };
if( m_owner != nullptr ) { if( m_owner != nullptr ) {
m_properties.soundproofing = auto const listenerlocation { (
m_owner->soundproofing( m_owner != listenervehicle ?
static_cast<int>( placement ), 4 : // part of two-stage calculation owner->outside->listener, or single stage owner->outside one
( m_owner != listenervehicle ? occupiedcab ) };
4 : // part of two-stage calculation owner->outside->listener, or single stage owner->outside one m_properties.soundproofing = (
occupiedcab ) ); m_soundproofing ? // custom soundproofing has higher priority than that of the owner
m_soundproofing.value()[ listenerlocation + 1 ] : // cab indices start from -1 so we have to account for this
m_owner->soundproofing( static_cast<int>( placement ), listenerlocation ) );
} }
if( ( listenervehicle ) && ( listenervehicle != m_owner ) ) { if( ( listenervehicle ) && ( listenervehicle != m_owner ) ) {
// if the listener is located in another vehicle, calculate additional proofing of the sound coming from outside // if the listener is located in another vehicle, calculate additional proofing of the sound coming from outside

10
sound.h
View File

@@ -116,6 +116,11 @@ public:
start( float const Offset ); start( float const Offset );
float const & float const &
start() const; start() const;
// custom soundproofing setter/getter
auto &
soundproofing();
auto const &
soundproofing() const;
// returns true if there isn't any sound buffer associated with the object, false otherwise // returns true if there isn't any sound buffer associated with the object, false otherwise
bool bool
empty() const; empty() const;
@@ -231,6 +236,7 @@ private:
std::vector<soundchunk_pair> m_soundchunks; // table of samples activated when associated variable is within certain range std::vector<soundchunk_pair> m_soundchunks; // table of samples activated when associated variable is within certain range
bool m_soundchunksempty { true }; // helper, cached check whether sample table is linked with any actual samples bool m_soundchunksempty { true }; // helper, cached check whether sample table is linked with any actual samples
int m_crossfaderange {}; // range of transition from one chunk to another int m_crossfaderange {}; // range of transition from one chunk to another
std::optional< std::array<float, 6> > m_soundproofing; // custom soundproofing parameters
}; };
// owner setter/getter // owner setter/getter
@@ -245,6 +251,10 @@ inline std::string const & sound_source::name() const { return m_name; }
// playback starting point shift setter/getter // playback starting point shift setter/getter
inline void sound_source::start( float const Offset ) { m_startoffset = Offset; } inline void sound_source::start( float const Offset ) { m_startoffset = Offset; }
inline float const & sound_source::start() const { return m_startoffset; } inline float const & sound_source::start() const { return m_startoffset; }
// custom soundproofing setter/getter
inline auto & sound_source::soundproofing() { return m_soundproofing; }
inline auto const & sound_source::soundproofing() const { return m_soundproofing; }
// collection of sound sources present in the scene // collection of sound sources present in the scene
class sound_table : public basic_table<sound_source> { class sound_table : public basic_table<sound_source> {

View File

@@ -393,15 +393,15 @@ init() {
"Wylaczyc przetwornice", "Wylaczyc przetwornice",
"Odblokowac przekaznik nadmiarowy przetwornicy", "Odblokowac przekaznik nadmiarowy przetwornicy",
"Odblokowac przekaznik roznicowy obwodu glownego", "Odblokowac przekaznik roznicowy obwodu glownego",
"Odblokowac przekaznik nadmiarowy motorow trakcyjnych", "Odblokowac przekaznik nadmiarowy silnikow trakcyjnych",
"Zamknac wylacznik szybki", "Zamknac wylacznik szybki",
"Otworzyc wylacznik szybki", "Otworzyc wylacznik szybki",
"Zalaczyc sprezarke", "Zalaczyc sprezarke",
"Wylaczyc sprezarke", "Wylaczyc sprezarke",
"Zalaczyc wentylatory przednich motorow trakcyjnych", "Zalaczyc wentylatory przednich silnikow trakcyjnych",
"Wylaczyc wentylatory przednich motorow trakcyjnych", "Wylaczyc wentylatory przednich silnikow trakcyjnych",
"Zalaczyc wentylatory tylnych motorow trakcyjnych", "Zalaczyc wentylatory tylnych silnikow trakcyjnych",
"Wylaczyc wentylatory tylnych motorow trakcyjnych", "Wylaczyc wentylatory tylnych silnikow trakcyjnych",
"Zalaczyc hamulec sprezynowy", "Zalaczyc hamulec sprezynowy",
"Zwolnic hamules sprezynowy", "Zwolnic hamules sprezynowy",
"Zalaczyc hamulec reczny", "Zalaczyc hamulec reczny",

View File

@@ -77,7 +77,7 @@ ui_layer::init( GLFWwindow *Window ) {
static ImWchar const glyphranges[] = { static ImWchar const glyphranges[] = {
0x0020, 0x00FF, // ascii + extension 0x0020, 0x00FF, // ascii + extension
0x0100, 0x017F, // latin extended-a // 0x0100, 0x017F, // latin extended-a
0x2070, 0x2079, // superscript 0x2070, 0x2079, // superscript
0x2500, 0x256C, // box drawings 0x2500, 0x256C, // box drawings
0, 0,

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#define VERSION_MAJOR 21 #define VERSION_MAJOR 21
#define VERSION_MINOR 408 #define VERSION_MINOR 424
#define VERSION_REVISION 0 #define VERSION_REVISION 0