diff --git a/Driver.cpp b/Driver.cpp index b4236a09..c2d633cb 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -182,9 +182,9 @@ TSpeedPos::TSpeedPos(TTrack *track, double dist, int flag) Set(track, dist, flag); }; -TSpeedPos::TSpeedPos(basic_event *event, double dist, TOrders order) +TSpeedPos::TSpeedPos(basic_event *event, double dist, double length, TOrders order) { - Set(event, dist, order); + Set(event, dist, length, order); }; void TSpeedPos::Clear() @@ -366,13 +366,20 @@ bool TSpeedPos::IsProperSemaphor(TOrders order) return false; // true gdy zatrzymanie, wtedy nie ma po co skanować dalej } -bool TSpeedPos::Set(basic_event *event, double dist, TOrders order) +bool TSpeedPos::Set(basic_event *event, double dist, double length, TOrders order) { // zapamiętanie zdarzenia fDist = dist; - iFlags = spEnabled | spEvent; // event+istotny + iFlags = spEvent; evEvent = event; vPos = event->input_location(); // współrzędne eventu albo komórki pamięci (zrzutować na tor?) - CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości + if( dist + length >= 0 ) { + iFlags |= spEnabled; + CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości + } + else { + // located behind the tracking consist, don't bother with it + return false; + } // zależnie od trybu sprawdzenie czy jest tutaj gdzieś semafor lub tarcza manewrowa // jeśli wskazuje stop wtedy wystawiamy true jako koniec sprawdzania // WriteLog("EventSet: Vel=" + AnsiString(fVelNext) + " iFlags=" + AnsiString(iFlags) + " order="+AnsiString(order)); @@ -582,6 +589,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) if( newspeedpoint.Set( pEvent, GetDistanceToEvent( pTrack, pEvent, fLastDir, fCurrentDistance ), + fLength, OrderCurrentGet() ) ) { fDistance = newspeedpoint.fDist; // jeśli sygnał stop, to nie ma potrzeby dalej skanować @@ -1911,72 +1919,83 @@ void TController::AutoRewident() mvOccupied->BrakeOpModeFlag = i; } } + // teraz zerujemy tabelkę opóźnienia hamowania for (int i = 0; i < BrakeAccTableSize; ++i) { fBrake_a0[i+1] = 0; fBrake_a1[i+1] = 0; } - // 4. Przeliczanie siły hamowania - double const velstep = ( mvOccupied->Vmax*0.5 ) / BrakeAccTableSize; - d = pVehicles[0]; // pojazd na czele składu - while (d) { - for( int i = 0; i < BrakeAccTableSize; ++i ) { - fBrake_a0[ i + 1 ] += d->MoverParameters->BrakeForceR( 0.25, velstep*( 1 + 2 * i ) ); - fBrake_a1[ i + 1 ] += d->MoverParameters->BrakeForceR( 1.00, velstep*( 1 + 2 * i ) ); - } - d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) - } - for (int i = 0; i < BrakeAccTableSize; ++i) - { - fBrake_a1[i+1] -= fBrake_a0[i+1]; - fBrake_a0[i+1] /= fMass; - fBrake_a0[i + 1] += 0.001*velstep*(1 + 2 * i); - fBrake_a1[i+1] /= (12*fMass); - } - IsCargoTrain = ( mvOccupied->CategoryFlag == 1 ) && ( ( mvOccupied->BrakeDelayFlag & bdelay_G ) != 0 ); - IsHeavyCargoTrain = ( true == IsCargoTrain ) && ( fBrake_a0[ 1 ] > 0.4 ); - - BrakingInitialLevel = ( - IsHeavyCargoTrain ? 1.25 : - IsCargoTrain ? 1.25 : - 1.00 ); - - BrakingLevelIncrease = ( - IsHeavyCargoTrain ? 0.25 : - IsCargoTrain ? 0.25 : - 0.25 ); - - if( mvOccupied->TrainType == dt_EZT ) { - if( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) { - // HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors - fNominalAccThreshold = std::max( -0.60, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + if( OrderCurrentGet() & Shunt ) { + // for uniform behaviour and compatibility with older scenarios set default acceleration table values for shunting + fAccThreshold = ( + mvOccupied->TrainType == dt_EZT ? -0.55 : + mvOccupied->TrainType == dt_DMU ? -0.45 : + -0.2 ); + // HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors + if( ( mvOccupied->TrainType == dt_EZT ) + && ( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) ) { + fAccThreshold += 0.10; } - else { - fNominalAccThreshold = std::max( -0.75, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); - } - fBrakeReaction = 0.25; - } - else if( mvOccupied->TrainType == dt_DMU ) { - fNominalAccThreshold = std::max( -0.45, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); - fBrakeReaction = 0.25; } - else if (ustaw > 16) { - fNominalAccThreshold = -fBrake_a0[ BrakeAccTableSize ] - 4 * fBrake_a1[ BrakeAccTableSize ]; - fBrakeReaction = 1.00 + fLength*0.004; - } - else { - fNominalAccThreshold = -fBrake_a0[ BrakeAccTableSize ] - 1 * fBrake_a1[ BrakeAccTableSize ]; - fBrakeReaction = 1.00 + fLength*0.005; - } - fAccThreshold = fNominalAccThreshold; -/* - if( IsHeavyCargoTrain ) { - // HACK: heavy cargo trains don't activate brakes early enough - fAccThreshold = std::max( -0.2, fAccThreshold ); + + if( OrderCurrentGet() & Obey_train ) { + // 4. Przeliczanie siły hamowania + double const velstep = ( mvOccupied->Vmax*0.5 ) / BrakeAccTableSize; + d = pVehicles[0]; // pojazd na czele składu + while (d) { + for( int i = 0; i < BrakeAccTableSize; ++i ) { + fBrake_a0[ i + 1 ] += d->MoverParameters->BrakeForceR( 0.25, velstep*( 1 + 2 * i ) ); + fBrake_a1[ i + 1 ] += d->MoverParameters->BrakeForceR( 1.00, velstep*( 1 + 2 * i ) ); + } + d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) + } + for (int i = 0; i < BrakeAccTableSize; ++i) + { + fBrake_a1[i+1] -= fBrake_a0[i+1]; + fBrake_a0[i+1] /= fMass; + fBrake_a0[i + 1] += 0.001*velstep*(1 + 2 * i); + fBrake_a1[i+1] /= (12*fMass); + } + + IsCargoTrain = ( mvOccupied->CategoryFlag == 1 ) && ( ( mvOccupied->BrakeDelayFlag & bdelay_G ) != 0 ); + IsHeavyCargoTrain = ( true == IsCargoTrain ) && ( fBrake_a0[ 1 ] > 0.4 ); + + BrakingInitialLevel = ( + IsHeavyCargoTrain ? 1.25 : + IsCargoTrain ? 1.25 : + 1.00 ); + + BrakingLevelIncrease = ( + IsHeavyCargoTrain ? 0.25 : + IsCargoTrain ? 0.25 : + 0.25 ); + + if( mvOccupied->TrainType == dt_EZT ) { + if( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) { + // HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors + fNominalAccThreshold = std::max( -0.60, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + } + else { + fNominalAccThreshold = std::max( -0.75, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + } + fBrakeReaction = 0.25; + } + else if( mvOccupied->TrainType == dt_DMU ) { + fNominalAccThreshold = std::max( -0.45, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + fBrakeReaction = 0.25; + } + else if (ustaw > 16) { + fNominalAccThreshold = -fBrake_a0[ BrakeAccTableSize ] - 4 * fBrake_a1[ BrakeAccTableSize ]; + fBrakeReaction = 1.00 + fLength*0.004; + } + else { + fNominalAccThreshold = -fBrake_a0[ BrakeAccTableSize ] - 1 * fBrake_a1[ BrakeAccTableSize ]; + fBrakeReaction = 1.00 + fLength*0.005; + } + fAccThreshold = fNominalAccThreshold; } -*/ } double TController::ESMVelocity(bool Main) @@ -2854,7 +2873,11 @@ bool TController::IncSpeed() auto const sufficienttractionforce { std::abs( mvControlling->Ft ) > ( IsHeavyCargoTrain ? 125 : 100 ) * 1000.0 }; auto const seriesmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn == 1 ) }; auto const parallelmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 ) }; - auto const useseriesmodevoltage { mvControlling->EnginePowerSource.CollectorParameters.MaxV * ( IsHeavyCargoTrain ? 0.70 : 0.80 ) }; + auto const useseriesmodevoltage { + interpolate( + mvControlling->EnginePowerSource.CollectorParameters.MinV, + mvControlling->EnginePowerSource.CollectorParameters.MaxV, + ( IsHeavyCargoTrain ? 0.35 : 0.40 ) ) }; auto const useseriesmode = ( ( mvControlling->Imax > mvControlling->ImaxLo ) || ( fVoltage < useseriesmodevoltage ) @@ -2885,7 +2908,7 @@ bool TController::IncSpeed() if( usefieldshunting ) { // to dać bocznik // engage the shuntfield only if there's sufficient power margin to draw from - auto const sufficientpowermargin { fVoltage - useseriesmodevoltage > ( IsHeavyCargoTrain ? 100.0 : 50.0 ) }; + auto const sufficientpowermargin { fVoltage - useseriesmodevoltage > ( IsHeavyCargoTrain ? 100.0 : 75.0 ) }; OK = ( sufficientpowermargin ? @@ -2898,7 +2921,14 @@ bool TController::IncSpeed() mvControlling->DecScndCtrl( 2 ); } // kręcimy nastawnik jazdy - auto const sufficientpowermargin { fVoltage - useseriesmodevoltage > ( IsHeavyCargoTrain ? 80.0 : 40.0 ) }; + // don't draw too much power; + // keep from dropping into series mode when entering/using parallel mode, and from shutting down in the series mode + auto const sufficientpowermargin { + fVoltage - ( + mvControlling->RList[ std::min( mvControlling->MainCtrlPos + 1, mvControlling->MainCtrlPosNo ) ].Bn == 1 ? + mvControlling->EnginePowerSource.CollectorParameters.MinV : + useseriesmodevoltage ) + > ( IsHeavyCargoTrain ? 80.0 : 60.0 ) }; OK = ( ( sufficientpowermargin && ( false == mvControlling->DelayCtrlFlag ) ) ? @@ -3909,12 +3939,13 @@ TController::UpdateSituation(double dt) { p = p->Next(); // pojazd podłączony z tyłu (patrząc od czoła) } - // crude way to deal with automatic door opening on W4 preventing further ride + // HACK: crude way to deal with automatic door opening on W4 preventing further ride // for human-controlled vehicles with no door control and dynamic brake auto-activating with door open + // TODO: check if this situation still happens and the hack is still needed if( ( false == AIControllFlag ) && ( iDrivigFlags & moveDoorOpened ) && ( mvOccupied->DoorCloseCtrl != control_t::driver ) - && ( mvControlling->MainCtrlPos > 0 ) ) { + && ( mvControlling->MainCtrlPos > ( mvControlling->EngineType != TEngineType::DieselEngine ? 0 : 1 ) ) ) { // for diesel 1st position is effectively 0 Doors( false ); } @@ -4013,7 +4044,12 @@ TController::UpdateSituation(double dt) { } } - auto const useseriesmodevoltage { mvControlling->EnginePowerSource.CollectorParameters.MaxV * ( IsHeavyCargoTrain ? 0.70 : 0.80 ) }; + // TODO: refactor this calculation into a subroutine + auto const useseriesmodevoltage { + interpolate( + mvControlling->EnginePowerSource.CollectorParameters.MinV, + mvControlling->EnginePowerSource.CollectorParameters.MaxV, + ( IsHeavyCargoTrain ? 0.35 : 0.40 ) ) }; if( fVoltage <= useseriesmodevoltage ) { // if the power station is heavily burdened try to reduce the load @@ -5865,12 +5901,20 @@ basic_event * TController::CheckTrackEventBackward(double fDirection, TTrack *Tr { // sprawdzanie eventu w torze, czy jest sygnałowym - skanowanie do tyłu // NOTE: this method returns only one event which meets the conditions, due to limitations in the caller // TBD, TODO: clean up the caller and return all suitable events, as in theory things will go awry if the track has more than one signal + auto const dir{ pVehicles[ 0 ]->VectorFront() * pVehicles[ 0 ]->DirectionGet() }; + auto const pos{ pVehicles[ 0 ]->HeadPosition() }; auto const &eventsequence { ( fDirection > 0 ? Track->m_events2 : Track->m_events1 ) }; for( auto const &event : eventsequence ) { if( ( event.second != nullptr ) && ( event.second->m_passive ) && ( typeid(*(event.second)) == typeid( getvalues_event ) ) ) { - return event.second; + // since we're checking for events behind us discard the sources in front of the scanning vehicle + auto const sl{ event.second->input_location() }; // położenie komórki pamięci + auto const sem{ sl - pos }; // wektor do komórki pamięci od końca składu + if( dir.x * sem.x + dir.z * sem.z < 0 ) { + // iloczyn skalarny jest ujemny, gdy sygnał stoi z tyłu + return event.second; + } } } return nullptr; diff --git a/Driver.h b/Driver.h index 9140a899..e6fa6125 100644 --- a/Driver.h +++ b/Driver.h @@ -141,7 +141,7 @@ class TSpeedPos public: TSpeedPos(TTrack *track, double dist, int flag); - TSpeedPos(basic_event *event, double dist, TOrders order); + TSpeedPos(basic_event *event, double dist, double length, TOrders order); TSpeedPos() = default; void Clear(); bool Update(); @@ -150,7 +150,7 @@ class TSpeedPos void UpdateDistance( double dist ) { fDist -= dist; } - bool Set(basic_event *e, double d, TOrders order = Wait_for_orders); + bool Set(basic_event *e, double d, double length, TOrders order = Wait_for_orders); void Set(TTrack *t, double d, int f); std::string TableText() const; std::string GetName() const; diff --git a/DynObj.cpp b/DynObj.cpp index bbc68141..5d56d0d3 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2782,7 +2782,7 @@ TDynamicObject::update_load_visibility() { } */ auto loadpercentage { ( - MoverParameters->MaxLoad == 0.0 ? + MoverParameters->MaxLoad == 0.f ? 0.0 : 100.0 * MoverParameters->LoadAmount / MoverParameters->MaxLoad ) }; auto const sectionloadpercentage { ( @@ -2816,7 +2816,7 @@ TDynamicObject::update_load_offset() { if( MoverParameters->LoadType.offset_min == 0.f ) { return; } auto const loadpercentage { ( - MoverParameters->MaxLoad == 0.0 ? + MoverParameters->MaxLoad == 0.f ? 0.0 : 100.0 * MoverParameters->LoadAmount / MoverParameters->MaxLoad ) }; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index d0918c15..9fbe5b3d 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -812,10 +812,13 @@ void TMoverParameters::UpdateBatteryVoltage(double dt) // HACK: allow to draw power also from adjacent converter, applicable for EMUs // TODO: expand power cables system to include low voltage power transfers + // HACK: emulate low voltage generator powered directly by the diesel engine auto const converteractive{ ( ( ConverterFlag ) || ( ( ( Couplers[ side::front ].CouplingFlag & coupling::permanent ) != 0 ) && Couplers[ side::front ].Connected->ConverterFlag ) - || ( ( ( Couplers[ side::rear ].CouplingFlag & coupling::permanent ) != 0 ) && Couplers[ side::rear ].Connected->ConverterFlag ) ) }; + || ( ( ( Couplers[ side::rear ].CouplingFlag & coupling::permanent ) != 0 ) && Couplers[ side::rear ].Connected->ConverterFlag ) ) + || ( ( EngineType == TEngineType::DieselElectric ) && ( true == Mains ) ) + || ( ( EngineType == TEngineType::DieselEngine ) && ( true == Mains ) ) }; if ((NominalBatteryVoltage / BatteryVoltage < 1.22) && Battery) { // 110V @@ -1529,18 +1532,18 @@ void TMoverParameters::WaterPumpCheck( double const Timestep ) { // water heater status check void TMoverParameters::WaterHeaterCheck( double const Timestep ) { - WaterHeater.is_damaged = ( - ( true == WaterHeater.is_damaged ) - || ( ( true == WaterHeater.is_active ) - && ( false == WaterPump.is_active ) ) ); - WaterHeater.is_active = ( ( false == WaterHeater.is_damaged ) && ( true == Battery ) && ( true == WaterHeater.is_enabled ) && ( true == WaterHeater.breaker ) && ( ( WaterHeater.is_active ) || ( WaterHeater.config.temp_min < 0 ) || ( dizel_heat.temperatura1 < WaterHeater.config.temp_min ) ) ); - + + WaterHeater.is_damaged = ( + ( true == WaterHeater.is_damaged ) + || ( ( true == WaterHeater.is_active ) + && ( false == WaterPump.is_active ) ) ); + if( ( WaterHeater.config.temp_max > 0 ) && ( dizel_heat.temperatura1 > WaterHeater.config.temp_max ) ) { WaterHeater.is_active = false; @@ -6664,7 +6667,7 @@ TMoverParameters::AssignLoad( std::string const &Name, float const Amount ) { for( auto const &loadattributes : LoadAttributes ) { if( Name == loadattributes.name ) { LoadType = loadattributes; - LoadAmount = Amount; + LoadAmount = clamp( Amount, 0.f, MaxLoad ) ; return true; } } @@ -6702,7 +6705,7 @@ bool TMoverParameters::LoadingDone(double const LSpeed, std::string const &Loadn if( ( LoadAmount <= 0 ) || ( CommandIn.Value1 <= 0 ) ) { // pusto lub rozładowano żądaną ilość LoadStatus = 4; // skończony rozładunek - LoadAmount = std::max( 0.f, LoadAmount ); //ładunek nie może być ujemny + LoadAmount = clamp( LoadAmount, 0.f, MaxLoad); //ładunek nie może być ujemny } if( LoadAmount == 0.f ) { AssignLoad(""); // jak nic nie ma, to nie ma też nazwy diff --git a/Train.cpp b/Train.cpp index 3fe0e1d4..2a9e8a92 100644 --- a/Train.cpp +++ b/Train.cpp @@ -448,9 +448,9 @@ PyObject *TTrain::GetTrainState() { // basic systems state data PyDict_SetItemString( dict, "battery", PyGetBool( mvControlled->Battery ) ); PyDict_SetItemString( dict, "linebreaker", PyGetBool( mvControlled->Mains ) ); - PyDict_SetItemString( dict, "converter", PyGetBool( mover->ConverterFlag ) ); - PyDict_SetItemString( dict, "converter_overload", PyGetBool( mover->ConvOvldFlag ) ); - PyDict_SetItemString( dict, "compress", PyGetBool( mover->CompressorFlag ) ); + PyDict_SetItemString( dict, "converter", PyGetBool( mvControlled->ConverterFlag ) ); + PyDict_SetItemString( dict, "converter_overload", PyGetBool( mvControlled->ConvOvldFlag ) ); + PyDict_SetItemString( dict, "compress", PyGetBool( mvControlled->CompressorFlag ) ); // reverser PyDict_SetItemString( dict, "direction", PyGetInt( mover->ActiveDir ) ); // throttle @@ -463,34 +463,35 @@ PyObject *TTrain::GetTrainState() { bool const bEP = ( mvControlled->LocHandle->GetCP() > 0.2 ) || ( fEIMParams[ 0 ][ 2 ] > 0.01 ); PyDict_SetItemString( dict, "dir_brake", PyGetBool( bEP ) ); bool bPN; - if( ( typeid( *mvControlled->Hamulec ) == typeid( TLSt ) ) - || ( typeid( *mvControlled->Hamulec ) == typeid( TEStED ) ) ) { + if( ( typeid( *mvOccupied->Hamulec ) == typeid( TLSt ) ) + || ( typeid( *mvOccupied->Hamulec ) == typeid( TEStED ) ) ) { - TBrake* temp_ham = mvControlled->Hamulec.get(); + TBrake* temp_ham = mvOccupied->Hamulec.get(); bPN = ( static_cast( temp_ham )->GetEDBCP() > 0.2 ); } else bPN = false; PyDict_SetItemString( dict, "indir_brake", PyGetBool( bPN ) ); - PyDict_SetItemString( dict, "brake_delay_flag", PyGetInt( mvControlled->BrakeDelayFlag )); - PyDict_SetItemString( dict, "brake_op_mode_flag", PyGetInt( mvControlled->BrakeOpModeFlag )); + PyDict_SetItemString( dict, "brake_delay_flag", PyGetInt( mvOccupied->BrakeDelayFlag )); + PyDict_SetItemString( dict, "brake_op_mode_flag", PyGetInt( mvOccupied->BrakeOpModeFlag )); // other controls PyDict_SetItemString( dict, "ca", PyGetBool( TestFlag( mvOccupied->SecuritySystem.Status, s_aware ) ) ); PyDict_SetItemString( dict, "shp", PyGetBool( TestFlag( mvOccupied->SecuritySystem.Status, s_active ) ) ); PyDict_SetItemString( dict, "pantpress", PyGetFloat( mvControlled->PantPress ) ); PyDict_SetItemString( dict, "universal3", PyGetBool( InstrumentLightActive ) ); PyDict_SetItemString( dict, "radio_channel", PyGetInt( iRadioChannel ) ); + PyDict_SetItemString( dict, "door_lock", PyGetInt( mvOccupied->DoorLockEnabled ) ); // movement data PyDict_SetItemString( dict, "velocity", PyGetFloat( mover->Vel ) ); PyDict_SetItemString( dict, "tractionforce", PyGetFloat( mover->Ft ) ); PyDict_SetItemString( dict, "slipping_wheels", PyGetBool( mover->SlippingWheels ) ); PyDict_SetItemString( dict, "sanding", PyGetBool( mover->SandDose ) ); // electric current data - PyDict_SetItemString( dict, "traction_voltage", PyGetFloat( mover->RunningTraction.TractionVoltage ) ); - PyDict_SetItemString( dict, "voltage", PyGetFloat( mover->Voltage ) ); - PyDict_SetItemString( dict, "im", PyGetFloat( mover->Im ) ); - PyDict_SetItemString( dict, "fuse", PyGetBool( mover->FuseFlag ) ); - PyDict_SetItemString( dict, "epfuse", PyGetBool( mover->EpFuse ) ); + PyDict_SetItemString( dict, "traction_voltage", PyGetFloat( mvControlled->RunningTraction.TractionVoltage ) ); + PyDict_SetItemString( dict, "voltage", PyGetFloat( mvControlled->Voltage ) ); + PyDict_SetItemString( dict, "im", PyGetFloat( mvControlled->Im ) ); + PyDict_SetItemString( dict, "fuse", PyGetBool( mvControlled->FuseFlag ) ); + PyDict_SetItemString( dict, "epfuse", PyGetBool( mvOccupied->EpFuse ) ); // induction motor state data char const *TXTT[ 10 ] = { "fd", "fdt", "fdb", "pd", "pdt", "pdb", "itothv", "1", "2", "3" }; char const *TXTC[ 10 ] = { "fr", "frt", "frb", "pr", "prt", "prb", "im", "vm", "ihv", "uhv" }; @@ -575,6 +576,7 @@ PyObject *TTrain::GetTrainState() { PyDict_SetItemString( dict, "minutes", PyGetInt( simulation::Time.data().wMinute ) ); PyDict_SetItemString( dict, "seconds", PyGetInt( simulation::Time.second() ) ); PyDict_SetItemString( dict, "air_temperature", PyGetInt( Global.AirTemperature ) ); + PyDict_SetItemString( dict, "light_level", PyGetFloat( Global.fLuminance - std::max( 0.f, Global.Overcast - 1.f ) ) ); Application.release_python_lock(); return dict; @@ -1041,7 +1043,7 @@ void TTrain::OnCommand_trainbrakeincrease( TTrain *Train, command_data const &Co if( Command.action != GLFW_RELEASE ) { if( Train->mvOccupied->BrakeHandle == TBrakeHandle::FV4a ) { - Train->mvOccupied->BrakeLevelAdd( 0.1 /*15.0 * Command.time_delta*/ ); + Train->mvOccupied->BrakeLevelAdd( 0.1 * Global.fBrakeStep ); } else { Train->set_train_brake( Train->mvOccupied->BrakeCtrlPos + Global.fBrakeStep ); @@ -1054,7 +1056,7 @@ void TTrain::OnCommand_trainbrakedecrease( TTrain *Train, command_data const &Co if( Command.action != GLFW_RELEASE ) { // press or hold if( Train->mvOccupied->BrakeHandle == TBrakeHandle::FV4a ) { - Train->mvOccupied->BrakeLevelAdd( -0.1 /*-15.0 * Command.time_delta*/ ); + Train->mvOccupied->BrakeLevelAdd( -0.1 * Global.fBrakeStep ); } else { Train->set_train_brake( Train->mvOccupied->BrakeCtrlPos - Global.fBrakeStep ); @@ -4085,14 +4087,14 @@ void TTrain::OnCommand_generictoggle( TTrain *Train, command_data const &Command auto const itemindex = static_cast( Command.command ) - static_cast( user_command::generictoggle0 ); auto &item = Train->ggUniversals[ itemindex ]; - +/* if( item.SubModel == nullptr ) { if( Command.action == GLFW_PRESS ) { WriteLog( "Train generic item " + std::to_string( itemindex ) + " is missing, or wasn't defined" ); } return; } - +*/ if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down if( item.GetDesiredValue() < 0.5 ) { @@ -5475,6 +5477,10 @@ bool TTrain::Update( double const Deltatime ) // others btLampkaMalfunction.Turn( mvControlled->dizel_heat.PA ); btLampkaMotorBlowers.Turn( ( mvControlled->MotorBlowers[ side::front ].is_active ) && ( mvControlled->MotorBlowers[ side::rear ].is_active ) ); + // universal devices state indicators + for( auto idx = 0; idx < btUniversals.size(); ++idx ) { + btUniversals[ idx ].Turn( ggUniversals[ idx ].GetValue() > 0.5 ); + } } else { // wylaczone @@ -5530,6 +5536,10 @@ bool TTrain::Update( double const Deltatime ) // others btLampkaMalfunction.Turn( false ); btLampkaMotorBlowers.Turn( false ); + // universal devices state indicators + for( auto &universal : btUniversals ) { + universal.Turn( false ); + } } { // yB - wskazniki drugiego czlonu @@ -6184,7 +6194,9 @@ void TTrain::update_sounds_runningnoise( sound_source &Sound ) { // volume calculation auto volume = Sound.m_amplitudeoffset - + Sound.m_amplitudefactor * mvOccupied->Vel; + + Sound.m_amplitudefactor * interpolate( + 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 { ( @@ -6208,7 +6220,7 @@ void TTrain::update_sounds_runningnoise( sound_source &Sound ) { interpolate( 0.0, 1.0, clamp( - mvOccupied->Vel / 40.0, + mvOccupied->Vel / 25.0, 0.0, 1.0 ) ); } @@ -7075,6 +7087,9 @@ void TTrain::clear_cab_controls() btLampkaHamulecReczny.Clear(); btLampkaBlokadaDrzwi.Clear(); btLampkaDoorLockOff.Clear(); + for( auto &universal : btUniversals ) { + universal.Clear(); + } btInstrumentLight.Clear(); btDashboardLight.Clear(); btTimetableLight.Clear(); @@ -7507,7 +7522,17 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-rearrightend:", btLampkaRearRightEndLight }, { "i-dashboardlight:", btDashboardLight }, { "i-timetablelight:", btTimetableLight }, - { "i-cablight:", btCabLight } + { "i-cablight:", btCabLight }, + { "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 ] } }; auto lookup = lights.find( Label ); if( lookup != lights.end() ) { diff --git a/Train.h b/Train.h index 0279f257..a398ec20 100644 --- a/Train.h +++ b/Train.h @@ -505,6 +505,7 @@ public: // reszta może by?publiczna TButton btLampkaHamowanie2zes; TButton btLampkaOpory; TButton btLampkaWysRozr; + std::array btUniversals; // NOTE: temporary arrangement until we have dynamically built control table TButton btInstrumentLight; TButton btDashboardLight; TButton btTimetableLight; diff --git a/drivermode.cpp b/drivermode.cpp index 94029c02..6b4d576d 100644 --- a/drivermode.cpp +++ b/drivermode.cpp @@ -1133,6 +1133,7 @@ driver_mode::InOutKey() if( train == nullptr ) { FreeFlyModeFlag = true; // nadal poza kabiną + Camera.m_owner = nullptr; // detach camera from the vehicle return; } diff --git a/material.cpp b/material.cpp index 9ea3fbd9..91db805b 100644 --- a/material.cpp +++ b/material.cpp @@ -60,17 +60,19 @@ opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool c } else if( ( key == "texture1:" ) || ( key == "texture_diffuse:" ) ) { + auto const value { deserialize_random_set( Input ) }; if( ( texture1 == null_handle ) || ( Priority > priority1 ) ) { - texture1 = GfxRenderer.Fetch_Texture( deserialize_random_set( Input ), Loadnow ); + texture1 = GfxRenderer.Fetch_Texture( value, Loadnow ); priority1 = Priority; } } else if( ( key == "texture2:" ) || ( key == "texture_normalmap:" ) ) { + auto const value { deserialize_random_set( Input ) }; if( ( texture2 == null_handle ) || ( Priority > priority2 ) ) { - texture2 = GfxRenderer.Fetch_Texture( deserialize_random_set( Input ), Loadnow ); + texture2 = GfxRenderer.Fetch_Texture( value, Loadnow ); priority2 = Priority; } }