diff --git a/Button.cpp b/Button.cpp index 7257b5ef..c1cb3d48 100644 --- a/Button.cpp +++ b/Button.cpp @@ -123,12 +123,13 @@ TButton::Turn( bool const State ) { } } -void TButton::Update() { +void TButton::Update( bool const Power ) { - if( ( bData != nullptr ) - && ( *bData != m_state ) ) { + auto const state { Power && ( bData ? *bData : m_state ) }; - m_state = ( *bData ); + if( state != m_state ) { + + m_state = state; play(); } diff --git a/Button.h b/Button.h index 3340e37e..109e39f5 100644 --- a/Button.h +++ b/Button.h @@ -30,7 +30,7 @@ public: bool Active() { return ( ( pModelOn != nullptr ) || ( pModelOff != nullptr ) ); } - void Update(); + void Update( bool const Power = true ); bool Init( std::string const &asName, TModel3d const *pModel, bool bNewOn = false ); void Load( cParser &Parser, TDynamicObject const *Owner ); void AssignBool(bool const *bValue); diff --git a/Driver.cpp b/Driver.cpp index 4b67a56b..a108f827 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -16,6 +16,7 @@ http://mozilla.org/MPL/2.0/. #include "Driver.h" #include "Globals.h" +#include "translation.h" #include "Logs.h" #include "Train.h" #include "mtable.h" @@ -349,7 +350,7 @@ std::string TSpeedPos::TableText() const bool TSpeedPos::IsProperSemaphor(TOrders order) { // sprawdzenie czy semafor jest zgodny z trybem jazdy - if (order < 0x40) // Wait_for_orders, Prepare_engine, Change_direction, Connect, Disconnect, Shunt + if (order < Obey_train) // Wait_for_orders, Prepare_engine, Change_direction, Connect, Disconnect, Shunt { if (iFlags & (spSemaphor | spShuntSemaphor)) return true; @@ -370,18 +371,17 @@ bool TSpeedPos::Set(basic_event *event, double dist, double length, TOrders orde iFlags = spEvent; evEvent = event; vPos = event->input_location(); // współrzędne eventu albo komórki pamięci (zrzutować na tor?) - 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 + // ignore events located behind the consist, but with exception of stop points which may be needed to update freshly received timetable + if( ( dist + length < 0 ) + && ( event->input_command() != TCommandType::cm_PassengerStopPoint ) ) { return false; } + iFlags |= spEnabled; + CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości // 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)); - if (order < 0x40) // Wait_for_orders, Prepare_engine, Change_direction, Connect, Disconnect, Shunt + if (order < Obey_train) // Wait_for_orders, Prepare_engine, Change_direction, Connect, Disconnect, Shunt { if (iFlags & (spSemaphor | spShuntSemaphor) && fVelNext == 0.0) return true; @@ -484,6 +484,20 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) if (iTableDirection != iDirection ) { // jeśli zmiana kierunku, zaczynamy od toru ze wskazanym pojazdem + TableClear(); +/* + // aktualna prędkość // changed to -1 to recognize speed limit, if any + fLastVel = -1.0; + sSpeedTable.clear(); + iLast = -1; + tLast = nullptr; //żaden nie sprawdzony + SemNextIndex = -1; + SemNextStopIndex = -1; +*/ + if( VelSignalLast == 0.0 ) { + // don't allow potential red light overrun keep us from reversing + VelSignalLast = -1.0; + } iTableDirection = iDirection; // ustalenie w jakim kierunku jest wypełniana tabelka względem pojazdu pTrack = pVehicle->RaTrackGet(); // odcinek, na którym stoi fTrackLength = pVehicle->RaTranslationGet(); // pozycja na tym torze (odległość od Point1) @@ -501,17 +515,6 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) .Length(); // aktualna odległość ma być ujemna gdyż jesteśmy na końcu składu fCurrentDistance = -fLength - fTrackLength; - // aktualna prędkość // changed to -1 to recognize speed limit, if any - fLastVel = -1.0; - sSpeedTable.clear(); - iLast = -1; - tLast = nullptr; //żaden nie sprawdzony - SemNextIndex = -1; - SemNextStopIndex = -1; - if( VelSignalLast == 0.0 ) { - // don't allow potential red light overrun keep us from reversing - VelSignalLast = -1.0; - } fTrackLength = pTrack->Length(); //skasowanie zmian w zmiennej żeby poprawnie liczyło w dalszych krokach MoveDistanceReset(); // AI startuje 1s po zaczęciu jazdy i mógł już coś przejechać } @@ -537,7 +540,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) return; } else { - if( ( OrderCurrentGet() < 0x40 ) + if( ( OrderCurrentGet() < Obey_train ) && ( sSpeedTable[SemNextStopIndex].iFlags & ( spSemaphor | spShuntSemaphor | spOutsideStation ) ) ) { return; } @@ -757,7 +760,7 @@ void TController::TableCheck(double fDistance) { // przeliczenie odległości w tabelce, ewentualnie doskanowanie (bez analizy prędkości itp.) if( iTableDirection != iDirection ) { // jak zmiana kierunku, to skanujemy od końca składu - TableTraceRoute( fDistance, pVehicles[ 1 ] ); + TableTraceRoute( fDistance, pVehicles[ end::rear ] ); TableSort(); } else if (iTableDirection) @@ -784,7 +787,7 @@ void TController::TableCheck(double fDistance) --iLast; } tLast = sSpeedTable[ i ].trTrack; - TableTraceRoute( fDistance, pVehicles[ 1 ] ); + TableTraceRoute( fDistance, pVehicles[ end::rear ] ); TableSort(); // nie kontynuujemy pętli, trzeba doskanować ciąg dalszy break; @@ -822,7 +825,7 @@ void TController::TableCheck(double fDistance) sSpeedTable[iLast].Update(); // aktualizacja ostatniego // WriteLog("TableCheck: Upate last track. Dist=" + AnsiString(sSpeedTable[iLast].fDist)); if( sSpeedTable[ iLast ].fDist < fDistance ) { - TableTraceRoute( fDistance, pVehicles[ 1 ] ); // doskanowanie dalszego odcinka + TableTraceRoute( fDistance, pVehicles[ end::rear ] ); // doskanowanie dalszego odcinka TableSort(); } // garbage collection @@ -856,7 +859,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // jeśli przystanek, trzeba obsłużyć wg rozkładu iDrivigFlags |= moveStopPointFound; // stop points are irrelevant when not in one of the basic modes - if( ( OrderCurrentGet() & ( Obey_train | Shunt ) ) == 0 ) { continue; } + if( ( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) { continue; } // first 19 chars of the command is expected to be "PassengerStopPoint:" so we skip them if ( ToLower(sSpeedTable[i].evEvent->input_text()).compare( 19, sizeof(asNextStop), ToLower(asNextStop)) != 0 ) { // jeśli nazwa nie jest zgodna @@ -1008,7 +1011,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN } } - if (OrderCurrentGet() & Shunt) { + if (OrderCurrentGet() & ( Shunt | Loose_shunt )) { OrderNext(Obey_train); // uruchomić jazdę pociągową CheckVehicles(); // zmienić światła } @@ -1171,7 +1174,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( mvOccupied->Vel < 2.0 ) { // stanąć nie musi, ale zwolnić przynajmniej if( ( sSpeedTable[ i ].fDist < fMaxProximityDist ) - && ( TrackBlock() > 1000.0 ) ) { + && ( Obstacle.distance > 1000 ) ) { // jest w maksymalnym zasięgu to można go pominąć (wziąć drugą prędkosć) // as long as there isn't any obstacle in arbitrary view range eSignSkip = sSpeedTable[ i ].evEvent; @@ -1257,7 +1260,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN //sprawdzenie eventów pasywnych przed nami if( ( mvOccupied->CategoryFlag & 1 ) - && ( sSpeedTable[ i ].fDist > pVehicles[ 0 ]->fTrackBlock - 20.0 ) ) { + && ( sSpeedTable[ i ].fDist > Obstacle.distance - 20 ) ) { // jak sygnał jest dalej niż zawalidroga v = 0.0; // to może być podany dla tamtego: jechać tak, jakby tam stop był } @@ -1684,15 +1687,62 @@ std::string TController::Order2Str(TOrders Order) const { case Connect: return "Connect"; case Disconnect: return "Disconnect"; case Shunt: return "Shunt"; + case Loose_shunt: return "Loose_shunt"; case Obey_train: return "Obey_train"; + case Bank: return "Bank"; case Jump_to_first_order: return "Jump_to_first_order"; default: return "Undefined"; } } +std::array orderbuffer; + std::string TController::OrderCurrent() const { // pobranie aktualnego rozkazu celem wyświetlenia - return "[" + std::to_string(OrderPos) + "] " + Order2Str(OrderList[OrderPos]); + auto const order { OrderCurrentGet() }; + if( order & Change_direction ) { + return locale::strings[ locale::string::driver_scenario_changedirection ]; + } + + switch( OrderList[ OrderPos ] ) { + case Wait_for_orders: { return locale::strings[ locale::string::driver_scenario_waitfororders ]; } + case Prepare_engine: { return locale::strings[ locale::string::driver_scenario_prepareengine ]; } + case Release_engine: { return locale::strings[ locale::string::driver_scenario_releaseengine ]; } + case Change_direction: { return locale::strings[ locale::string::driver_scenario_changedirection ]; } + case Connect: { return locale::strings[ locale::string::driver_scenario_connect ]; } + case Disconnect: { + if( iVehicleCount < 0 ) { + // done with uncoupling, order should update shortly + return locale::strings[ locale::string::driver_scenario_waitfororders ]; + } + // try to provide some task details + auto const count { iVehicleCount }; + + if( iVehicleCount > 1 ) { + std::snprintf( + orderbuffer.data(), orderbuffer.size(), + ( iVehicleCount < 5 ? + locale::strings[ locale::string::driver_scenario_fewvehicles ].c_str() : // 2-4 + locale::strings[ locale::string::driver_scenario_somevehicles ].c_str() ), // 5+ + count ); + } + auto const countstring { ( + count == 0 ? locale::strings[ locale::string::driver_scenario_allvehicles ] : + count == 1 ? locale::strings[ locale::string::driver_scenario_onevehicle ] : + orderbuffer.data() ) }; + + std::snprintf( + orderbuffer.data(), orderbuffer.size(), + locale::strings[ locale::string::driver_scenario_disconnect ].c_str(), + countstring.c_str() ); + return orderbuffer.data(); + } + case Shunt: { return locale::strings[ locale::string::driver_scenario_shunt ]; } + case Loose_shunt: { return locale::strings[ locale::string::driver_scenario_looseshunt ]; } + case Obey_train: { return locale::strings[ locale::string::driver_scenario_obeytrain ]; } + case Bank: { return locale::strings[ locale::string::driver_scenario_bank ]; } + default: { return{}; } + } }; void TController::OrdersClear() @@ -1768,7 +1818,7 @@ void TController::Activation() // dla 2Ls150 - przed ustawieniem kierunku - można zmienić tryb pracy if( mvControlling->ShuntModeAllow ) { mvControlling->CurrentSwitch( - ( ( OrderList[ OrderPos ] & Shunt ) == Shunt ) + ( ( OrderCurrentGet() & ( Shunt | Loose_shunt ) ) != 0 ) || ( fMass > 224000.0 ) ); // do tego na wzniesieniu może nie dać rady na liniowym } } @@ -1903,7 +1953,7 @@ void TController::AutoRewident() fBrake_a1[i+1] = 0; } - if( OrderCurrentGet() & Shunt ) { + if( OrderCurrentGet() & ( Shunt | Loose_shunt ) ) { // for uniform behaviour and compatibility with older scenarios set default acceleration table values for shunting fAccThreshold = ( mvOccupied->TrainType == dt_EZT ? -0.55 : @@ -1916,7 +1966,7 @@ void TController::AutoRewident() } } - if( OrderCurrentGet() & Obey_train ) { + if( OrderCurrentGet() & ( Obey_train | Bank ) ) { // 4. Przeliczanie siły hamowania double const velstep = ( mvOccupied->Vmax*0.5 ) / BrakeAccTableSize; d = pVehicles[0]; // pojazd na czele składu @@ -2017,9 +2067,10 @@ double TController::ESMVelocity(bool Main) int TController::CheckDirection() { int d = mvOccupied->DirAbsolute; // który sprzęg jest z przodu - if( !d ) // jeśli nie ma ustalonego kierunku - d = mvOccupied->CabNo; // to jedziemy wg aktualnej kabiny - iDirection = d; // ustalenie kierunku jazdy (powinno zrobić PrepareEngine?) + if( !d ) { + // jeśli nie ma ustalonego kierunku to jedziemy wg aktualnej kabiny + d = mvOccupied->CabNo; + } return d; } @@ -2058,7 +2109,7 @@ bool TController::CheckVehicles(TOrders user) && ( p->MoverParameters->BrakeSubsystem != TBrakeSubSystem::ss_LSt ) ) { iDrivigFlags &= ~( moveOerlikons ); } - p = p->Neightbour(dir); // pojazd podłączony od wskazanej strony + p = p->Neighbour(dir); // pojazd podłączony od wskazanej strony } if (main) iDrivigFlags |= movePrimary; // nie znaleziono innego, można się porządzić @@ -2071,8 +2122,8 @@ bool TController::CheckVehicles(TOrders user) { // HACK: wagony muszą mieć baterię załączoną do otwarcia drzwi... if( ( p != pVehicle ) - && ( ( p->MoverParameters->Couplers[ end::front ].CouplingFlag & ( coupling::control | coupling::permanent ) ) == 0 ) - && ( ( p->MoverParameters->Couplers[ end::rear ].CouplingFlag & ( coupling::control | coupling::permanent ) ) == 0 ) ) { + && ( ( p->MoverParameters->Couplers[ end::front ].CouplingFlag & ( coupling::control ) ) == 0 ) + && ( ( p->MoverParameters->Couplers[ end::rear ].CouplingFlag & ( coupling::control ) ) == 0 ) ) { // NOTE: don't set battery in the occupied vehicle, let the user/ai do it explicitly p->MoverParameters->BatterySwitch( true ); } @@ -2107,7 +2158,7 @@ bool TController::CheckVehicles(TOrders user) frontlights, rearlights ); } - else if (OrderCurrentGet() & (Shunt | Connect)) + else if (OrderCurrentGet() & (Shunt | Loose_shunt | Connect)) { // HACK: the 'front' and 'rear' of the consist is determined by current consist direction // since direction shouldn't affect Tb1 light configuration, we 'counter' this behaviour by virtually swapping end vehicles @@ -2139,7 +2190,7 @@ bool TController::CheckVehicles(TOrders user) } } - if( OrderCurrentGet() & ( Obey_train | Shunt ) ) { + if( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) { // nastawianie hamulca do jazdy pociągowej AutoRewident(); // enable door locks @@ -2227,6 +2278,15 @@ void TController::DirectionInitial() CheckVehicles(); // sprawdzenie świateł oraz skrajnych pojazdów do skanowania }; +void TController::DirectionChange() { + + auto const initialstate { iDirection }; + iDirection = CheckDirection(); + if( iDirection != initialstate ) { + CheckVehicles( Change_direction ); + } +} + int TController::OrderDirectionChange(int newdir, TMoverParameters *Vehicle) { // zmiana kierunku jazdy, niezależnie od kabiny int testd = newdir; @@ -2275,9 +2335,7 @@ void TController::SetVelocity(double NewVel, double NewVelNext, TStopReason r) { eStopReason = stopNone; // podana prędkość, to nie ma powodów do stania // to całe poniżej to warunki zatrąbienia przed ruszeniem - if (OrderList[OrderPos] ? - OrderList[OrderPos] & (Obey_train | Shunt | Connect | Prepare_engine) : - true) // jeśli jedzie w dowolnym trybie + if( (OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank | Connect | Prepare_engine ) ) != 0 ) // jeśli jedzie w dowolnym trybie if ((mvOccupied->Vel < 1.0)) // jesli stoi (na razie, bo chyba powinien też, gdy hamuje przed semaforem) if (iDrivigFlags & moveStartHorn) // jezeli trąbienie włączone if (!(iDrivigFlags & (moveStartHornDone | moveConnect))) @@ -2306,7 +2364,7 @@ double TController::BrakeAccFactor() const void TController::SetDriverPsyche() { - if ((Psyche == Aggressive) && (OrderList[OrderPos] == Obey_train)) + if ((Psyche == Aggressive) && (OrderCurrentGet() == Obey_train)) { ReactionTime = HardReactionTime; // w zaleznosci od charakteru maszynisty if (mvOccupied->CategoryFlag & 2) @@ -2350,7 +2408,6 @@ bool TController::PrepareEngine() voltrear = false; LastReactionTime = 0.0; ReactionTime = PrepareTime; - iDrivigFlags |= moveActive; // może skanować sygnały i reagować na komendy if ( mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) { voltfront = true; @@ -2405,11 +2462,11 @@ bool TController::PrepareEngine() if( !iDirection ) { // jeśli nie ma ustalonego kierunku if( ( mvControlling->PantFrontVolt != 0.0 ) || ( mvControlling->PantRearVolt != 0.0 ) || voltfront || voltrear ) { - if( mvOccupied->Couplers[ 1 ].CouplingFlag == coupling::faux ) { + if( mvOccupied->Couplers[ end::rear ].Connected == nullptr ) { // jeśli z tyłu nie ma nic iDirection = -1; // jazda w kierunku sprzęgu 1 } - if( mvOccupied->Couplers[ 0 ].CouplingFlag == coupling::faux ) { + if( mvOccupied->Couplers[ end::front ].Connected == nullptr ) { // jeśli z przodu nie ma nic iDirection = 1; // jazda w kierunku sprzęgu 0 } @@ -2462,6 +2519,10 @@ bool TController::PrepareEngine() mvOccupied->MotorBlowersSwitch( true, end::front ); mvOccupied->MotorBlowersSwitchOff( false, end::rear ); mvOccupied->MotorBlowersSwitch( true, end::rear ); + // enable train brake if it's off + if( mvOccupied->fBrakeCtrlPos == mvOccupied->Handle->GetPos( bh_NP ) ) { + mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_RP ) ); + } } } else @@ -2481,6 +2542,8 @@ bool TController::PrepareEngine() } eAction = TAction::actUnknown; iEngineActive = 1; + iDrivigFlags |= moveActive; // może skanować sygnały i reagować na komendy + return true; } else { @@ -2615,12 +2678,12 @@ bool TController::IncBrake() || ( mvOccupied->TrainType == dt_ET42 ) ) { // NOTE: we're doing simplified checks full of presuptions here. // they'll break if someone does strange thing like turning around the second unit - if( ( mvOccupied->Couplers[ 1 ].CouplingFlag & coupling::permanent ) - && ( mvOccupied->Couplers[ 1 ].Connected->Couplers[ 1 ].CouplingFlag > 0 ) ) { + if( ( mvOccupied->Couplers[ end::rear ].CouplingFlag & coupling::permanent ) + && ( mvOccupied->Couplers[ end::rear ].Connected->Couplers[ end::rear ].Connected != nullptr ) ) { standalone = false; } - if( ( mvOccupied->Couplers[ 0 ].CouplingFlag & coupling::permanent ) - && ( mvOccupied->Couplers[ 0 ].Connected->Couplers[ 0 ].CouplingFlag > 0 ) ) { + if( ( mvOccupied->Couplers[ end::front ].CouplingFlag & coupling::permanent ) + && ( mvOccupied->Couplers[ end::front ].Connected->Couplers[ end::front ].Connected != nullptr ) ) { standalone = false; } } @@ -2628,22 +2691,27 @@ bool TController::IncBrake() // enforce use of train brake for DMUs standalone = false; } + else if( ( ( OrderCurrentGet() & ( Loose_shunt ) ) != 0 ) + && ( mvOccupied->Vel - VelDesired < fVelPlus + fVelMinus ) ) { + // try to reduce train brake use in loose shunting mode + standalone = true; + } else { /* standalone = ( ( mvOccupied->Couplers[ 0 ].CouplingFlag == 0 ) && ( mvOccupied->Couplers[ 1 ].CouplingFlag == 0 ) ); */ - if( pVehicles[ 0 ] != pVehicles[ 1 ] ) { + if( pVehicles[ end::front ] != pVehicles[ end::rear ] ) { // more detailed version, will use manual braking also for coupled sets of controlled vehicles - auto *vehicle = pVehicles[ 0 ]; // start from first + auto *vehicle = pVehicles[ end::front ]; // start from first while( ( true == standalone ) && ( vehicle != nullptr ) ) { // NOTE: we could simplify this by doing only check of the rear coupler, but this can be quite tricky in itself // TODO: add easier ways to access front/rear coupler taking into account vehicle's direction standalone = - ( ( ( vehicle->MoverParameters->Couplers[ 0 ].CouplingFlag == 0 ) || ( vehicle->MoverParameters->Couplers[ 0 ].CouplingFlag & coupling::control ) ) - && ( ( vehicle->MoverParameters->Couplers[ 1 ].CouplingFlag == 0 ) || ( vehicle->MoverParameters->Couplers[ 1 ].CouplingFlag & coupling::control ) ) ); + ( ( ( vehicle->MoverParameters->Couplers[ end::front ].Connected == nullptr ) || ( vehicle->MoverParameters->Couplers[ end::front ].CouplingFlag & coupling::control ) ) + && ( ( vehicle->MoverParameters->Couplers[ end::rear ].Connected == nullptr ) || ( vehicle->MoverParameters->Couplers[ end::rear ].CouplingFlag & coupling::control ) ) ); vehicle = vehicle->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) } } @@ -2723,7 +2791,7 @@ bool TController::IncBrake() OK = mvOccupied->BrakeLevelAdd(1.0); } else { - OK = mvOccupied->IncLocalBrakeLevel(1); + OK = IncBrakeEIM(); } } else if( mvOccupied->fBrakeCtrlPos != mvOccupied->Handle->GetPos( bh_EPB ) ) { @@ -2741,6 +2809,28 @@ bool TController::IncBrake() return OK; } +bool TController::IncBrakeEIM() +{ // zwiększenie hamowania + bool OK = false; + switch (mvControlling->EIMCtrlType) + { + case 0: + OK = mvControlling->IncLocalBrakeLevel(1); + break; + case 1: + OK = mvControlling->MainCtrlPos > 0; + if (OK) + mvControlling->MainCtrlPos = 0; + break; + case 2: + OK = mvControlling->MainCtrlPos > 1; + if (OK) + mvControlling->MainCtrlPos = 1; + break; + } + return OK; +} + bool TController::DecBrake() { // zmniejszenie siły hamowania bool OK = false; @@ -2778,7 +2868,7 @@ bool TController::DecBrake() OK = mvOccupied->BrakeLevelAdd(-1.0); } else { - OK = mvOccupied->DecLocalBrakeLevel(1); + OK = DecBrakeEIM(); } } else if (mvOccupied->fBrakeCtrlPos != mvOccupied->Handle->GetPos(bh_EPR)) @@ -2797,6 +2887,28 @@ bool TController::DecBrake() return OK; }; +bool TController::DecBrakeEIM() +{ // zmniejszenie siły hamowania + bool OK = false; + switch (mvControlling->EIMCtrlType) + { + case 0: + OK = mvControlling->DecLocalBrakeLevel(1); + break; + case 1: + OK = mvControlling->MainCtrlPos < 2; + if (OK) + mvControlling->MainCtrlPos = 2; + break; + case 2: + OK = mvControlling->MainCtrlPos < 3; + if (OK) + mvControlling->MainCtrlPos = 3; + break; + } + return OK; +} + bool TController::IncSpeed() { // zwiększenie prędkości; zwraca false, jeśli dalej się nie da zwiększać bool OK = true; @@ -3005,7 +3117,7 @@ bool TController::DecSpeed(bool force) OK = mvControlling->DecMainCtrl(2 + (mvControlling->MainCtrlPos / 2)); break; case TEngineType::ElectricInductionMotor: - OK = mvControlling->DecMainCtrl(1); + OK = DecSpeedEIM(); break; case TEngineType::WheelsDriven: if (!mvControlling->CabNo) @@ -3032,6 +3144,28 @@ bool TController::DecSpeed(bool force) return OK; }; +bool TController::DecSpeedEIM() +{ // zmniejszenie prędkości (ale nie hamowanie) + bool OK = false; // domyślnie false, aby wyszło z pętli while + switch (mvControlling->EIMCtrlType) + { + case 0: + OK = mvControlling->DecMainCtrl(1); + break; + case 1: + OK = mvControlling->MainCtrlPos > 4; + if (OK) + mvControlling->MainCtrlPos = 4; + break; + case 2: + OK = mvControlling->MainCtrlPos > 2; + if (OK) + mvControlling->MainCtrlPos = 2; + break; + } + return OK; +} + void TController::SpeedSet() { // Ra: regulacja prędkości, wykonywana w każdym przebłysku świadomości AI // ma dokręcać do bezoporowych i zdejmować pozycje w przypadku przekroczenia prądu @@ -3259,14 +3393,18 @@ void TController::Doors( bool const Open, int const Side ) { && ( ( fActionTime > -0.5 ) || ( false == AIControllFlag ) ) ) { // ai doesn't close the door until it's free to depart, but human driver has free reign to do stupid things - if( ( pVehicle->MoverParameters->Doors.open_control == control_t::conductor ) + if( ( pVehicle->MoverParameters->Doors.close_control == control_t::conductor ) || ( ( true == AIControllFlag ) - && ( ( pVehicle->MoverParameters->Doors.open_control == control_t::driver ) - || ( pVehicle->MoverParameters->Doors.open_control == control_t::mixed ) ) ) ) { + && ( ( pVehicle->MoverParameters->Doors.close_control == control_t::driver ) + || ( pVehicle->MoverParameters->Doors.close_control == control_t::mixed ) ) ) ) { // if the door are controlled by the driver, we let the user operate them unless this user is an ai // the train conductor, if present, handles door operation also for human-driven trains pVehicle->MoverParameters->OperateDoors( side::right, false ); pVehicle->MoverParameters->OperateDoors( side::left, false ); + if( pVehicle->MoverParameters->Doors.permit_needed ) { + pVehicle->MoverParameters->PermitDoors( side::right, false ); + pVehicle->MoverParameters->PermitDoors( side::left, false ); + } } auto *vehicle = pVehicles[ 0 ]; // pojazd na czole składu @@ -3295,6 +3433,10 @@ void TController::Doors( bool const Open, int const Side ) { bool TController::doors_open() const { + return ( + IsAnyDoorOpen[ side::right ] + || IsAnyDoorOpen[ side::left ] ); +/* auto *vehicle = pVehicles[ 0 ]; // pojazd na czole składu while( vehicle != nullptr ) { if( ( false == vehicle->MoverParameters->Doors.instances[side::right].is_closed ) @@ -3306,6 +3448,7 @@ TController::doors_open() const { } // if we're still here there's nothing open return false; +*/ } void TController::RecognizeCommand() @@ -3467,7 +3610,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N { if (NewLocation) vCommandLocation = *NewLocation; - if ((NewValue1 != 0.0) && (OrderList[OrderPos] != Obey_train)) + if ((NewValue1 != 0.0) && (OrderCurrentGet() != Obey_train)) { // o ile jazda if( iEngineActive == 0 ) { // trzeba odpalić silnik najpierw, światła ustawi @@ -3546,7 +3689,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N if (NewCommand == "Change_direction") { - TOrders o = OrderList[OrderPos]; // co robił przed zmianą kierunku + TOrders o = OrderCurrentGet(); // co robił przed zmianą kierunku if (!iEngineActive) OrderNext(Prepare_engine); // trzeba odpalić silnik najpierw if (NewValue1 == 0.0) @@ -3573,18 +3716,29 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N return true; } - if (NewCommand == "Obey_train") - { - if (!iEngineActive) - OrderNext(Prepare_engine); // trzeba odpalić silnik najpierw - OrderNext(Obey_train); + if (NewCommand == "Obey_train") { + if( false == iEngineActive ) { + OrderNext( Prepare_engine ); // trzeba odpalić silnik najpierw + } + OrderNext( Obey_train ); // if (NewValue1>0) TrainNumber=floor(NewValue1); //i co potem ??? OrderCheck(); // jeśli jazda pociągowa teraz, to wykonać niezbędne operacje return true; } - if (NewCommand == "Shunt") + if (NewCommand == "Bank") { + if( false == iEngineActive ) { + OrderNext( Prepare_engine ); // trzeba odpalić silnik najpierw + } + OrderNext( Bank ); + // if (NewValue1>0) TrainNumber=floor(NewValue1); //i co potem ??? + OrderCheck(); // jeśli jazda pociągowa teraz, to wykonać niezbędne operacje + + return true; + } + + if( ( NewCommand == "Shunt" ) || ( NewCommand == "Loose_shunt" ) ) { // NewValue1 - ilość wagonów (-1=wszystkie); NewValue2: 0=odczep, 1..63=dołącz, -1=bez zmian //-3,-y - podłączyć do całego stojącego składu (sprzęgiem y>=1), zmienić kierunek i czekać w // trybie pociągowym @@ -3594,8 +3748,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N //-1, y - podłączyć do całego stojącego składu (sprzęgiem y>=1) i jechać dalej //-1, 0 - tryb manewrowy bez zmian (odczepianie z pozostawieniem wagonów nie ma sensu) // 0, 0 - odczepienie lokomotywy - // 1,-y - podłączyć się do składu (sprzęgiem y>=1), a następnie odczepić i zabrać (x) - // wagonów + // 1,-y - podłączyć się do składu (sprzęgiem y>=1), a następnie odczepić i zabrać (x) wagonów // 1, 0 - odczepienie lokomotywy z jednym wagonem iDrivigFlags &= ~moveStopHere; // podjeżanie do semaforów zezwolone if (!iEngineActive) @@ -3609,31 +3762,36 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N iDirectionOrder = -iDirection; // zmiana na ciągnięcie OrderPush(Change_direction); // najpierw zmień kierunek, bo odczepiamy z tyłu OrderPush(Disconnect); // a odczep już po zmianie kierunku + if( ( NewValue2 > 0.0 ) + && ( NewCommand == "Loose_shunt" ) ) { + // after decoupling continue pushing in the original direction + // NOTE: for backward compatibility this option isn't supported for basic shunting mode + iDirectionOrder = iDirection; // back to pushing + OrderPush( Change_direction ); + } } - else if (NewValue2 < 0.0) // jeśli wszystkie, a sprzęg ujemny, to tylko zmiana kierunku - // po podczepieniu + else if (NewValue2 < 0.0) // jeśli wszystkie, a sprzęg ujemny, to tylko zmiana kierunku po podczepieniu { // np. Shunt -1 -3 iDirectionOrder = -iDirection; // jak się podczepi, to jazda w przeciwną stronę OrderNext(Change_direction); } WaitingTime = 0.0; // nie ma co dalej czekać, można zatrąbić i jechać, chyba że już jedzie } - else // if (NewValue2==0.0) //zerowy sprzęg - if (NewValue1 >= 0.0) // jeśli ilość wagonów inna niż wszystkie - { // będzie odczepianie, ale jeśli wagony są z przodu, to trzeba najpierw zmienić kierunek - if ((mvOccupied->Couplers[mvOccupied->DirAbsolute > 0 ? 1 : 0].CouplingFlag == - 0) ? // z tyłu nic - (mvOccupied->Couplers[mvOccupied->DirAbsolute > 0 ? 0 : 1].CouplingFlag > 0) : - false) // a z przodu skład - { - iDirectionOrder = -iDirection; // zmiana na ciągnięcie - OrderNext(Change_direction); // najpierw zmień kierunek (zastąpi Disconnect) - OrderPush(Disconnect); // a odczep już po zmianie kierunku + else { // if (NewValue2==0.0) //zerowy sprzęg + if( NewValue1 >= 0.0 ) { + // jeśli ilość wagonów inna niż wszystkie będzie odczepianie, + // ale jeśli wagony są z przodu, to trzeba najpierw zmienić kierunek + if( ( mvOccupied->Couplers[ mvOccupied->DirAbsolute > 0 ? end::rear : end::front ].Connected == nullptr ) // z tyłu nic + && ( mvOccupied->Couplers[ mvOccupied->DirAbsolute > 0 ? end::front : end::rear ].Connected != nullptr ) ) { // a z przodu skład + iDirectionOrder = -iDirection; // zmiana na ciągnięcie + OrderNext( Change_direction ); // najpierw zmień kierunek (zastąpi Disconnect) + OrderPush( Disconnect ); // a odczep już po zmianie kierunku + } + else if( mvOccupied->Couplers[ mvOccupied->DirAbsolute > 0 ? end::rear : end::front ].Connected != nullptr ) { // z tyłu coś + OrderNext( Disconnect ); // jak ciągnie, to tylko odczep (NewValue1) wagonów + } + WaitingTime = 0.0; // nie ma co dalej czekać, można zatrąbić i jechać, chyba że już jedzie } - else if (mvOccupied->Couplers[mvOccupied->DirAbsolute > 0 ? 1 : 0].CouplingFlag > - 0) // z tyłu coś - OrderNext(Disconnect); // jak ciągnie, to tylko odczep (NewValue1) wagonów - WaitingTime = 0.0; // nie ma co dalej czekać, można zatrąbić i jechać, chyba że już jedzie } if (NewValue1 == -1.0) { @@ -3644,9 +3802,9 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N iDrivigFlags |= moveStopHere; // nie podjeżdżać do semafora, jeśli droga nie jest wolna // (nie dotyczy Connect) if (NewValue1 < -2.5) // jeśli - OrderNext(Obey_train); // to potem jazda pociągowa + OrderNext(( NewCommand == "Shunt" ? Obey_train : Bank )); // to potem jazda pociągowa else - OrderNext(Shunt); // to potem manewruj dalej + OrderNext(( NewCommand == "Shunt" ? Shunt : Loose_shunt )); // to potem manewruj dalej CheckVehicles(); // sprawdzić światła // if ((iVehicleCount>=0)&&(NewValue1<0)) WriteLog("Skasowano ilosć wagonów w Shunt!"); if (NewValue1 != iVehicleCount) @@ -3797,12 +3955,18 @@ TController::UpdateSituation(double dt) { LastReactionTime += dt; LastUpdatedTime += dt; if( ( mvOccupied->Vel < 0.05 ) - && ( ( OrderCurrentGet() & ( Obey_train | Shunt ) ) != 0 ) ) { + && ( ( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) != 0 ) ) { IdleTime += dt; } else { IdleTime = 0.0; } + // HACK: activate route scanning if an idling vehicle is activated by a human user + if( ( OrderCurrentGet() == Wait_for_orders ) + && ( false == AIControllFlag ) + && ( true == mvControlling->Battery ) ) { + OrderNext( Prepare_engine ); + } // log vehicle data if( ( WriteLogFlag ) @@ -4019,7 +4183,7 @@ TController::UpdateSituation(double dt) { mvControlling->PantFront( true ); } } - if( mvOccupied->Vel > 10 ) { + if( mvOccupied->Vel > 5 ) { // opuszczenie przedniego po rozpędzeniu się o ile jest więcej niż jeden if( mvControlling->EnginePowerSource.CollectorParameters.CollectorsNo > 1 ) { if( iDirection >= 0 ) // jak jedzie w kierunku sprzęgu 0 @@ -4127,20 +4291,22 @@ TController::UpdateSituation(double dt) { // następnie w drugą stronę, z drogi hamowania i chwilowej prędkości powinno być wyznaczane zalecane ciśnienie // przybliżona droga hamowania - fBrakeDist = fDriverBraking * mvOccupied->Vel * ( 40.0 + mvOccupied->Vel ); + // NOTE: we're ensuring some minimal braking distance to reduce ai flipping states between starting and braking + auto const velceil { std::max( 2.0, std::ceil( mvOccupied->Vel ) ) }; + fBrakeDist = fDriverBraking * velceil * ( 40.0 + velceil ); if( fMass > 1000000.0 ) { // korekta dla ciężkich, bo przeżynają - da to coś? fBrakeDist *= 2.0; } if( ( -fAccThreshold > 0.05 ) && ( mvOccupied->CategoryFlag == 1 ) ) { - fBrakeDist = mvOccupied->Vel * mvOccupied->Vel / 25.92 / -fAccThreshold; + fBrakeDist = velceil * velceil / 25.92 / -fAccThreshold; } if( mvOccupied->BrakeDelayFlag == bdelay_G ) { // dla nastawienia G koniecznie należy wydłużyć drogę na czas reakcji - fBrakeDist += 2 * mvOccupied->Vel; + fBrakeDist += 2 * velceil; } - if( ( mvOccupied->Vel > 0.05 ) + if( ( mvOccupied->Vel > 15.0 ) && ( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) && ( ( mvControlling->TrainType & dt_EZT ) != 0 ) ) { // HACK: make the induction motor powered EMUs start braking slightly earlier @@ -4174,55 +4340,48 @@ TController::UpdateSituation(double dt) { // 8. Ustalić częstotliwość świadomości AI (zatrzymanie precyzyjne - częściej, brak atrakcji // - rzadziej). - // check for potential colliders + // check for potential collisions { - auto rearvehicle = ( - pVehicles[ 0 ] == pVehicles[ 1 ] ? - pVehicles[ 0 ] : - pVehicles[ 1 ] ); + // HACK: vehicle order in the consist is based on intended travel direction + // if our actual travel direction doesn't match that, we should be scanning from the other end of the consist + // we cast to int to avoid getting confused by microstutters + auto *frontvehicle { pVehicles[ ( static_cast( mvOccupied->V ) * iDirection >= 0 ? end::front : end::rear ) ] }; + + int routescandirection; // for moving vehicle determine heading from velocity; for standing fall back on the set direction - if( ( std::abs( mvOccupied->V ) < 0.1 ? // ignore potential micro-stutters in oposite direction during "almost stop" + if( ( std::abs( frontvehicle->MoverParameters->V ) > 0.5 ? // ignore potential micro-stutters in oposite direction during "almost stop" + frontvehicle->MoverParameters->V > 0.0 : + ( pVehicle->DirectionGet() == frontvehicle->DirectionGet() ? iDirection > 0 : - mvOccupied->V > 0.0 ) ) { + iDirection < 0 ) ) ) { // towards coupler 0 - if( ( mvOccupied->V * iDirection < 0.0 ) - || ( ( rearvehicle->NextConnected != nullptr ) - && ( rearvehicle->MoverParameters->Couplers[ ( rearvehicle->DirectionGet() > 0 ? 1 : 0 ) ].CouplingFlag == coupling::faux ) ) ) { - // scan behind if we're moving backward, or if we had something connected there and are moving away - rearvehicle->ABuScanObjects( ( - pVehicle->DirectionGet() == rearvehicle->DirectionGet() ? - -1 : - 1 ), - fMaxProximityDist ); - } - pVehicles[ 0 ]->ABuScanObjects( ( - pVehicle->DirectionGet() == pVehicles[ 0 ]->DirectionGet() ? - 1 : - -1 ), - routescanrange ); + routescandirection = end::front; } else { // towards coupler 1 - if( ( mvOccupied->V * iDirection < 0.0 ) - || ( ( rearvehicle->PrevConnected != nullptr ) - && ( rearvehicle->MoverParameters->Couplers[ ( rearvehicle->DirectionGet() > 0 ? 0 : 1 ) ].CouplingFlag == coupling::faux ) ) ) { - // scan behind if we're moving backward, or if we had something connected there and are moving away - rearvehicle->ABuScanObjects( ( - pVehicle->DirectionGet() == rearvehicle->DirectionGet() ? - 1 : - -1 ), - fMaxProximityDist ); + routescandirection = end::rear; + } +/* + if( pVehicle->MoverParameters->ActiveCab < 0 ) { + // flip the scan direction in the rear cab + routescandirection ^= routescandirection; + } +*/ + Obstacle = neighbour_data(); + auto const lookup { frontvehicle->find_vehicle( routescandirection, routescanrange ) }; + + if( std::get( lookup ) == true ) { + + Obstacle.vehicle = std::get( lookup ); + Obstacle.vehicle_end = std::get( lookup ); + Obstacle.distance = std::get( lookup ); + + if( Obstacle.distance < ( mvOccupied->CategoryFlag == 2 ? 50 : 100 ) ) { + // at short distances (re)calculate range between couplers directly + Obstacle.distance = TMoverParameters::CouplerDist( frontvehicle->MoverParameters, Obstacle.vehicle->MoverParameters ); } - pVehicles[ 0 ]->ABuScanObjects( ( - pVehicle->DirectionGet() == pVehicles[ 0 ]->DirectionGet() ? - -1 : - 1 ), - routescanrange ); } } - - - // tu bedzie logika sterowania if (AIControllFlag) { @@ -4263,7 +4422,7 @@ TController::UpdateSituation(double dt) { } } - switch (OrderList[OrderPos]) + switch (OrderCurrentGet()) { // ustalenie prędkości przy doczepianiu i odczepianiu, dystansów w pozostałych przypadkach case Connect: { // podłączanie do składu @@ -4276,37 +4435,17 @@ TController::UpdateSituation(double dt) { if (AIControllFlag) { // to robi tylko AI, wersję dla człowieka trzeba dopiero zrobić // sprzęgi sprawdzamy w pierwszej kolejności, bo jak połączony, to koniec - bool ok; // true gdy się podłączy (uzyskany sprzęg będzie zgodny z żądanym) - if (pVehicles[0]->DirectionGet() > 0) // jeśli sprzęg 0 - { // sprzęg 0 - próba podczepienia - if( pVehicles[ 0 ]->MoverParameters->Couplers[ 0 ].Connected ) { - // jeśli jest coś wykryte (a chyba jest, nie?) - if( pVehicles[ 0 ]->MoverParameters->Attach( - 0, 2, pVehicles[ 0 ]->MoverParameters->Couplers[ 0 ].Connected, - iCoupler ) ) { - // pVehicles[0]->dsbCouplerAttach->SetVolume(DSBVOLUME_MAX); - // pVehicles[0]->dsbCouplerAttach->Play(0,0,0); - } - } - // udało się? (mogło częściowo) - ok = (pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag == iCoupler); - } - else // if (pVehicles[0]->MoverParameters->DirAbsolute<0) //jeśli sprzęg 1 - { // sprzęg 1 - próba podczepienia - if( pVehicles[ 0 ]->MoverParameters->Couplers[ 1 ].Connected ) { - // jeśli jest coś wykryte (a chyba jest, nie?) - if( pVehicles[ 0 ]->MoverParameters->Attach( - 1, 2, pVehicles[ 0 ]->MoverParameters->Couplers[ 1 ].Connected, - iCoupler ) ) { - // pVehicles[0]->dsbCouplerAttach->SetVolume(DSBVOLUME_MAX); - // pVehicles[0]->dsbCouplerAttach->Play(0,0,0); - } - } - // udało się? (mogło częściowo) - ok = (pVehicles[0]->MoverParameters->Couplers[1].CouplingFlag == iCoupler); - } - if (ok) - { // jeżeli został podłączony + auto *vehicle { pVehicles[ end::front ] }; + auto *vehicleparameters { vehicle->MoverParameters }; + int const end { ( vehicle->DirectionGet() > 0 ? end::front : end::rear ) }; + auto const &neighbour { vehicleparameters->Neighbours[ end ] }; + // próba podczepienia + vehicleparameters->Attach( + end, neighbour.vehicle_end, + neighbour.vehicle->MoverParameters, + iCoupler ); + if( vehicleparameters->Couplers[ end ].CouplingFlag == iCoupler ) { + // jeżeli został podłączony iCoupler = 0; // dalsza jazda manewrowa już bez łączenia iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie @@ -4322,7 +4461,7 @@ TController::UpdateSituation(double dt) { fMaxProximityDist = 5.0; //[m] w takim przedziale odległości powinien stanąć fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania fVelMinus = 1.0; // margines prędkości powodujący załączenie napędu - if( pVehicles[ 0 ]->fTrackBlock <= 20.0 ) { + if( Obstacle.distance <= 20.0 ) { // przy zderzeniu fTrackBlock nie jest miarodajne // początek podczepiania, z wyłączeniem sprawdzania fTrackBlock iDrivigFlags |= moveConnect; @@ -4354,6 +4493,13 @@ TController::UpdateSituation(double dt) { fVelMinus = std::min( 0.1 * fShuntVelocity, 3.0 ); break; } + case Loose_shunt: { + fMinProximityDist = -1.0; + fMaxProximityDist = 0.0; //[m] dojechać maksymalnie + fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania + fVelMinus = 0.5; // margines prędkości powodujący załączenie napędu + break; + } case Obey_train: { // na jaka odleglosc i z jaka predkoscia ma podjechac do przeszkody if( mvOccupied->CategoryFlag & 1 ) { @@ -4403,6 +4549,10 @@ TController::UpdateSituation(double dt) { } break; } + case Bank: { + // TODO: implement + break; + } default: { fMinProximityDist = 5.0; fMaxProximityDist = 10.0; //[m] @@ -4411,7 +4561,7 @@ TController::UpdateSituation(double dt) { } } // switch OrderList[OrderPos] - switch (OrderList[OrderPos]) + switch (OrderCurrentGet()) { // co robi maszynista case Prepare_engine: // odpala silnik // if (AIControllFlag) @@ -4445,13 +4595,16 @@ TController::UpdateSituation(double dt) { break; case Wait_for_orders: // jeśli czeka, też ma skanować, żeby odpalić się od semafora case Shunt: + case Loose_shunt: case Obey_train: + case Bank: case Connect: case Disconnect: case Change_direction: // tryby wymagające jazdy case Change_direction | Shunt: // zmiana kierunku podczas manewrów + case Change_direction | Loose_shunt: case Change_direction | Connect: // zmiana kierunku podczas podłączania - if (OrderList[OrderPos] != Obey_train) // spokojne manewry + if ((OrderCurrentGet() & ( Obey_train | Bank )) == 0) // spokojne manewry { VelSignal = min_speed( VelSignal, 40.0 ); // jeśli manewry, to ograniczamy prędkość @@ -4462,8 +4615,8 @@ TController::UpdateSituation(double dt) { && ( false == TestFlag( iDrivigFlags, movePress ) ) && ( iCoupler == 0 ) // && ( mvOccupied->Vel > 0.0 ) - && ( pVehicle->MoverParameters->Couplers[ end::front ].CouplingFlag == coupling::faux ) - && ( pVehicle->MoverParameters->Couplers[ end::rear ].CouplingFlag == coupling::faux ) ) { + && ( pVehicle->MoverParameters->Couplers[ end::front ].Connected == nullptr ) + && ( pVehicle->MoverParameters->Couplers[ end::rear ].Connected == nullptr ) ) { SetVelocity(0, 0, stopJoin); // 1. faza odczepiania: zatrzymanie // WriteLog("Zatrzymanie w celu odczepienia"); AccPreferred = std::min( 0.0, AccPreferred ); @@ -4473,14 +4626,14 @@ TController::UpdateSituation(double dt) { else SetDriverPsyche(); // Ra: było w PrepareEngine(), potrzebne tu? - if (OrderList[OrderPos] & (Shunt | Obey_train | Connect)) { + if (OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank | Connect )) { // odjechać sam może tylko jeśli jest w trybie jazdy // automatyczne ruszanie po odstaniu albo spod SBL if( ( VelSignal == 0.0 ) && ( WaitingTime > 0.0 ) && ( mvOccupied->RunningTrack.Velmax != 0.0 ) ) { // jeśli stoi, a upłynął czas oczekiwania i tor ma niezerową prędkość - if( ( OrderList[ OrderPos ] & ( Obey_train | Shunt ) ) + if( ( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) && ( iDrivigFlags & moveStopHere ) ) { // zakaz ruszania z miejsca bez otrzymania wolnej drogi WaitingTime = -WaitingExpireTime; @@ -4501,7 +4654,7 @@ TController::UpdateSituation(double dt) { else { // samochód ma stać, aż dostanie odjazd, chyba że stoi przez kolizję if (eStopReason == stopBlock) - if (pVehicles[0]->fTrackBlock > fDriverDist) + if (Obstacle.distance > fDriverDist) if (AIControllFlag) { PrepareEngine(); // zmieni ustawiony kierunek @@ -4523,14 +4676,14 @@ TController::UpdateSituation(double dt) { } // koniec samoistnego odjeżdżania if( ( true == AIControllFlag) - && ( true == TestFlag( OrderList[ OrderPos ], Change_direction ) ) ) { + && ( true == TestFlag( OrderCurrentGet(), Change_direction ) ) ) { // sprobuj zmienic kierunek (może być zmieszane z jeszcze jakąś komendą) if( mvOccupied->Vel < 0.1 ) { // jeśli się zatrzymał, to zmieniamy kierunek jazdy, a nawet kabinę/człon Activation(); // ustawienie zadanego wcześniej kierunku i ewentualne przemieszczenie AI PrepareEngine(); JumpToNextOrder(); // następnie robimy, co jest do zrobienia (Shunt albo Obey_train) - if( OrderList[ OrderPos ] & ( Shunt | Connect ) ) { + if( OrderCurrentGet() & ( Shunt | Loose_shunt | Connect ) ) { // jeśli dalej mamy manewry if( false == TestFlag( iDrivigFlags, moveStopHere ) ) { // o ile nie ma stać w miejscu, @@ -4544,7 +4697,7 @@ TController::UpdateSituation(double dt) { if( ( true == AIControllFlag ) && ( iEngineActive == 0 ) - && ( OrderList[ OrderPos ] & ( Change_direction | Connect | Disconnect | Shunt | Obey_train ) ) ) { + && ( OrderCurrentGet() & ( Change_direction | Connect | Disconnect | Shunt | Loose_shunt | Obey_train | Bank ) ) ) { // jeśli coś ma robić to niech odpala do skutku PrepareEngine(); } @@ -4583,21 +4736,18 @@ TController::UpdateSituation(double dt) { if (iDrivigFlags & moveStopCloser) VelSignal = -1.0; // ma czekać na sygnał z sygnalizatora! case TCommandType::cm_SetVelocity: // od wersji 357 semafor nie budzi wyłączonej lokomotywy - if (!(OrderList[OrderPos] & - ~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders - if (fabs(VelSignal) >= - 1.0) // 0.1 nie wysyła się do samochodow, bo potem nie ruszą + if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) // jedzie w dowolnym trybie albo Wait_for_orders + if (fabs(VelSignal) >= 1.0) // 0.1 nie wysyła się do samochodow, bo potem nie ruszą PutCommand("SetVelocity", VelSignal, VelNext, nullptr); // komenda robi dodatkowe operacje break; case TCommandType::cm_ShuntVelocity: // od wersji 357 Tm nie budzi wyłączonej lokomotywy - if (!(OrderList[OrderPos] & - ~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders + if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) // jedzie w dowolnym trybie albo Wait_for_orders PutCommand("ShuntVelocity", VelSignal, VelNext, nullptr); else if (iCoupler) // jeśli jedzie w celu połączenia SetVelocity(VelSignal, VelNext); break; case TCommandType::cm_Command: // komenda z komórki - if( !( OrderList[ OrderPos ] & ~( Obey_train | Shunt ) ) ) { + if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) { // jedzie w dowolnym trybie albo Wait_for_orders if( mvOccupied->Vel < 0.1 ) { // dopiero jak stanie @@ -4614,139 +4764,153 @@ TController::UpdateSituation(double dt) { } // disconnect mode potentially overrides scan results // TBD: when in this mode skip scanning altogether? - if( ( OrderCurrentGet() & Disconnect ) != 0 ) { + if( ( true == AIControllFlag ) + && ( ( OrderCurrentGet() & Disconnect ) != 0 ) ) { - if (AIControllFlag) { - if (iVehicleCount >= 0) { - // jeśli była podana ilość wagonów - if (iDrivigFlags & movePress) { - // jeśli dociskanie w celu odczepienia - // 3. faza odczepiania. - SetVelocity(2, 0); // jazda w ustawionym kierunku z prędkością 2 - if ((mvControlling->MainCtrlPos > 0) || - (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic)) // jeśli jazda - { - WriteLog(mvOccupied->Name + " odczepianie w kierunku " + std::to_string(mvOccupied->DirAbsolute)); - TDynamicObject *p = - pVehicle; // pojazd do odczepienia, w (pVehicle) siedzi AI - int d; // numer sprzęgu, który sprawdzamy albo odczepiamy - int n = iVehicleCount; // ile wagonów ma zostać - do - { // szukanie pojazdu do odczepienia - d = p->DirectionGet() > 0 ? - 0 : - 1; // numer sprzęgu od strony czoła składu - // if (p->MoverParameters->Couplers[d].CouplerType==Articulated) - // //jeśli sprzęg typu wózek (za mało) - if (p->MoverParameters->Couplers[d].CouplingFlag & ctrain_depot) // jeżeli sprzęg zablokowany - // if (p->GetTrack()->) //a nie stoi na torze warsztatowym - // (ustalić po czym poznać taki tor) - ++n; // to liczymy człony jako jeden - p->MoverParameters->BrakeReleaser(1); // wyluzuj pojazd, aby dało się dopychać - p->MoverParameters->BrakeLevelSet(0); // hamulec na zero, aby nie hamował - if (n) - { // jeśli jeszcze nie koniec - p = p->Prev(); // kolejny w stronę czoła składu (licząc od - // tyłu), bo dociskamy - if (!p) - iVehicleCount = -2, - n = 0; // nie ma co dalej sprawdzać, doczepianie zakończone - } - } while (n--); - if( p ? p->MoverParameters->Couplers[ d ].CouplingFlag == coupling::faux : true ) { - // no target, or already just virtual coupling - WriteLog( mvOccupied->Name + " didn't find anything to disconnect." ); - iVehicleCount = -2; // odczepiono, co było do odczepienia - } else if ( p->Dettach(d) == coupling::faux ) { - // tylko jeśli odepnie - WriteLog( mvOccupied->Name + " odczepiony." ); - iVehicleCount = -2; - } // a jak nie, to dociskać dalej - } - if (iVehicleCount >= 0) // zmieni się po odczepieniu - if (!mvOccupied->DecLocalBrakeLevel(1)) - { // dociśnij sklad - WriteLog( mvOccupied->Name + " dociskanie..." ); - // mvOccupied->BrakeReleaser(); //wyluzuj lokomotywę - // Ready=true; //zamiast sprawdzenia odhamowania całego składu - IncSpeed(); // dla (Ready)==false nie ruszy + if (iVehicleCount >= 0) { + // jeśli była podana ilość wagonów + if (iDrivigFlags & movePress) { + // jeśli dociskanie w celu odczepienia + // 3. faza odczepiania. + SetVelocity(2, 0); // jazda w ustawionym kierunku z prędkością 2 + if( ( mvControlling->MainCtrlPos > 0 ) + || ( mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ) ) { + // jeśli jazda + WriteLog(mvOccupied->Name + " odczepianie w kierunku " + std::to_string(mvOccupied->DirAbsolute)); + TDynamicObject *p = pVehicle; // pojazd do odczepienia, w (pVehicle) siedzi AI + int d; // numer sprzęgu, który sprawdzamy albo odczepiamy + int n = iVehicleCount; // ile wagonów ma zostać + do + { // szukanie pojazdu do odczepienia + d = p->DirectionGet() > 0 ? + end::front : + end::rear; // numer sprzęgu od strony czoła składu + // if (p->MoverParameters->Couplers[d].CouplerType==Articulated) + // //jeśli sprzęg typu wózek (za mało) + if (p->MoverParameters->Couplers[d].CouplingFlag & ctrain_depot) // jeżeli sprzęg zablokowany + // if (p->GetTrack()->) //a nie stoi na torze warsztatowym + // (ustalić po czym poznać taki tor) + ++n; // to liczymy człony jako jeden + p->MoverParameters->BrakeReleaser(1); // wyluzuj pojazd, aby dało się dopychać + p->MoverParameters->BrakeLevelSet(0); // hamulec na zero, aby nie hamował + if (n) + { // jeśli jeszcze nie koniec + p = p->Prev(); // kolejny w stronę czoła składu (licząc od tyłu), bo dociskamy + if (!p) + iVehicleCount = -2, + n = 0; // nie ma co dalej sprawdzać, doczepianie zakończone } + } while (n--); + if( ( p == nullptr ) + || ( p->MoverParameters->Couplers[ d ].Connected == nullptr ) ) { + // no target, or already just virtual coupling + WriteLog( mvOccupied->Name + " didn't find anything to disconnect." ); + iVehicleCount = -2; // odczepiono, co było do odczepienia + } else if ( p->Dettach(d) == coupling::faux ) { + // tylko jeśli odepnie + WriteLog( mvOccupied->Name + " odczepiony." ); + iVehicleCount = -2; + } // a jak nie, to dociskać dalej } - if ((mvOccupied->Vel < 0.01) && !(iDrivigFlags & movePress)) - { // 2. faza odczepiania: zmień kierunek na przeciwny i dociśnij - // za radą yB ustawiamy pozycję 3 kranu (ruszanie kranem w innych miejscach - // powino zostać wyłączone) - // WriteLog("Zahamowanie składu"); - mvOccupied->BrakeLevelSet( - mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ? - 1 : - 3 ); - double p = mvOccupied->BrakePressureActual.PipePressureVal; - if( p < 3.9 ) { - // tu może być 0 albo -1 nawet - // TODO: zabezpieczenie przed dziwnymi CHK do czasu wyjaśnienia sensu 0 oraz -1 w tym miejscu - p = 3.9; + if (iVehicleCount >= 0) // zmieni się po odczepieniu + if (!mvOccupied->DecLocalBrakeLevel(1)) + { // dociśnij sklad + WriteLog( mvOccupied->Name + " dociskanie..." ); + // mvOccupied->BrakeReleaser(); //wyluzuj lokomotywę + // Ready=true; //zamiast sprawdzenia odhamowania całego składu + IncSpeed(); // dla (Ready)==false nie ruszy } - if (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ? - mvOccupied->BrakePress > 2 : - mvOccupied->PipePress < p + 0.1) - { // jeśli w miarę został zahamowany (ciśnienie mniejsze niż podane na - // pozycji 3, zwyle 0.37) - if (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic) - mvOccupied->BrakeLevelSet(0); // wyłączenie EP, gdy wystarczy (może - // nie być potrzebne, bo na początku jest) - WriteLog("Luzowanie lokomotywy i zmiana kierunku"); - mvOccupied->BrakeReleaser(1); // wyluzuj lokomotywę; a ST45? - mvOccupied->DecLocalBrakeLevel(LocalBrakePosNo); // zwolnienie hamulca - iDrivigFlags |= movePress; // następnie będzie dociskanie - DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na przeciwny (dociskanie) - CheckVehicles(); // od razu zmienić światła (zgasić) - bez tego się nie odczepi - /* - // NOTE: disabled to prevent closing the door before passengers can disembark - fStopTime = 0.0; // nie ma na co czekać z odczepianiem - */ + } + if ((mvOccupied->Vel < 0.01) && !(iDrivigFlags & movePress)) + { // 2. faza odczepiania: zmień kierunek na przeciwny i dociśnij + // za radą yB ustawiamy pozycję 3 kranu (ruszanie kranem w innych miejscach + // powino zostać wyłączone) + // WriteLog("Zahamowanie składu"); + mvOccupied->BrakeLevelSet( + mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ? + 1 : + 3 ); + double p = mvOccupied->BrakePressureActual.PipePressureVal; + if( p < 3.9 ) { + // tu może być 0 albo -1 nawet + // TODO: zabezpieczenie przed dziwnymi CHK do czasu wyjaśnienia sensu 0 oraz -1 w tym miejscu + p = 3.9; + } + if (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ? + mvOccupied->BrakePress > 2 : + mvOccupied->PipePress < p + 0.1) + { // jeśli w miarę został zahamowany (ciśnienie mniejsze niż podane na pozycji 3, zwyle 0.37) + if( mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ) { + // wyłączenie EP, gdy wystarczy (może nie być potrzebne, bo na początku jest) + mvOccupied->BrakeLevelSet( 0 ); } + WriteLog("Luzowanie lokomotywy i zmiana kierunku"); + mvOccupied->BrakeReleaser(1); // wyluzuj lokomotywę; a ST45? + mvOccupied->DecLocalBrakeLevel(LocalBrakePosNo); // zwolnienie hamulca + iDrivigFlags |= movePress; // następnie będzie dociskanie + DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na przeciwny (dociskanie) + CheckVehicles(); // od razu zmienić światła (zgasić) - bez tego się nie odczepi } - else { - if( mvOccupied->Vel > 0.01 ) { - // 1st phase(?) - // bring it to stop if it's not already stopped - SetVelocity( 0, 0, stopJoin ); // wyłączyć przyspieszanie - } - } - } // odczepiania - else // to poniżej jeśli ilość wagonów ujemna - if (iDrivigFlags & movePress) - { // 4. faza odczepiania: zwolnij i zmień kierunek - SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie - if (!DecSpeed()) // jeśli już bardziej wyłączyć się nie da - { // ponowna zmiana kierunku - WriteLog( mvOccupied->Name + " ponowna zmiana kierunku" ); - DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na właściwy - iDrivigFlags &= ~movePress; // koniec dociskania - JumpToNextOrder(); // zmieni światła - TableClear(); // skanowanie od nowa - iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem - SetVelocity(fShuntVelocity, fShuntVelocity); // ustawienie prędkości jazdy + } + else { + if( mvOccupied->Vel > 0.01 ) { + // 1st phase(?) + // bring it to stop if it's not already stopped + SetVelocity( 0, 0, stopJoin ); // wyłączyć przyspieszanie } } + } // odczepiania + else // to poniżej jeśli ilość wagonów ujemna + if (iDrivigFlags & movePress) + { // 4. faza odczepiania: zwolnij i zmień kierunek + SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie + if (!DecSpeed()) // jeśli już bardziej wyłączyć się nie da + { // ponowna zmiana kierunku + WriteLog( mvOccupied->Name + " ponowna zmiana kierunku" ); + DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na właściwy + iDrivigFlags &= ~movePress; // koniec dociskania + JumpToNextOrder(); // zmieni światła + TableClear(); // skanowanie od nowa + iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem + SetVelocity(fShuntVelocity, fShuntVelocity); // ustawienie prędkości jazdy + } } } - if( true == TestFlag( OrderList[ OrderPos ], Change_direction ) ) { + // when loose shunting try to detect situations where engaged brakes in a consist to be pushed prevent movement + if( ( true == AIControllFlag ) + && ( ( OrderCurrentGet() & Loose_shunt ) != 0 ) + && ( Obstacle.distance < 1.0 ) + && ( AccDesired > 0.1 ) + && ( mvOccupied->Vel < 1.0 ) ){ + + auto *vehicle { Obstacle.vehicle }; + auto const direction { ( vehicle->Prev() != nullptr ? end::front : end::rear ) }; + while( vehicle != nullptr ) { + if( vehicle->MoverParameters->BrakePress > 0.2 ) { + vehicle->MoverParameters->BrakeLevelSet( 0 ); // hamulec na zero, aby nie hamował + vehicle->MoverParameters->BrakeReleaser( 1 ); // wyluzuj pojazd, aby dało się dopychać + } + // NOTE: we trust the consist to be arranged in a valid chain + // TBD, TODO: traversal direction validation? + vehicle = ( direction == end::front ? vehicle->Prev() : vehicle->Next() ); + } + } + + if( true == TestFlag( OrderCurrentGet(), Change_direction ) ) { // if ordered to change direction, try to stop SetVelocity( 0, 0, stopDir ); } if( VelNext == 0.0 ) { - if( !( OrderList[ OrderPos ] & ~( Shunt | Connect ) ) ) { + if( !( OrderCurrentGet() & ~( Shunt | Loose_shunt | Connect ) ) ) { // 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 // Ra 2F1H: z tym (fTrackBlock) to nie jest najlepszy pomysł, bo lepiej by // było porównać z odległością od sygnalizatora z przodu - if( ( OrderList[ OrderPos ] & Connect ) ? - ( pVehicles[ 0 ]->fTrackBlock > 2000 || pVehicles[ 0 ]->fTrackBlock > FirstSemaphorDist ) : - true ) { + if( ( ( OrderCurrentGet() & Connect ) == 0 ) + || ( Obstacle.distance > std::min( 2000.0, FirstSemaphorDist ) ) ) { + if( ( comm = BackwardScan() ) != TCommandType::cm_Unknown ) { // jeśli w drugą można jechać // należy sprawdzać odległość od znalezionego sygnalizatora, @@ -4758,7 +4922,7 @@ TController::UpdateSituation(double dt) { } iDirectionOrder = -iDirection; // zmiana kierunku jazdy // zmiana kierunku bez psucia kolejnych komend - OrderList[ OrderPos ] = TOrders( OrderList[ OrderPos ] | Change_direction ); + OrderList[ OrderPos ] = TOrders( OrderCurrentGet() | Change_direction ); } } } @@ -4771,121 +4935,100 @@ TController::UpdateSituation(double dt) { VelDesired = fVelMax; // bo VelDesired<0 oznacza prędkość maksymalną // Ra: jazda na widoczność -/* - // condition disabled, it'd prevent setting reduced acceleration in the last connect stage - if ((iDrivigFlags & moveConnect) == 0) // przy końcówce podłączania nie hamować -*/ - { // sprawdzenie jazdy na widoczność - auto const vehicle = pVehicles[ 0 ]; // base calculactions off relevant end of the consist - auto const coupler = - vehicle->MoverParameters->Couplers + ( - vehicle->DirectionGet() > 0 ? - 0 : - 1 ); // sprzęg z przodu składu - if( ( coupler->Connected ) - && ( coupler->CouplingFlag == coupling::faux ) ) { - // mamy coś z przodu podłączone sprzęgiem wirtualnym - // wyliczanie optymalnego przyspieszenia do jazdy na widoczność -/* - ActualProximityDist = std::min( + // Ra: jazda na widoczność + if( Obstacle.distance < 5000 ) { + // mamy coś z przodu + // prędkość pojazdu z przodu (zakładając, że jedzie w tę samą stronę!!!) + auto const k { Obstacle.vehicle->MoverParameters->Vel }; + if( k - vel < 5 ) { + // porównanie modułów prędkości [km/h] + // zatroszczyć się trzeba, jeśli tamten nie jedzie znacząco szybciej + ActualProximityDist = std::min( ActualProximityDist, - vehicle->fTrackBlock - ( - mvOccupied->CategoryFlag & 2 ? - fMinProximityDist : // cars can bunch up tighter - fMaxProximityDist ) ); // other vehicle types less so -*/ - // prędkość pojazdu z przodu (zakładając, że jedzie w tę samą stronę!!!) - double k = coupler->Connected->Vel; - if( k - vel < 5 ) { - // porównanie modułów prędkości [km/h] - // zatroszczyć się trzeba, jeśli tamten nie jedzie znacząco szybciej - ActualProximityDist = std::min( - ActualProximityDist, - vehicle->fTrackBlock ); + Obstacle.distance ); - if( ActualProximityDist <= ( - ( mvOccupied->CategoryFlag & 2 ) ? - 100.0 : // cars - 250.0 ) ) { // others - // regardless of driving mode at close distance take precaution measures: - // match the other vehicle's speed or slow down if the other vehicle is stopped + if( ActualProximityDist <= ( + ( mvOccupied->CategoryFlag & 2 ) ? + 100.0 : // cars + 250.0 ) ) { // others + // regardless of driving mode at close distance take precaution measures: + // match the other vehicle's speed or slow down if the other vehicle is stopped + VelDesired = + min_speed( + VelDesired, + std::max( + k, + ( mvOccupied->CategoryFlag & 2 ) ? + 40.0 : // cars + 20.0 ) ); // others + if( vel > VelDesired + fVelPlus ) { + // if going too fast force some prompt braking + AccPreferred = std::min( -0.65, AccPreferred ); + } + } + + double const distance = Obstacle.distance - fMaxProximityDist - ( fBrakeDist * 1.15 ); // odległość bezpieczna zależy od prędkości + if( distance < 0.0 ) { + // jeśli odległość jest zbyt mała + if( k < 10.0 ) // k - prędkość tego z przodu + { // jeśli tamten porusza się z niewielką prędkością albo stoi + if( ( OrderCurrentGet() & ( Connect | Loose_shunt ) ) != 0 ) { + // jeśli spinanie, to jechać dalej + AccPreferred = std::min( 0.35, AccPreferred ); // nie hamuj + VelDesired = std::floor( + min_speed( + VelDesired, + ( Obstacle.distance > 150 ? + k + 20.0: + std::min( 8.0, k + 4.0 ) ) ) ); + VelNext = std::floor( std::min( 8.0, k + 2.0 ) ); // i pakuj się na tamtego + } + else { + // a normalnie to hamować + VelNext = 0.0; + if( Obstacle.distance <= fMinProximityDist ) { + VelDesired = 0.0; + } + + if( ( mvOccupied->CategoryFlag & 1 ) + && ( OrderCurrentGet() & Obey_train ) ) { + // trains which move normally should try to stop at safe margin + ActualProximityDist -= fDriverDist; + } + } + } + else { + // jeśli oba jadą, to przyhamuj lekko i ogranicz prędkość + if( Obstacle.distance < ( + ( mvOccupied->CategoryFlag & 2 ) ? + fMaxProximityDist + 0.5 * vel : // cars + 2.0 * fMaxProximityDist + 2.0 * vel ) ) { //others + // jak tamten jedzie wolniej a jest w drodze hamowania + AccPreferred = std::min( -0.9, AccPreferred ); + VelNext = min_speed( std::round( k ) - 5.0, VelDesired ); + if( Obstacle.distance <= ( + ( mvOccupied->CategoryFlag & 2 ) ? + fMaxProximityDist : // cars + 2.0 * fMaxProximityDist ) ) { //others + // try to force speed change if obstacle is really close + VelDesired = VelNext; + } + } + } + ReactionTime = ( + mvOccupied->Vel > 0.01 ? + 0.1 : // orientuj się, bo jest goraco + 2.0 ); // we're already standing still, so take it easy + } + else { + if( OrderCurrentGet() & Connect ) { + // if there's something nearby in the connect mode don't speed up too much VelDesired = min_speed( VelDesired, - std::max( - k, - ( mvOccupied->CategoryFlag & 2 ) ? - 40.0 : // cars - 20.0 ) ); // others - if( vel > VelDesired + fVelPlus ) { - // if going too fast force some prompt braking - AccPreferred = std::min( -0.65, AccPreferred ); - } - } - - double const distance = vehicle->fTrackBlock - fMaxProximityDist - ( fBrakeDist * 1.15 ); // odległość bezpieczna zależy od prędkości - if( distance < 0.0 ) { - // jeśli odległość jest zbyt mała - if( k < 10.0 ) // k - prędkość tego z przodu - { // jeśli tamten porusza się z niewielką prędkością albo stoi - if( OrderCurrentGet() & Connect ) { - // jeśli spinanie, to jechać dalej - AccPreferred = std::min( 0.35, AccPreferred ); // nie hamuj - VelDesired = - min_speed( - VelDesired, - ( vehicle->fTrackBlock > 150.0 ? - 20.0: - 4.0 ) ); - VelNext = 2.0; // i pakuj się na tamtego - } - else { - // a normalnie to hamować - VelNext = 0.0; - if( vehicle->fTrackBlock <= fMinProximityDist ) { - VelDesired = 0.0; - } - - if( ( mvOccupied->CategoryFlag & 1 ) - && ( OrderCurrentGet() & Obey_train ) ) { - // trains which move normally should try to stop at safe margin - ActualProximityDist -= fDriverDist; - } - } - } - else { - // jeśli oba jadą, to przyhamuj lekko i ogranicz prędkość - if( vehicle->fTrackBlock < ( - ( mvOccupied->CategoryFlag & 2 ) ? - fMaxProximityDist + 0.5 * vel : // cars - 2.0 * fMaxProximityDist + 2.0 * vel ) ) { //others - // jak tamten jedzie wolniej a jest w drodze hamowania - AccPreferred = std::min( -0.9, AccPreferred ); - VelNext = min_speed( std::round( k ) - 5.0, VelDesired ); - if( vehicle->fTrackBlock <= ( - ( mvOccupied->CategoryFlag & 2 ) ? - fMaxProximityDist : // cars - 2.0 * fMaxProximityDist ) ) { //others - // try to force speed change if obstacle is really close - VelDesired = VelNext; - } - } - } - ReactionTime = ( - mvOccupied->Vel > 0.01 ? - 0.1 : // orientuj się, bo jest goraco - 2.0 ); // we're already standing still, so take it easy - } - else { - if( OrderCurrentGet() & Connect ) { - // if there's something nearby in the connect mode don't speed up too much - VelDesired = - min_speed( - VelDesired, - ( vehicle->fTrackBlock > 100.0 ? - 20.0 : - 4.0 ) ); - } + ( Obstacle.distance > 100 ? + 20.0 : + 4.0 ) ); } } } @@ -4941,7 +5084,7 @@ TController::UpdateSituation(double dt) { } } - if( ( OrderCurrentGet() & ( Shunt | Obey_train ) ) != 0 ) { + if( ( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) != 0 ) { // w Connect nie, bo moveStopHere odnosi się do stanu po połączeniu if( ( ( iDrivigFlags & moveStopHere ) != 0 ) && ( vel < 0.01 ) @@ -5045,7 +5188,7 @@ TController::UpdateSituation(double dt) { if( mvOccupied->CategoryFlag & 1 ) { // trains if( ( OrderCurrentGet() & ( Shunt | Connect ) ) - && ( pVehicles[0]->fTrackBlock < 50.0 ) ) { + && ( Obstacle.distance < 50 ) ) { // crude detection of edge case, if approaching another vehicle coast slowly until min distance // this should allow to bunch up trainsets more on sidings VelDesired = min_speed( 5.0, VelDesired ); @@ -5411,7 +5554,7 @@ TController::UpdateSituation(double dt) { // wyluzuj lokomotywę - może być więcej! mvOccupied->BrakeReleaser( 1 ); } - else if( OrderList[ OrderPos ] != Disconnect ) { + else if( OrderCurrentGet() != Disconnect ) { // przy odłączaniu nie zwalniamy tu hamulca if( ( fAccGravity * fAccGravity < 0.001 ? true : @@ -5484,7 +5627,7 @@ TController::UpdateSituation(double dt) { // hamować bardziej, gdy aktualne opóźnienie hamowania mniejsze niż (AccDesired) IncBrake(); } - else if( OrderList[ OrderPos ] != Disconnect ) { + else if( OrderCurrentGet() != Disconnect ) { // przy odłączaniu nie zwalniamy tu hamulca if( AbsAccS < AccDesired - 0.05 ) { // jeśli opóźnienie większe od wymaganego (z histerezą) luzowanie, gdy za dużo @@ -5529,7 +5672,7 @@ TController::UpdateSituation(double dt) { if ((AccDesired < fAccGravity - 0.05) && (AbsAccS < AccDesired - fBrake_a1[0]*0.51)) { // jak hamuje, to nie tykaj kranu za często // yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2 - if( OrderList[ OrderPos ] != Disconnect ) { + if( OrderCurrentGet() != Disconnect ) { // przy odłączaniu nie zwalniamy tu hamulca DecBrake(); // tutaj zmniejszało o 1 przy odczepianiu } @@ -5572,7 +5715,7 @@ TController::UpdateSituation(double dt) { } if (AIControllFlag) { // odhamowywanie składu po zatrzymaniu i zabezpieczanie lokomotywy - if( ( ( OrderList[ OrderPos ] & ( Disconnect | Connect ) ) == 0 ) + if( ( ( OrderCurrentGet() & ( Disconnect | Connect ) ) == 0 ) && ( std::abs( fAccGravity ) < 0.01 ) ) { // przy (p)odłączaniu nie zwalniamy tu hamulca // only do this on flats, on slopes keep applied the train brake @@ -5653,9 +5796,7 @@ void TController::JumpToNextOrder() if (OrderList[OrderPos] & Change_direction) // jeśli zmiana kierunku if (OrderList[OrderPos] != Change_direction) // ale nałożona na coś { - OrderList[OrderPos] = - TOrders(OrderList[OrderPos] & - ~Change_direction); // usunięcie zmiany kierunku z innej komendy + OrderList[OrderPos] = TOrders(OrderList[OrderPos] & ~Change_direction); // usunięcie zmiany kierunku z innej komendy OrderCheck(); return; } @@ -5686,14 +5827,14 @@ void TController::JumpToFirstOrder() void TController::OrderCheck() { // reakcja na zmianę rozkazu - if( OrderList[ OrderPos ] != Obey_train ) { + if( OrderCurrentGet() != Obey_train ) { // reset light hints m_lighthints[ end::front ] = m_lighthints[ end::rear ] = -1; } - if( OrderList[ OrderPos ] & ( Shunt | Connect | Obey_train ) ) { + if( OrderCurrentGet() & ( Shunt | Loose_shunt | Connect | Obey_train | Bank ) ) { CheckVehicles(); // sprawdzić światła } - if( OrderList[ OrderPos ] & ( Shunt | Connect ) ) { + if( OrderCurrentGet() & ( Shunt | Loose_shunt | Connect ) ) { // HACK: ensure consist doors will be closed on departure iDrivigFlags |= moveDoorOpened; } @@ -5718,7 +5859,7 @@ void TController::OrderNext(TOrders NewOrder) OrderTop = OrderPos; // ale może jest czymś zajęty na razie if (NewOrder >= Shunt) // jeśli ma jechać { // ale może być zajęty chwilowymi operacjami - while (OrderList[OrderTop] ? OrderList[OrderTop] < Shunt : false) // jeśli coś robi + while ((OrderList[OrderTop] != Wait_for_orders) && (OrderList[OrderTop] < Shunt)) // jeśli coś robi ++OrderTop; // pomijamy wszystkie tymczasowe prace } else @@ -5763,16 +5904,6 @@ void TController::OrdersDump() } }; -inline TOrders TController::OrderCurrentGet() -{ - return OrderList[OrderPos]; -} - -inline TOrders TController::OrderNextGet() -{ - return OrderList[OrderPos + 1]; -} - void TController::OrdersInit(double fVel) { // wypełnianie tabelki rozkazów na podstawie rozkładu // ustawienie kolejności komend, niezależnie kto prowadzi @@ -6013,7 +6144,7 @@ TCommandType TController::BackwardScan() // dzięki temu będzie można stawać za wskazanym sygnalizatorem, a zwłaszcza jeśli będzie jazda na kozioł // ograniczenia prędkości nie są wtedy istotne, również koniec toru jest do niczego nie przydatny // zwraca true, jeśli należy odwrócić kierunek jazdy pojazdu - if( ( OrderList[ OrderPos ] & ~( Shunt | Connect ) ) ) { + if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Connect ) ) ) { // skanowanie sygnałów tylko gdy jedzie w trybie manewrowym albo czeka na rozkazy return TCommandType::cm_Unknown; } @@ -6078,14 +6209,14 @@ TCommandType TController::BackwardScan() bool move = false; // czy AI w trybie manewerowym ma dociągnąć pod S1 if( e->input_command() == TCommandType::cm_SetVelocity ) { if( ( vmechmax == 0.0 ) ? - ( OrderCurrentGet() & ( Shunt | Connect ) ) : + ( OrderCurrentGet() & ( Shunt | Loose_shunt | Connect ) ) : ( OrderCurrentGet() & Connect ) ) { // przy podczepianiu ignorować wyjazd? move = true; // AI w trybie manewerowym ma dociągnąć pod S1 } else { if( ( scandist > fMinProximityDist ) && ( ( mvOccupied->Vel > 0.0 ) - && ( OrderCurrentGet() != Shunt ) ) ) { + && ( ( OrderCurrentGet() & ( Shunt | Loose_shunt ) ) == 0 ) ) ) { // jeśli semafor jest daleko, a pojazd jedzie, to informujemy o zmianie prędkości // jeśli jedzie manewrowo, musi dostać SetVelocity, żeby sie na pociągowy przełączył #if LOGBACKSCAN @@ -6117,14 +6248,13 @@ TCommandType TController::BackwardScan() } } } - if (OrderCurrentGet() ? OrderCurrentGet() & (Shunt | Connect) : + if (OrderCurrentGet() ? OrderCurrentGet() & (Shunt | Loose_shunt | Connect) : true) // w Wait_for_orders też widzi tarcze { // reakcja AI w trybie manewrowym dodatkowo na sygnały manewrowe if (move ? true : e->input_command() == TCommandType::cm_ShuntVelocity) { // jeśli powyżej było SetVelocity 0 0, to dociągamy pod S1 - if ((scandist > fMinProximityDist) ? - (mvOccupied->Vel > 0.0) || (vmechmax == 0.0) : - false) + if ((scandist > fMinProximityDist) && + (mvOccupied->Vel > 0.0) || (vmechmax == 0.0) ) { // jeśli tarcza jest daleko, to: //- jesli pojazd jedzie, to informujemy o zmianie prędkości //- jeśli stoi, to z własnej inicjatywy może podjechać pod zamkniętą @@ -6235,11 +6365,13 @@ void TController::TakeControl(bool yes) if (OrderCurrentGet()) // jeśli coś robi PrepareEngine(); // niech sprawdzi stan silnika else // jeśli nic nie robi - if (pVehicle->iLights[mvOccupied->CabNo < 0 ? 1 : 0] & - 21) // któreś ze świateł zapalone? + if (pVehicle->iLights[ ( mvOccupied->CabNo < 0 ? + end::rear : + end::front ) ] + & (light::headlight_left | light::headlight_right | light::headlight_upper)) // któreś ze świateł zapalone? { // od wersji 357 oczekujemy podania komend dla AI przez scenerię OrderNext(Prepare_engine); - if (pVehicle->iLights[mvOccupied->CabNo < 0 ? 1 : 0] & 4) // górne światło zapalone + if (pVehicle->iLights[mvOccupied->CabNo < 0 ? end::rear : end::front] & light::headlight_upper) // górne światło zapalone OrderNext(Obey_train); // jazda pociągowa else OrderNext(Shunt); // jazda manewrowa @@ -6248,27 +6380,8 @@ void TController::TakeControl(bool yes) else iDrivigFlags |= moveStopHere; // a jak stoi, to niech czeka } - /* od wersji 357 oczekujemy podania komend dla AI przez scenerię - if (OrderCurrentGet()) - {if (OrderCurrentGet()iLights[mvOccupied->CabNo<0?1:0]&4) //górne światło - OrderNext(Obey_train); //jazda pociągowa - else - OrderNext(Shunt); //jazda manewrowa - } - } - else //jeśli jest w stanie Wait_for_orders - JumpToFirstOrder(); //uruchomienie? - // czy dac ponizsze? to problematyczne - //SetVelocity(pVehicle->GetVelocity(),-1); //utrzymanie dotychczasowej? - if (pVehicle->GetVelocity()>0.0) - SetVelocity(-1,-1); //AI ustali sobie odpowiednią prędkość - */ - // Activation(); //przeniesie użytkownika w ostatnio wybranym kierunku CheckVehicles(); // ustawienie świateł - TableClear(); // ponowne utworzenie tabelki, bo człowiek mógł pojechać niezgodnie z - // sygnałami + TableClear(); // ponowne utworzenie tabelki, bo człowiek mógł pojechać niezgodnie z sygnałami } else { // a teraz użytkownik @@ -6329,7 +6442,7 @@ bool TController::IsStop() const double TController::TrackBlock() const { - return pVehicles[ end::front ]->fTrackBlock; + return Obstacle.distance; } void TController::MoveTo(TDynamicObject *to) diff --git a/Driver.h b/Driver.h index eb822348..4b407a74 100644 --- a/Driver.h +++ b/Driver.h @@ -19,15 +19,18 @@ enum TOrders { // rozkazy dla AI Wait_for_orders = 0, // czekanie na dostarczenie następnych rozkazów // operacje tymczasowe - Prepare_engine = 1, // włączenie silnika - Release_engine = 2, // wyłączenie silnika - Change_direction = 4, // zmiana kierunku (bez skanowania sygnalizacji) - Connect = 8, // podłączanie wagonów (z częściowym skanowaniem sygnalizacji) - Disconnect = 0x10, // odłączanie wagonów (bez skanowania sygnalizacji) + Prepare_engine = 1 << 0, // włączenie silnika + Release_engine = 1 << 1, // wyłączenie silnika + Change_direction = 1 << 2, // zmiana kierunku (bez skanowania sygnalizacji) + Connect = 1 << 3, // podłączanie wagonów (z częściowym skanowaniem sygnalizacji) + Disconnect = 1 << 4, // odłączanie wagonów (bez skanowania sygnalizacji) // jazda - Shunt = 0x20, // tryb manewrowy - Obey_train = 0x40, // tryb pociągowy - Jump_to_first_order = 0x60 // zapęlenie do pierwszej pozycji (po co?) + Shunt = 1 << 5, // tryb manewrowy + Loose_shunt = 1 << 6, // coupling-free shunting mode + Obey_train = 1 << 7, // tryb pociągowy + Bank = 1 << 8, // assist mode + // others + Jump_to_first_order = 1 << 9 // zapęlenie do pierwszej pozycji (po co?) }; enum TMovementStatus @@ -193,6 +196,7 @@ public: TMoverParameters const *Controlling() const { return mvControlling; } void DirectionInitial(); + void DirectionChange(); inline int Direction() const { return iDirection; } @@ -207,6 +211,10 @@ private: bool DecBrake(); bool IncSpeed(); bool DecSpeed(bool force = false); + bool IncBrakeEIM(); + bool DecBrakeEIM(); + bool IncSpeedEIM(); + bool DecSpeedEIM(); void SpeedSet(); void SpeedCntrl(double DesiredSpeed); double ESMVelocity(bool Main); @@ -308,21 +316,25 @@ private: public: void PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation, TStopReason reason = stopComm); bool PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason = stopComm ); + // defines assignment data + inline auto assignment() -> std::string & { return m_assignment; } + inline auto assignment() const -> std::string const & { return m_assignment; } + std::string OrderCurrent() const; private: void RecognizeCommand(); // odczytuje komende przekazana lokomotywie void JumpToNextOrder(); void JumpToFirstOrder(); void OrderPush(TOrders NewOrder); void OrderNext(TOrders NewOrder); - inline TOrders OrderCurrentGet(); - inline TOrders OrderNextGet(); + inline TOrders OrderCurrentGet() const; + inline TOrders OrderNextGet() const; void OrderCheck(); void OrdersInit(double fVel); void OrdersClear(); void OrdersDump(); - std::string OrderCurrent() const; std::string Order2Str(TOrders Order) const; // members + std::string m_assignment; Math3D::vector3 vCommandLocation; // polozenie wskaznika, sygnalizatora lub innego obiektu do ktorego odnosi sie komenda // NOTE: not used TOrders OrderList[ maxorders ]; // lista rozkazów int OrderPos = 0, @@ -376,6 +388,7 @@ private: std::size_t SemNextStopIndex{ std::size_t( -1 ) }; double dMoveLen = 0.0; // odległość przejechana od ostatniego sprawdzenia tabelki basic_event *eSignNext = nullptr; // sygnał zmieniający prędkość, do pokazania na [F2] + neighbour_data Obstacle; // nearest vehicle detected ahead on current route // timetable // methods @@ -448,3 +461,11 @@ private: */ }; + +inline TOrders TController::OrderCurrentGet() const { + return OrderList[ OrderPos ]; +} + +inline TOrders TController::OrderNextGet() const { + return OrderList[ OrderPos + 1 ]; +} diff --git a/DynObj.cpp b/DynObj.cpp index 83c7c804..7c039229 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -173,24 +173,24 @@ TDynamicObject * TDynamicObject::FirstFind(int &coupler_nr, int cf) return NULL; // Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów if ((temp->MoverParameters->Couplers[coupler_nr].CouplingFlag & cf) != cf) return temp; // nic nie ma już dalej podłączone sprzęgiem cf - if (coupler_nr == 0) + if (coupler_nr == end::front) { // jeżeli szukamy od sprzęgu 0 - if (temp->PrevConnected) // jeśli mamy coś z przodu + if (temp->PrevConnected()) // jeśli mamy coś z przodu { - if (temp->PrevConnectedNo == 0) // jeśli pojazd od strony sprzęgu 0 jest odwrócony + if (temp->PrevConnectedNo() == end::front) // jeśli pojazd od strony sprzęgu 0 jest odwrócony coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu - temp = temp->PrevConnected; // ten jest od strony 0 + temp = temp->PrevConnected(); // ten jest od strony 0 } else return temp; // jeśli jednak z przodu nic nie ma } else { - if (temp->NextConnected) + if (temp->NextConnected()) { - if (temp->NextConnectedNo == 1) // jeśli pojazd od strony sprzęgu 1 jest odwrócony + if (temp->NextConnectedNo() == end::rear) // jeśli pojazd od strony sprzęgu 1 jest odwrócony coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu - temp = temp->NextConnected; // ten pojazd jest od strony 1 + temp = temp->NextConnected(); // ten pojazd jest od strony 1 } else return temp; // jeśli jednak z tyłu nic nie ma @@ -201,8 +201,7 @@ TDynamicObject * TDynamicObject::FirstFind(int &coupler_nr, int cf) //--------------------------------------------------------------------------- float TDynamicObject::GetEPP() -{ // szukanie skrajnego połączonego pojazdu w - // pociagu +{ // szukanie skrajnego połączonego pojazdu w pociagu // od strony sprzegu (coupler_nr) obiektu (start) TDynamicObject *temp = this; int coupler_nr = 0; @@ -215,26 +214,26 @@ float TDynamicObject::GetEPP() break; // Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów eq += temp->MoverParameters->PipePress * temp->MoverParameters->Dim.L; am += temp->MoverParameters->Dim.L; - if ((temp->MoverParameters->Couplers[coupler_nr].CouplingFlag & 2) != 2) + if ((temp->MoverParameters->Couplers[coupler_nr].CouplingFlag & coupling::brakehose) != coupling::brakehose) break; // nic nie ma już dalej podłączone if (coupler_nr == 0) { // jeżeli szukamy od sprzęgu 0 - if (temp->PrevConnected) // jeśli mamy coś z przodu + if (temp->PrevConnected()) // jeśli mamy coś z przodu { - if (temp->PrevConnectedNo == 0) // jeśli pojazd od strony sprzęgu 0 jest odwrócony + if (temp->PrevConnectedNo() == end::front) // jeśli pojazd od strony sprzęgu 0 jest odwrócony coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu - temp = temp->PrevConnected; // ten jest od strony 0 + temp = temp->PrevConnected(); // ten jest od strony 0 } else break; // jeśli jednak z przodu nic nie ma } else { - if (temp->NextConnected) + if (temp->NextConnected()) { - if (temp->NextConnectedNo == 1) // jeśli pojazd od strony sprzęgu 1 jest odwrócony + if (temp->NextConnectedNo() == end::rear) // jeśli pojazd od strony sprzęgu 1 jest odwrócony coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu - temp = temp->NextConnected; // ten pojazd jest od strony 1 + temp = temp->NextConnected(); // ten pojazd jest od strony 1 } else break; // jeśli jednak z tyłu nic nie ma @@ -249,26 +248,26 @@ float TDynamicObject::GetEPP() break; // Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów eq += temp->MoverParameters->PipePress * temp->MoverParameters->Dim.L; am += temp->MoverParameters->Dim.L; - if ((temp->MoverParameters->Couplers[coupler_nr].CouplingFlag & 2) != 2) + if ((temp->MoverParameters->Couplers[coupler_nr].CouplingFlag & coupling::brakehose) != coupling::brakehose) break; // nic nie ma już dalej podłączone if (coupler_nr == 0) { // jeżeli szukamy od sprzęgu 0 - if (temp->PrevConnected) // jeśli mamy coś z przodu + if (temp->PrevConnected()) // jeśli mamy coś z przodu { - if (temp->PrevConnectedNo == 0) // jeśli pojazd od strony sprzęgu 0 jest odwrócony + if (temp->PrevConnectedNo() == end::front) // jeśli pojazd od strony sprzęgu 0 jest odwrócony coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu - temp = temp->PrevConnected; // ten jest od strony 0 + temp = temp->PrevConnected(); // ten jest od strony 0 } else break; // jeśli jednak z przodu nic nie ma } else { - if (temp->NextConnected) + if (temp->NextConnected()) { - if (temp->NextConnectedNo == 1) // jeśli pojazd od strony sprzęgu 1 jest odwrócony + if (temp->NextConnectedNo() == end::rear) // jeśli pojazd od strony sprzęgu 1 jest odwrócony coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu - temp = temp->NextConnected; // ten pojazd jest od strony 1 + temp = temp->NextConnected(); // ten pojazd jest od strony 1 } else break; // jeśli jednak z tyłu nic nie ma @@ -287,40 +286,6 @@ TDynamicObject * TDynamicObject::GetFirstDynamic(int cpl_type, int cf) return FirstFind(cpl_type, cf); // używa referencji }; -/* -TDynamicObject* TDynamicObject::GetFirstCabDynamic(int cpl_type) -{//ZiomalCl: szukanie skrajnego obiektu z kabiną - TDynamicObject* temp=this; - int coupler_nr=cpl_type; - for (int i=0;i<300;i++) //ograniczenie do 300 na wypadek zapętlenia składu - { - if (!temp) - return NULL; //Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów - if (temp->MoverParameters->CabNo!=0&&temp->MoverParameters->SandCapacity!=0) - return temp; //nic nie ma już dalej podłączone - if (temp->MoverParameters->Couplers[coupler_nr].CouplingFlag==0) - return NULL; - if (coupler_nr==0) - {//jeżeli szukamy od sprzęgu 0 - if (temp->PrevConnectedNo==0) //jeśli pojazd od strony sprzęgu 0 jest -odwrócony - coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu - if (temp->PrevConnected) - temp=temp->PrevConnected; //ten jest od strony 0 - } - else - { - if (temp->NextConnectedNo==1) //jeśli pojazd od strony sprzęgu 1 jest -odwrócony - coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu - if (temp->NextConnected) - temp=temp->NextConnected; //ten pojazd jest od strony 1 - } - } - return NULL; //to tylko po wyczerpaniu pętli -}; -*/ - void TDynamicObject::ABuSetModelShake( Math3D::vector3 mShake ) { modelShake = mShake; @@ -370,11 +335,11 @@ void TDynamicObject::SetPneumatic(bool front, bool red) tamten = 0; ten = GetPneumatic(front, red); // 1=lewy skos,2=prawy skos,3=dwa proste if (front) - if (PrevConnected) // pojazd od strony sprzęgu 0 - tamten = PrevConnected->GetPneumatic((PrevConnectedNo == 0 ? true : false), red); + if (PrevConnected()) // pojazd od strony sprzęgu 0 + tamten = PrevConnected()->GetPneumatic((PrevConnectedNo() == end::front ? true : false), red); if (!front) - if (NextConnected) // pojazd od strony sprzęgu 1 - tamten = NextConnected->GetPneumatic((NextConnectedNo == 0 ? true : false), red); + if (NextConnected()) // pojazd od strony sprzęgu 1 + tamten = NextConnected()->GetPneumatic((NextConnectedNo() == end::front ? true : false), red); if (ten == tamten) // jeśli układ jest symetryczny switch (ten) { @@ -385,7 +350,7 @@ void TDynamicObject::SetPneumatic(bool front, bool red) x = 3; break; // mamy prawy skos, dać prawe skosy case 3: // wszystkie cztery na prosto - if (MoverParameters->Couplers[front ? 0 : 1].Render) + if (MoverParameters->Couplers[front ? end::front : end::rear].Render) x = 1; else x = 4; @@ -696,15 +661,15 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) // ABu-240105: Dodatkowy warunek: if (...).Render, zeby rysowal tylko // jeden // z polaczonych sprzegow - if ((TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_coupler)) && - (MoverParameters->Couplers[0].Render)) + if ((TestFlag(MoverParameters->Couplers[end::front].CouplingFlag, ctrain_coupler)) && + (MoverParameters->Couplers[end::front].Render)) { btCoupler1.Turn( true ); btnOn = true; } // else btCoupler1.TurnOff(); - if ((TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_coupler)) && - (MoverParameters->Couplers[1].Render)) + if ((TestFlag(MoverParameters->Couplers[end::rear].CouplingFlag, ctrain_coupler)) && + (MoverParameters->Couplers[end::rear].Render)) { btCoupler2.Turn( true ); btnOn = true; @@ -722,7 +687,7 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) SetPneumatic(true, true); // ktore z nich nalezy SetPneumatic(false, true); // wyswietlic w tej klatce - if (TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_pneumatic)) + if (TestFlag(MoverParameters->Couplers[end::front].CouplingFlag, ctrain_pneumatic)) { switch (cp1) { @@ -742,7 +707,7 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) btnOn = true; } - if (TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_pneumatic)) + if (TestFlag(MoverParameters->Couplers[end::rear].CouplingFlag, ctrain_pneumatic)) { switch (cp2) { @@ -763,7 +728,7 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) } // przewody zasilajace, j.w. (yB) - if (TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_scndpneumatic)) + if (TestFlag(MoverParameters->Couplers[end::front].CouplingFlag, ctrain_scndpneumatic)) { switch (sp1) { @@ -783,7 +748,7 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) btnOn = true; } - if (TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_scndpneumatic)) + if (TestFlag(MoverParameters->Couplers[end::rear].CouplingFlag, ctrain_scndpneumatic)) { switch (sp2) { @@ -808,18 +773,18 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) { // przewody powietrzne j.w., ABu: decyzja czy rysowac tylko na podstawie // 'render' - if (TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_pneumatic)) + if (TestFlag(MoverParameters->Couplers[end::front].CouplingFlag, ctrain_pneumatic)) { - if (MoverParameters->Couplers[0].Render) + if (MoverParameters->Couplers[end::front].Render) btCPneumatic1.TurnOn(); else btCPneumatic1r.TurnOn(); btnOn = true; } - if (TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_pneumatic)) + if (TestFlag(MoverParameters->Couplers[end::rear].CouplingFlag, ctrain_pneumatic)) { - if (MoverParameters->Couplers[1].Render) + if (MoverParameters->Couplers[end::rear].Render) btCPneumatic2.TurnOn(); else btCPneumatic2r.TurnOn(); @@ -1213,7 +1178,7 @@ void TDynamicObject::ABuCheckMyTrack() // Ra: w poniższej funkcji jest problem ze sprzęgami TDynamicObject * -TDynamicObject::ABuFindObject( int &Foundcoupler, double &Distance, TTrack const *Track, int const Direction, int const Mycoupler ) +TDynamicObject::ABuFindObject( int &Foundcoupler, double &Distance, TTrack const *Track, int const Direction, int const Mycoupler ) const { // Zwraca wskaźnik najbliższego obiektu znajdującego się // na torze w określonym kierunku, ale tylko wtedy, kiedy // obiekty mogą się zderzyć, tzn. nie mijają się. @@ -1346,6 +1311,14 @@ int TDynamicObject::Dettach(int dir) if( MoverParameters->Couplers[ dir ].CouplingFlag ) { // odczepianie, o ile coś podłączone MoverParameters->Dettach( dir ); +/* + if( true == MoverParameters->Dettach( dir ) ) { + auto *othervehicle { dir ? NextConnected : PrevConnected }; + auto const othercoupler { dir ? NextConnectedNo() : PrevConnectedNo() }; + ( othercoupler ? othervehicle->NextConnected : othervehicle->PrevConnected ) = nullptr; + ( dir ? NextConnected : PrevConnected ) = nullptr; + }; +*/ } // sprzęg po rozłączaniu (czego się nie da odpiąć return MoverParameters->Couplers[dir].CouplingFlag; @@ -1354,16 +1327,23 @@ int TDynamicObject::Dettach(int dir) void TDynamicObject::couple( int const Side ) { - if( MoverParameters->Couplers[ Side ].Connected == nullptr ) { return; } + auto const &neighbour { MoverParameters->Neighbours[ Side ] }; - if( MoverParameters->Couplers[ Side ].CouplingFlag == coupling::faux ) { + if( neighbour.vehicle == nullptr ) { return; } + + auto const &coupler { MoverParameters->Couplers[ Side ] }; + auto *othervehicle { neighbour.vehicle }; + auto *othervehicleparams{ othervehicle->MoverParameters }; + auto const &othercoupler { othervehicleparams->Couplers[ Side ] }; + + if( coupler.CouplingFlag == coupling::faux ) { // najpierw hak - if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag - & MoverParameters->Couplers[ Side ].AllowedFlag + if( ( coupler.AllowedFlag + & othercoupler.AllowedFlag & coupling::coupler ) == coupling::coupler ) { if( MoverParameters->Attach( - Side, 2, - MoverParameters->Couplers[ Side ].Connected, + Side, neighbour.vehicle_end, + othervehicleparams, coupling::coupler ) ) { // one coupling type per key press return; @@ -1375,20 +1355,15 @@ TDynamicObject::couple( int const Side ) { } if( false == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::brakehose ) ) { // pneumatyka - if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag - & MoverParameters->Couplers[ Side ].AllowedFlag + if( ( coupler.AllowedFlag + & othercoupler.AllowedFlag & coupling::brakehose ) == coupling::brakehose ) { if( MoverParameters->Attach( - Side, 2, - MoverParameters->Couplers[ Side ].Connected, - ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::brakehose ) ) ) { + Side, neighbour.vehicle_end, + othervehicleparams, + ( coupler.CouplingFlag | coupling::brakehose ) ) ) { SetPneumatic( Side != 0, true ); - if( Side == end::front ) { - PrevConnected->SetPneumatic( Side != 0, true ); - } - else { - NextConnected->SetPneumatic( Side != 0, true ); - } + othervehicle->SetPneumatic( Side != 0, true ); // one coupling type per key press return; } @@ -1396,20 +1371,15 @@ TDynamicObject::couple( int const Side ) { } if( false == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::mainhose ) ) { // zasilajacy - if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag - & MoverParameters->Couplers[ Side ].AllowedFlag + if( ( coupler.AllowedFlag + & othercoupler.AllowedFlag & coupling::mainhose ) == coupling::mainhose ) { if( MoverParameters->Attach( - Side, 2, - MoverParameters->Couplers[ Side ].Connected, - ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::mainhose ) ) ) { + Side, neighbour.vehicle_end, + othervehicleparams, + ( coupler.CouplingFlag | coupling::mainhose ) ) ) { SetPneumatic( Side != 0, false ); - if( Side == end::front ) { - PrevConnected->SetPneumatic( Side != 0, false ); - } - else { - NextConnected->SetPneumatic( Side != 0, false ); - } + othervehicle->SetPneumatic( Side != 0, false ); // one coupling type per key press return; } @@ -1417,13 +1387,13 @@ TDynamicObject::couple( int const Side ) { } if( false == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::control ) ) { // ukrotnionko - if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag - & MoverParameters->Couplers[ Side ].AllowedFlag + if( ( coupler.AllowedFlag + & othercoupler.AllowedFlag & coupling::control ) == coupling::control ) { if( MoverParameters->Attach( - Side, 2, - MoverParameters->Couplers[ Side ].Connected, - ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::control ) ) ) { + Side, neighbour.vehicle_end, + othervehicleparams, + ( coupler.CouplingFlag | coupling::control ) ) ) { // one coupling type per key press return; } @@ -1431,13 +1401,13 @@ TDynamicObject::couple( int const Side ) { } if( false == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::gangway ) ) { // mostek - if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag - & MoverParameters->Couplers[ Side ].AllowedFlag + if( ( coupler.AllowedFlag + & othercoupler.AllowedFlag & coupling::gangway ) == coupling::gangway ) { if( MoverParameters->Attach( - Side, 2, - MoverParameters->Couplers[ Side ].Connected, - ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::gangway ) ) ) { + Side, neighbour.vehicle_end, + othervehicleparams, + ( coupler.CouplingFlag | coupling::gangway ) ) ) { // one coupling type per key press return; } @@ -1445,13 +1415,13 @@ TDynamicObject::couple( int const Side ) { } if( false == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::heating ) ) { // heating - if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag - & MoverParameters->Couplers[ Side ].AllowedFlag + if( ( coupler.AllowedFlag + & othercoupler.AllowedFlag & coupling::heating ) == coupling::heating ) { if( MoverParameters->Attach( - Side, 2, - MoverParameters->Couplers[ Side ].Connected, - ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::heating ) ) ) { + Side, neighbour.vehicle_end, + othervehicleparams, + ( coupler.CouplingFlag | coupling::heating ) ) ) { // one coupling type per key press return; } @@ -1473,226 +1443,9 @@ TDynamicObject::uncouple( int const Side ) { return couplingflag; } -void TDynamicObject::CouplersDettach(double MinDist, int MyScanDir) { - // funkcja rozłączajaca podłączone sprzęgi, jeśli odległość przekracza (MinDist) - // MinDist - dystans minimalny, dla ktorego mozna rozłączać - if (MyScanDir > 0) { - // pojazd od strony sprzęgu 0 - if( ( PrevConnected != nullptr ) - && ( MoverParameters->Couplers[ end::front ].CoupleDist > MinDist ) ) { - // sprzęgi wirtualne zawsze przekraczają - if( ( PrevConnectedNo == end::front ? - PrevConnected->PrevConnected : - PrevConnected->NextConnected ) - == this ) { - // Ra: nie rozłączamy znalezionego, jeżeli nie do nas podłączony - // (może jechać w innym kierunku) - PrevConnected->MoverParameters->Couplers[PrevConnectedNo].Connected = nullptr; - if( PrevConnectedNo == end::front ) { - // sprzęg 0 nie podłączony - PrevConnected->PrevConnectedNo = 2; - PrevConnected->PrevConnected = nullptr; - } - else if( PrevConnectedNo == end::rear ) { - // sprzęg 1 nie podłączony - PrevConnected->NextConnectedNo = 2; - PrevConnected->NextConnected = nullptr; - } - } - // za to zawsze odłączamy siebie - PrevConnected = nullptr; - PrevConnectedNo = 2; // sprzęg 0 nie podłączony - MoverParameters->Couplers[ end::front ].Connected = nullptr; - } - } - else { - // pojazd od strony sprzęgu 1 - if( ( NextConnected != nullptr ) - && ( MoverParameters->Couplers[ end::rear ].CoupleDist > MinDist ) ) { - // sprzęgi wirtualne zawsze przekraczają - if( ( NextConnectedNo == end::front ? - NextConnected->PrevConnected : - NextConnected->NextConnected ) - == this) { - // Ra: nie rozłączamy znalezionego, jeżeli nie do nas podłączony - // (może jechać w innym kierunku) - NextConnected->MoverParameters->Couplers[ NextConnectedNo ].Connected = nullptr; - if( NextConnectedNo == end::front ) { - // sprzęg 0 nie podłączony - NextConnected->PrevConnectedNo = 2; - NextConnected->PrevConnected = nullptr; - } - else if( NextConnectedNo == end::rear ) { - // sprzęg 1 nie podłączony - NextConnected->NextConnectedNo = 2; - NextConnected->NextConnected = nullptr; - } - } - // za to zawsze odłączamy siebie - NextConnected = nullptr; - NextConnectedNo = 2; // sprzęg 1 nie podłączony - MoverParameters->Couplers[1].Connected = nullptr; - } - } -} - -void TDynamicObject::ABuScanObjects( int Direction, double Distance ) -{ // skanowanie toru w poszukiwaniu kolidujących pojazdów - // ScanDir - określa kierunek poszukiwania zależnie od zwrotu prędkości - // pojazdu - // ScanDir=1 - od strony Coupler0, ScanDir=-1 - od strony Coupler1 - auto const initialdirection = Direction; // zapamiętanie kierunku poszukiwań na torze początkowym, względem sprzęgów - - TTrack const *track = RaTrackGet(); - if( RaDirectionGet() < 0 ) { - // czy oś jest ustawiona w stronę Point1? - Direction = -Direction; - } - - // (teraz względem toru) - int const mycoupler = ( initialdirection < 0 ? 1 : 0 ); // numer sprzęgu do podłączenia w obiekcie szukajacym - int foundcoupler { -1 }; // numer sprzęgu w znalezionym obiekcie (znaleziony wypełni) - double distance = 0; // przeskanowana odleglość; odległość do zawalidrogi - TDynamicObject *foundobject = ABuFindObject( foundcoupler, distance, track, Direction, mycoupler ); // zaczynamy szukać na tym samym torze - - if( foundobject == nullptr ) { - // jeśli nie ma na tym samym, szukamy po okolicy szukanie najblizszego toru z jakims obiektem - // praktycznie przeklejone z TraceRoute()... - if (Direction >= 0) // uwzględniamy kawalek przeanalizowanego wcześniej toru - distance = track->Length() - RaTranslationGet(); // odległość osi od Point2 toru - else - distance = RaTranslationGet(); // odległość osi od Point1 toru - - while (distance < Distance) { - if (Direction > 0) { - // w kierunku Point2 toru - if( track ? - track->iNextDirection : - false ) { - // jeśli następny tor jest podpięty od Point2 - Direction = -Direction; // to zmieniamy kierunek szukania na tym torze - } - track = track->CurrentNext(); // potem dopiero zmieniamy wskaźnik - } - else { - // w kierunku Point1 - if( track ? - !track->iPrevDirection : - true ) { - // jeśli poprzedni tor nie jest podpięty od Point2 - Direction = -Direction; // to zmieniamy kierunek szukania na tym torze - } - track = track->CurrentPrev(); // potem dopiero zmieniamy wskaźnik - } - if (track) { - // jesli jest kolejny odcinek toru - foundobject = ABuFindObject(foundcoupler, distance, track, Direction, mycoupler); // przejrzenie pojazdów tego toru - if (foundobject) { - break; - } - } - else { - // jeśli toru nie ma, to wychodzimy - distance = Distance + 1.0; // koniec przeglądania torów - break; - } - } - } // Koniec szukania najbliższego toru z jakimś obiektem. - - // teraz odczepianie i jeśli coś się znalazło, doczepianie. - auto connectedobject = ( - initialdirection > 0 ? - PrevConnected : - NextConnected ); - if( ( connectedobject != nullptr ) - && ( connectedobject != foundobject ) ) { - // odłączamy, jeśli dalej niż metr i łączenie sprzęgiem wirtualnym - CouplersDettach( 1.0, initialdirection ); - } - - if (foundobject) { - // siebie można bezpiecznie podłączyć jednostronnie do znalezionego - MoverParameters->Attach( mycoupler, foundcoupler, foundobject->MoverParameters, coupling::faux ); - // MoverParameters->Couplers[MyCouplFound].Render=false; //wirtualnego nie renderujemy - if( mycoupler == end::front ) { - PrevConnected = foundobject; // pojazd od strony sprzęgu 0 - PrevConnectedNo = foundcoupler; - } - else { - NextConnected = foundobject; // pojazd od strony sprzęgu 1 - NextConnectedNo = foundcoupler; - } - - if( foundobject->MoverParameters->Couplers[ foundcoupler ].CouplingFlag == coupling::faux ) { - // Ra: wpinamy się wirtualnym tylko jeśli znaleziony ma wirtualny sprzęg - if( ( foundcoupler == end::front ? - foundobject->PrevConnected : - foundobject->NextConnected ) - != this ) { - // but first break existing connection of the target, - // otherwise we risk leaving the target's connected vehicle with active one-side connection - foundobject->CouplersDettach( - 1.0, - ( foundcoupler == end::front ? - 1 : - -1 ) ); - } - foundobject->MoverParameters->Attach( foundcoupler, mycoupler, this->MoverParameters, coupling::faux ); - - if( foundcoupler == end::front ) { - // jeśli widoczny sprzęg 0 znalezionego - if( ( DebugModeFlag ) - && ( foundobject->PrevConnected ) - && ( foundobject->PrevConnected != this ) ) { - WriteLog( "ScanObjects(): formed virtual link between \"" + asName + "\" (coupler " + to_string( mycoupler ) + ") and \"" + foundobject->asName + "\" (coupler " + to_string( foundcoupler ) + ")" ); - } - foundobject->PrevConnected = this; - foundobject->PrevConnectedNo = mycoupler; - } - else { - // jeśli widoczny sprzęg 1 znalezionego - if( ( DebugModeFlag ) - && ( foundobject->NextConnected ) - && ( foundobject->NextConnected != this ) ) { - WriteLog( "ScanObjects(): formed virtual link between \"" + asName + "\" (coupler " + to_string( mycoupler ) + ") and \"" + foundobject->asName + "\" (coupler " + to_string( foundcoupler ) + ")" ); - } - foundobject->NextConnected = this; - foundobject->NextConnectedNo = mycoupler; - } - } - - // NOTE: the distance we get is approximated as it's measured between active axles, not vehicle ends - fTrackBlock = distance; - if( distance < 100.0 ) { - // at short distances start to calculate range between couplers directly - // odległość do najbliższego pojazdu w linii prostej - fTrackBlock = MoverParameters->Couplers[ mycoupler ].CoupleDist; - } - if( ( false == TestFlag( track->iCategoryFlag, 1 ) ) - && ( distance > 50.0 ) ) { - // Ra: jeśli dwa samochody się mijają na odcinku przed zawrotką, to odległość między nimi nie może być liczona w linii prostej! - // NOTE: the distance is approximated, and additionally less accurate for cars heading in opposite direction - fTrackBlock = distance - ( 0.5 * ( MoverParameters->Dim.L + foundobject->MoverParameters->Dim.L ) ); - } - } - else { - // nic nie znalezione, to nie ma przeszkód - fTrackBlock = 10000.0; - } - -#ifdef EU07_DEBUG_COLLISIONS - // debug collider scans - if( ( PrevConnected != nullptr ) - && ( PrevConnected == NextConnected ) ) { - ErrorLog( "Bad coupling: " + asName + " has the same vehicle detected attached to both couplers" ); - } -#endif -} -//----------ABu: koniec skanowania pojazdow - TDynamicObject::TDynamicObject() { modelShake = Math3D::vector3(0, 0, 0); - fTrackBlock = 10000.0; // brak przeszkody na drodze +// fTrackBlock = 10000.0; // brak przeszkody na drodze btnOn = false; vUp = vWorldUp; vFront = vWorldFront; @@ -1704,8 +1457,8 @@ TDynamicObject::TDynamicObject() { // McZapkie-270202 Controller = AIdriver; bDisplayCab = false; // 030303 - NextConnected = PrevConnected = NULL; - NextConnectedNo = PrevConnectedNo = 2; // ABu: Numery sprzegow. 2=nie podłączony +// NextConnected = PrevConnected = NULL; +// NextConnectedNo = PrevConnectedNo = 2; // ABu: Numery sprzegow. 2=nie podłączony bEnabled = true; MyTrack = NULL; // McZapkie-260202 @@ -2424,16 +2177,10 @@ void TDynamicObject::Move(double fDistance) // if () na przechyłce będzie dodatkowo zmiana wysokości samochodu vPosition.y += MoverParameters->OffsetTrackV; // te offsety są liczone przez moverparam } - // Ra: skopiowanie pozycji do fizyki, tam potrzebna do zrywania sprzęgów - // MoverParameters->Loc.X=-vPosition.x; //robi to {Fast}ComputeMovement() - // MoverParameters->Loc.Y= vPosition.z; - // MoverParameters->Loc.Z= vPosition.y; // obliczanie pozycji sprzęgów do liczenia zderzeń auto dir = (0.5 * MoverParameters->Dim.L) * vFront; // wektor sprzęgu vCoulpler[end::front] = vPosition + dir; // współrzędne sprzęgu na początku vCoulpler[end::rear] = vPosition - dir; // współrzędne sprzęgu na końcu - MoverParameters->vCoulpler[end::front] = vCoulpler[end::front]; // tymczasowo kopiowane na inny poziom - MoverParameters->vCoulpler[end::rear] = vCoulpler[end::rear]; // bCameraNear= // if (bCameraNear) //jeśli istotne są szczegóły (blisko kamery) { // przeliczenie cienia @@ -2505,74 +2252,19 @@ void TDynamicObject::Move(double fDistance) }; void TDynamicObject::AttachPrev(TDynamicObject *Object, int iType) -{ // Ra: doczepia Object na końcu - // składu (nazwa funkcji może być - // myląca) +{ // Ra: doczepia Object na końcu składu (nazwa funkcji może być myląca) // Ra: używane tylko przy wczytywaniu scenerii - /* - //Ra: po wstawieniu pojazdu do scenerii nie miał on ustawionej pozycji, teraz - już ma - TLocation loc; - loc.X=-vPosition.x; - loc.Y=vPosition.z; - loc.Z=vPosition.y; - MoverParameters->Loc=loc; //Ra: do obliczania sprzęgów, na starcie nie są - przesunięte - loc.X=-Object->vPosition.x; - loc.Y=Object->vPosition.z; - loc.Z=Object->vPosition.y; - Object->MoverParameters->Loc=loc; //ustawienie dodawanego pojazdu - */ - MoverParameters->Attach(iDirection, Object->iDirection ^ 1, Object->MoverParameters, iType, true, false); - MoverParameters->Couplers[iDirection].Render = false; - Object->MoverParameters->Attach(Object->iDirection ^ 1, iDirection, MoverParameters, iType, true, false); - Object->MoverParameters->Couplers[Object->iDirection ^ 1].Render = true; // rysowanie sprzęgu w dołączanym - if (iDirection) - { //łączenie standardowe - NextConnected = Object; // normalnie doczepiamy go sobie do sprzęgu 1 - NextConnectedNo = Object->iDirection ^ 1; - } - else - { //łączenie odwrotne - PrevConnected = Object; // doczepiamy go sobie do sprzęgu 0, gdy stoimy odwrotnie - PrevConnectedNo = Object->iDirection ^ 1; - } - if (Object->iDirection) - { // dołączany jest normalnie ustawiany - Object->PrevConnected = this; // on ma nas z przodu - Object->PrevConnectedNo = iDirection; - } - else - { // dołączany jest odwrotnie ustawiany - Object->NextConnected = this; // on ma nas z tyłu - Object->NextConnectedNo = iDirection; - } -/* - // NOTE: this appears unnecessary and only messes things for the programmable lights function, which walks along - // whole trainset and expects each module to point to its own lights. Disabled, TBD, TODO: test for side-effects and delete if there's none - if (MoverParameters->TrainType & dt_EZT) // w przypadku łączenia członów, - // światła w rozrządczym zależą od stanu w silnikowym - if (MoverParameters->Couplers[iDirection].AllowedFlag & ctrain_depot) // gdy sprzęgi łączone warsztatowo (powiedzmy) - if ((MoverParameters->Power < 1.0) && (Object->MoverParameters->Power > 1.0)) // my nie mamy mocy, ale ten drugi ma - iLights = Object->MoverParameters->iLights; // to w tym z mocą będą światła załączane, a w tym bez tylko widoczne - else if ((MoverParameters->Power > 1.0) && - (Object->MoverParameters->Power < 1.0)) // my mamy moc, ale ten drugi nie ma - Object->iLights = MoverParameters->iLights; // to w tym z mocą będą światła załączane, a w tym bez tylko widoczne -*/ - return; - // SetPneumatic(1,1); //Ra: to i tak się nie wykonywało po return - // SetPneumatic(1,0); - // SetPneumatic(0,1); - // SetPneumatic(0,0); + MoverParameters->Attach( iDirection, Object->iDirection ^ 1, Object->MoverParameters, iType, true, false ); + update_neighbours(); } -bool TDynamicObject::UpdateForce(double dt, double dt1, bool FullVer) +bool TDynamicObject::UpdateForce(double dt) { if (!bEnabled) return false; if( dt > 0 ) { // wywalenie WS zależy od ustawienia kierunku - MoverParameters->ComputeTotalForce( dt, dt1, FullVer ); + MoverParameters->ComputeTotalForce( dt ); } return true; } @@ -2593,6 +2285,8 @@ void TDynamicObject::LoadExchange( int const Disembark, int const Embark, int co } } */ + if( Platform == 0 ) { return; } // edge case, if there's no accessible platforms discard the request + m_exchange.unload_count += Disembark; m_exchange.load_count += Embark; m_exchange.platforms = Platform; @@ -2979,7 +2673,7 @@ bool TDynamicObject::Update(double dt, double dt1) MoverParameters->EqvtPipePress = GetEPP(); // srednie cisnienie w PG if( ( Mechanik->Primary() ) && ( MoverParameters->EngineType == TEngineType::ElectricInductionMotor ) ) { - // jesli glowny i z asynchronami, to niech steruje hamulcem lacznie dla calego pociagu/ezt + // jesli glowny i z asynchronami, to niech steruje hamulcem i napedem lacznie dla calego pociagu/ezt auto const kier = (DirectionGet() * MoverParameters->ActiveCab > 0); auto FED { 0.0 }; auto np { 0 }; @@ -2991,6 +2685,17 @@ bool TDynamicObject::Update(double dt, double dt1) auto FmaxED { 0.0 }; auto Frj { 0.0 }; auto osie { 0 }; + // 0a. ustal aktualna nastawe zadania sily napedowej i hamowania + if (MoverParameters->Power < 1) + MoverParameters->MainCtrlPos = ctOwner->Controlling()->MainCtrlPos*MoverParameters->MainCtrlPosNo / std::max(1, ctOwner->Controlling()->MainCtrlPosNo); + MoverParameters->CheckEIMIC(dt1); + MoverParameters->CheckSpeedCtrl(); + + auto eimic = Min0R(MoverParameters->eimic, MoverParameters->eimicSpeedCtrl); + MoverParameters->eimic_real = eimic; + MoverParameters->SendCtrlToNext("EIMIC", Max0R(0, eimic), MoverParameters->CabNo); + auto LBR = Max0R(-eimic, 0); + // 1. ustal wymagana sile hamowania calego pociagu // - opoznienie moze byc ustalane na podstawie charakterystyki // - opoznienie moze byc ustalane na podstawie mas i cisnien granicznych @@ -3040,32 +2745,35 @@ bool TDynamicObject::Update(double dt, double dt1) auto const amax = RapidMult * std::min(FmaxPN / masamax, MoverParameters->MED_amax); auto const doorisopen { - ( false == MoverParameters->Doors.instances[ side::right ].is_closed ) - || ( false == MoverParameters->Doors.instances[ side::left ].is_closed ) }; + ( false == MoverParameters->Doors.instances[ side::left ].is_closed ) + || ( false == MoverParameters->Doors.instances[ side::right ].is_closed ) + || ( MoverParameters->Doors.permit_needed + && ( MoverParameters->Doors.instances[ side::left ].open_permit + || MoverParameters->Doors.instances[ side::right ].open_permit ) ) }; - if ((MoverParameters->Vel < 0.5) && (MoverParameters->BrakePress > 0.2) || doorisopen ) + if ((MoverParameters->Vel < 0.5) && (MoverParameters->BrakePress > 0.2 || doorisopen)) { MoverParameters->ShuntMode = true; } if (MoverParameters->ShuntMode) { MoverParameters->ShuntModeAllow = ( false == doorisopen ) && - (MoverParameters->LocalBrakeRatio() < 0.01); + (LBR < 0.01); } if( ( MoverParameters->Vel > 1 ) && ( false == doorisopen ) ) { MoverParameters->ShuntMode = false; MoverParameters->ShuntModeAllow = (MoverParameters->BrakePress > 0.2) && - (MoverParameters->LocalBrakeRatio() < 0.01); + (LBR < 0.01); } - auto Fzad = amax * MoverParameters->LocalBrakeRatio() * masa; + auto Fzad = amax * LBR * masa; if ((MoverParameters->BrakeCtrlPos == MoverParameters->Handle->GetPos(bh_EB)) && (MoverParameters->eimc[eimc_p_abed] < 0.001)) Fzad = amax * masa; //pętla bezpieczeństwa - pełne służbowe if ((MoverParameters->ScndS) && (MoverParameters->Vel > MoverParameters->eimc[eimc_p_Vh1]) && (FmaxED > 0)) { - Fzad = std::min(MoverParameters->LocalBrakeRatio() * FmaxED, FfulED); + Fzad = std::min(LBR * FmaxED, FfulED); } if (((MoverParameters->ShuntMode) && (Frj < 0.0015 * masa)) || (MoverParameters->V * MoverParameters->DirAbsolute < -0.2)) @@ -3137,7 +2845,7 @@ bool TDynamicObject::Update(double dt, double dt1) PrzekrF[i] = false; FzED[i] = (FmaxED > 0 ? FzadED / FmaxED : 0); p->MoverParameters->AnPos = - (MoverParameters->ScndS ? MoverParameters->LocalBrakeRatio() : FzED[i]); + (MoverParameters->ScndS ? LBR : FzED[i]); FzEP[ i ] = static_cast( FzadPN * p->MoverParameters->NAxles ) / static_cast( osie ); ++i; p->MoverParameters->ShuntMode = MoverParameters->ShuntMode; @@ -3742,13 +3450,13 @@ bool TDynamicObject::Update(double dt, double dt1) else { // unfold mirror on the side with open doors, if not moving too fast if( ( dMirrorMoveL < 1.0 ) - && ( true == MoverParameters->Doors.instances[side::left].is_open ) ) { + && ( true == MoverParameters->Doors.instances[side::left].open_permit ) ) { dMirrorMoveL = std::min( 1.0, dMirrorMoveL + 1.0 * dt1 ); } if( ( dMirrorMoveR < 1.0 ) - && ( true == MoverParameters->Doors.instances[side::right].is_open ) ) { + && ( true == MoverParameters->Doors.instances[side::right].open_permit ) ) { dMirrorMoveR = std::min( 1.0, dMirrorMoveR + 1.0 * dt1 ); @@ -4133,15 +3841,6 @@ void TDynamicObject::RenderSounds() { } } } - // doorstep sounds - if( door.step_position < 1.f ) { - for( auto &doorsounds : m_doorsounds ) { - if( doorsounds.placement == side ) { - doorsounds.step_open.play( sound_flags::exclusive ); - doorsounds.step_close.stop(); - } - } - } } if( true == door.is_closing ) { // door sounds can start playing before the door begins moving but shouldn't cease once the door closes @@ -4154,15 +3853,22 @@ void TDynamicObject::RenderSounds() { } } } - // doorstep sounds are played only when the doorstep is moving - if( ( door.step_position > 0.f ) - && ( door.step_position < 1.f ) ) { - for( auto &doorsounds : m_doorsounds ) { - if( doorsounds.placement == side ) { - // determine left side doors from their offset - doorsounds.step_close.play( sound_flags::exclusive ); - doorsounds.step_open.stop(); - } + } + // doorstep sounds + if( door.step_unfolding ) { + for( auto &doorsounds : m_doorsounds ) { + if( doorsounds.placement == side ) { + doorsounds.step_open.play( sound_flags::exclusive ); + doorsounds.step_close.stop(); + } + } + } + if( door.step_folding ) { + for( auto &doorsounds : m_doorsounds ) { + if( doorsounds.placement == side ) { + // determine left side doors from their offset + doorsounds.step_close.play( sound_flags::exclusive ); + doorsounds.step_open.stop(); } } } @@ -6093,181 +5799,207 @@ void TDynamicObject::RaLightsSet(int head, int rear) int TDynamicObject::DirectionSet(int d) { // ustawienie kierunku w składzie (wykonuje AI) auto const lastdirection { iDirection }; - iDirection = d > 0 ? 1 : 0; // d:1=zgodny,-1=przeciwny; iDirection:1=zgodny,0=przeciwny; + iDirection = ( d > 0 ) ? 1 : 0; // d:1=zgodny,-1=przeciwny; iDirection:1=zgodny,0=przeciwny; if( iDirection != lastdirection ) { // direction was flipped, switch recorded servicable platform sides for potentially ongoing load exchange auto const left { ( lastdirection > 0 ) ? 1 : 2 }; auto const right { 3 - left }; m_exchange.platforms = - ( m_exchange.platforms & left ? right : 0 ) - + ( m_exchange.platforms & right ? left : 0 ); + ( ( m_exchange.platforms & left ) != 0 ? right : 0 ) + + ( ( m_exchange.platforms & right ) != 0 ? left : 0 ); } if (MyTrack) - { // podczas wczytywania wstawiane jest AI, ale może jeszcze nie - // być toru + { // podczas wczytywania wstawiane jest AI, ale może jeszcze nie być toru // AI ustawi kierunek ponownie po uruchomieniu silnika - if (iDirection) // jeśli w kierunku Coupler 0 - { - if (MoverParameters->Couplers[0].CouplingFlag == - ctrain_virtual) // brak pojazdu podpiętego? - ABuScanObjects(1, 300); // szukanie czegoś do podłączenia - } - else if (MoverParameters->Couplers[1].CouplingFlag == - ctrain_virtual) // brak pojazdu podpiętego? - ABuScanObjects(-1, 300); + update_neighbours(); } // informacja o położeniu następnego - return 1 - (iDirection ? NextConnectedNo : PrevConnectedNo); + return 1 - ( ( iDirection > 0 ) ? NextConnectedNo() : PrevConnectedNo() ); }; -TDynamicObject * TDynamicObject::PrevAny() const -{ // wskaźnik na poprzedni, - // nawet wirtualny - return iDirection ? PrevConnected : NextConnected; -}; -TDynamicObject * TDynamicObject::Prev(int C) const -{ - if (MoverParameters->Couplers[iDirection ^ 1].CouplingFlag & C) - return iDirection ? PrevConnected : NextConnected; - return NULL; // gdy sprzęg wirtualny, to jakby nic nie było -}; -TDynamicObject * TDynamicObject::Next(int C) const -{ - if (MoverParameters->Couplers[iDirection].CouplingFlag & C) - return iDirection ? NextConnected : PrevConnected; - return NULL; // gdy sprzęg wirtualny, to jakby nic nie było -}; -double TDynamicObject::NextDistance(double d) -{ // ustalenie odległości do - // następnego pojazdu, potrzebne - // do wstecznego skanowania - if (!MoverParameters->Couplers[iDirection].Connected) - return d; // jeśli nic nie ma, zwrócenie domyślnej wartości - if ((d <= 0.0) || (MoverParameters->Couplers[iDirection].CoupleDist < d)) - return MoverParameters->Couplers[iDirection].Dist; - else - return d; +// wskaźnik na poprzedni, nawet wirtualny +TDynamicObject * TDynamicObject::PrevAny() const { + return MoverParameters->Neighbours[ iDirection ^ 1 ].vehicle; +} +TDynamicObject * TDynamicObject::Prev(int C) const { + return ( (MoverParameters->Couplers[ iDirection ^ 1 ].CouplingFlag & C) ? + MoverParameters->Neighbours[ iDirection ^ 1 ].vehicle : + nullptr );// gdy sprzęg wirtualny, to jakby nic nie było +} +TDynamicObject * TDynamicObject::Next(int C) const { + return ( (MoverParameters->Couplers[ iDirection ].CouplingFlag & C) ? + MoverParameters->Neighbours[ iDirection ].vehicle : + nullptr );// gdy sprzęg wirtualny, to jakby nic nie było +} + +// ustalenie następnego (1) albo poprzedniego (0) w składzie bez względu na prawidłowość iDirection +TDynamicObject * +TDynamicObject::Neighbour(int &dir) { + + auto *neighbour { ( + MoverParameters->Couplers[ dir ].CouplingFlag != coupling::faux ? + MoverParameters->Neighbours[ dir ].vehicle : + nullptr ) }; + // nowa wartość + dir = 1 - MoverParameters->Neighbours[ dir ].vehicle_end; + + return neighbour; }; -TDynamicObject * TDynamicObject::Neightbour(int &dir) -{ // ustalenie następnego (1) albo poprzedniego (0) w składzie bez - // względu na prawidłowość - // iDirection - int d = dir; // zapamiętanie kierunku - dir = 1 - (dir ? NextConnectedNo : PrevConnectedNo); // nowa wartość - return (d ? (MoverParameters->Couplers[1].CouplingFlag ? NextConnected : NULL) : - (MoverParameters->Couplers[0].CouplingFlag ? PrevConnected : NULL)); -}; +// updates potential collision sources +void +TDynamicObject::update_neighbours() { -void TDynamicObject::CoupleDist() -{ // obliczenie odległości sprzęgów - if (MyTrack ? (MyTrack->iCategoryFlag & 1) : - true) // jeśli nie ma przypisanego toru, to liczyć jak dla kolei - { // jeśli jedzie po szynach (również unimog), liczenie kul wystarczy - MoverParameters->SetCoupleDist(); + for( int end = end::front; end <= end::rear; ++end ) { + + auto &neighbour { MoverParameters->Neighbours[ end ] }; + auto const &coupler { MoverParameters->Couplers[ end ] }; + + if( ( coupler.Connected != nullptr ) + && ( neighbour.vehicle != nullptr ) ) { + // physical connection with another vehicle locks down collision source on this end +// neighbour.vehicle = coupler.Connected; +// neighbour.vehicle_end = coupler.ConnectedNr; + neighbour.distance = TMoverParameters::CouplerDist( MoverParameters, coupler.Connected ); + } + else { + // if there's no connected vehicle check for potential collision sources in the vicinity + // NOTE: we perform a new scan on each update to ensure we always locate the nearest potential source + neighbour = neighbour_data(); + // 10m ~= 140 km/h at 4 fps + safety margin, potential distance between outer axles of involved vehicles + auto const scanrange { std::max( 10.0, std::abs( MoverParameters->V ) ) + 40.0 }; + auto const lookup { find_vehicle( end, scanrange ) }; + + if( false == std::get( lookup ) ) { continue; } + + neighbour.vehicle = std::get( lookup ); + neighbour.vehicle_end = std::get( lookup ); + neighbour.distance = std::get( lookup ); + + if( neighbour.distance < ( neighbour.vehicle->MoverParameters->CategoryFlag == 2 ? 50 : 100 ) ) { + // at short distances (re)calculate range between couplers directly + neighbour.distance = TMoverParameters::CouplerDist( MoverParameters, neighbour.vehicle->MoverParameters ); + } + } } - else - { // na drodze trzeba uwzględnić wektory ruchu - double d0 = MoverParameters->Couplers[0].CoupleDist; - // double d1=MoverParameters->Couplers[1].CoupleDist; //sprzęg z tyłu - // samochodu można olać, - // dopóki nie jeździ na wstecznym - Math3D::vector3 p1, p2; - double d, w; // dopuszczalny dystans w poprzek - MoverParameters->SetCoupleDist(); // liczenie standardowe - if (MoverParameters->Couplers[0].Connected) // jeśli cokolwiek podłączone - if (MoverParameters->Couplers[0].CouplingFlag == 0) // jeśli wirtualny - if (MoverParameters->Couplers[0].CoupleDist < 300.0) // i mniej niż 300m - { // przez MoverParameters->Couplers[0].Connected nie da się dostać do - // DynObj, stąd - // prowizorka - // WriteLog("Collision of - // "+AnsiString(MoverParameters->Couplers[0].CoupleDist)+"m detected - // by - // "+asName+":0."); - w = 0.5 * (MoverParameters->Couplers[0].Connected->Dim.W + - MoverParameters->Dim.W); // minimalna odległość minięcia - d = -DotProduct(vLeft, vCoulpler[0]); // odległość prostej ruchu od początku - // układu współrzędnych - d = fabs( - DotProduct(vLeft, - ((TMoverParameters *)(MoverParameters->Couplers[0].Connected)) - ->vCoulpler[MoverParameters->Couplers[0].ConnectedNr]) + - d); - // WriteLog("Distance "+AnsiString(d)+"m from "+asName+":0."); - if (d > w) - MoverParameters->Couplers[0].CoupleDist = - (d0 < 10 ? 50 : d0); // przywrócenie poprzedniej - } - if (MoverParameters->Couplers[1].Connected) // jeśli cokolwiek podłączone - if (MoverParameters->Couplers[1].CouplingFlag == 0) // jeśli wirtualny - if (MoverParameters->Couplers[1].CoupleDist < 300.0) // i mniej niż 300m - { - // WriteLog("Collision of - // "+AnsiString(MoverParameters->Couplers[1].CoupleDist)+"m detected - // by - // "+asName+":1."); - w = 0.5 * (MoverParameters->Couplers[1].Connected->Dim.W + - MoverParameters->Dim.W); // minimalna odległość minięcia - d = -DotProduct(vLeft, vCoulpler[1]); // odległość prostej ruchu od początku - // układu współrzędnych - d = fabs( - DotProduct(vLeft, - ((TMoverParameters *)(MoverParameters->Couplers[1].Connected)) - ->vCoulpler[MoverParameters->Couplers[1].ConnectedNr]) + - d); - // WriteLog("Distance "+AnsiString(d)+"m from "+asName+":1."); - if (d > w) - MoverParameters->Couplers[0].CoupleDist = - (d0 < 10 ? 50 : d0); // przywrócenie poprzedniej - } +} + +// locates potential collision source within specified range, scanning track in specified direction. returns: true if neighbour was located, false otherwise +// NOTE: reuses legacy code. TBD, TODO: review, refactor? +std::tuple +TDynamicObject::find_vehicle( int const Direction, double const Distance ) const { + + auto direction { ( Direction == end::front ? 1 : -1 ) }; + auto const initialdirection { direction }; // zapamiętanie kierunku poszukiwań na torze początkowym, względem sprzęgów + + auto const *track { RaTrackGet() }; + if( RaDirectionGet() < 0 ) { + // czy oś jest ustawiona w stronę Point1? + direction = -direction; } -}; + + // (teraz względem toru) + auto const mycoupler { ( initialdirection < 0 ? end::rear : end::front ) }; // numer sprzęgu do podłączenia w obiekcie szukajacym + auto foundcoupler { -1 }; // numer sprzęgu w znalezionym obiekcie (znaleziony wypełni) + auto distance { 0.0 }; // przeskanowana odleglość; odległość do zawalidrogi + auto *foundobject { ABuFindObject( foundcoupler, distance, track, direction, mycoupler ) }; // zaczynamy szukać na tym samym torze + + if( foundobject == nullptr ) { + // jeśli nie ma na tym samym, szukamy po okolicy szukanie najblizszego toru z jakims obiektem + // praktycznie przeklejone z TraceRoute()... + if (direction >= 0) // uwzględniamy kawalek przeanalizowanego wcześniej toru + distance = track->Length() - RaTranslationGet(); // odległość osi od Point2 toru + else + distance = RaTranslationGet(); // odległość osi od Point1 toru + + while (distance < Distance) { + if (direction > 0) { + // w kierunku Point2 toru + if( track ? + track->iNextDirection : + false ) { + // jeśli następny tor jest podpięty od Point2 + direction = -direction; // to zmieniamy kierunek szukania na tym torze + } + track = track->CurrentNext(); // potem dopiero zmieniamy wskaźnik + } + else { + // w kierunku Point1 + if( track ? + !track->iPrevDirection : + true ) { + // jeśli poprzedni tor nie jest podpięty od Point2 + direction = -direction; // to zmieniamy kierunek szukania na tym torze + } + track = track->CurrentPrev(); // potem dopiero zmieniamy wskaźnik + } + if (track) { + // jesli jest kolejny odcinek toru + foundobject = ABuFindObject(foundcoupler, distance, track, direction, mycoupler); // przejrzenie pojazdów tego toru + if (foundobject) { + break; + } + } + else { + // jeśli toru nie ma, to wychodzimy + distance = Distance + 1.0; // koniec przeglądania torów + break; + } + } + } // Koniec szukania najbliższego toru z jakimś obiektem. + + if( foundobject == nullptr ) { return {}; } +/* + auto const *vehicle { MoverParameters }; + auto const *foundvehicle { foundobject->MoverParameters }; + if( ( false == TestFlag( track->iCategoryFlag, 1 ) ) + && ( distance > 50.0 ) ) { + // Ra: jeśli dwa samochody się mijają na odcinku przed zawrotką, to odległość między nimi nie może być liczona w linii prostej! + // NOTE: the distance is approximated, and additionally less accurate for cars heading in opposite direction + distance -= ( 0.5 * ( vehicle->Dim.L + foundvehicle->Dim.L ) ); + } + else if( distance < 100.0 ) { + // at short distances start to calculate range between couplers directly + // odległość do najbliższego pojazdu w linii prostej + distance = TMoverParameters::CouplerDist( vehicle, foundvehicle ); + } +*/ + return { foundobject, foundcoupler, distance, true }; +} TDynamicObject * TDynamicObject::ControlledFind() { // taka proteza: - // chcę podłączyć - // kabinę EN57 - // bezpośrednio z - // silnikowym, aby - // nie robić tego - // przez - // ukrotnienie + // 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 - // lepiej by było przesyłać komendy sterowania, co jednak wymaga przebudowy - // transmisji komend - // (LD) - // problem się robi ze światłami, które będą zapalane w silnikowym, ale muszą - // świecić się w - // rozrządczych - // dla EZT światłą czołowe będą "zapalane w silnikowym", ale widziane z - // rozrządczych + // lepiej by było przesyłać komendy sterowania, co jednak wymaga przebudowy transmisji komend (LD) + // problem się robi ze światłami, które będą zapalane w silnikowym, ale muszą świecić się w rozrządczych + // dla EZT światłą 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 + // 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... TDynamicObject *d = this; // zaczynamy od aktualnego - if( d->MoverParameters->TrainType & dt_EZT ) { + if( ( d->MoverParameters->TrainType == dt_EZT ) + || ( d->MoverParameters->TrainType == dt_DMU ) ) { // na razie dotyczy to EZT - if( ( d->NextConnected != nullptr ) - && ( true == TestFlag( d->MoverParameters->Couplers[ 1 ].AllowedFlag, coupling::permanent ) ) ) { + if( ( d->NextConnected() != nullptr ) + && ( true == TestFlag( d->MoverParameters->Couplers[ end::rear ].AllowedFlag, coupling::permanent ) ) ) { // gdy jest człon od sprzęgu 1, a sprzęg łączony warsztatowo (powiedzmy) if( ( d->MoverParameters->Power < 1.0 ) - && ( d->NextConnected->MoverParameters->Power > 1.0 ) ) { + && ( d->NextConnected()->MoverParameters->Power > 1.0 ) ) { // my nie mamy mocy, ale ten drugi ma - d = d->NextConnected; // będziemy sterować tym z mocą + d = d->NextConnected(); // będziemy sterować tym z mocą } } - else if( ( d->PrevConnected != nullptr ) - && ( true == TestFlag( d->MoverParameters->Couplers[ 0 ].AllowedFlag, coupling::permanent ) ) ) { + else if( ( d->PrevConnected() != nullptr ) + && ( true == TestFlag( d->MoverParameters->Couplers[ end::front ].AllowedFlag, coupling::permanent ) ) ) { // gdy jest człon od sprzęgu 0, a sprzęg łączony warsztatowo (powiedzmy) if( ( d->MoverParameters->Power < 1.0 ) - && ( d->PrevConnected->MoverParameters->Power > 1.0 ) ) { + && ( d->PrevConnected()->MoverParameters->Power > 1.0 ) ) { // my nie mamy mocy, ale ten drugi ma - d = d->PrevConnected; // będziemy sterować tym z mocą + d = d->PrevConnected(); // będziemy sterować tym z mocą } } } @@ -6399,22 +6131,22 @@ TDynamicObject::ConnectedEnginePowerSource( TDynamicObject const *Caller ) const } // ...otherwise check rear first... // NOTE: the order should be reversed in flipped vehicles, but we ignore this out of laziness - if( ( nullptr != NextConnected ) - && ( NextConnected != Caller ) + if( ( nullptr != NextConnected() ) + && ( NextConnected() != Caller ) && ( ( MoverParameters->Couplers[1].CouplingFlag & ctrain_controll ) == ctrain_controll ) ) { - auto source = NextConnected->ConnectedEnginePowerSource( this ); + auto source = NextConnected()->ConnectedEnginePowerSource( this ); if( source != TPowerSource::NotDefined ) { return source; } } // ...then rear... - if( ( nullptr != PrevConnected ) - && ( PrevConnected != Caller ) + if( ( nullptr != PrevConnected() ) + && ( PrevConnected() != Caller ) && ( ( MoverParameters->Couplers[ 0 ].CouplingFlag & ctrain_controll ) == ctrain_controll ) ) { - auto source = PrevConnected->ConnectedEnginePowerSource( this ); + auto source = PrevConnected()->ConnectedEnginePowerSource( this ); if( source != TPowerSource::NotDefined ) { return source; @@ -6874,7 +6606,7 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub if( Vehicle.EngineType == TEngineType::ElectricInductionMotor ) { if( Vehicle.InverterFrequency > 0.1 ) { - volume = inverter.m_amplitudeoffset + inverter.m_amplitudefactor * std::sqrt( std::abs( Vehicle.dizel_fill ) ); + volume = inverter.m_amplitudeoffset + inverter.m_amplitudefactor * std::sqrt( std::abs( Vehicle.eimv_pr) ); inverter .pitch( inverter.m_frequencyoffset + inverter.m_frequencyfactor * Vehicle.InverterFrequency ) @@ -6997,25 +6729,25 @@ vehicle_table::update( double Deltatime, int Iterationcount ) { update_traction( vehicle ); } vehicle->MoverParameters->ComputeConstans(); - vehicle->CoupleDist(); + vehicle->update_neighbours(); } if( Iterationcount > 1 ) { // ABu: ponizsze wykonujemy tylko jesli wiecej niz jedna iteracja for( int iteration = 0; iteration < ( Iterationcount - 1 ); ++iteration ) { for( auto *vehicle : m_items ) { - vehicle->UpdateForce( Deltatime, Deltatime, false ); + vehicle->UpdateForce( Deltatime ); } for( auto *vehicle : m_items ) { vehicle->FastUpdate( Deltatime ); } } } + for( auto *vehicle : m_items ) { + vehicle->UpdateForce( Deltatime ); + } auto const totaltime { Deltatime * Iterationcount }; // całkowity czas - for( auto *vehicle : m_items ) { - vehicle->UpdateForce( Deltatime, totaltime, true ); - } for( auto *vehicle : m_items ) { // Ra 2015-01: tylko tu przelicza sieć trakcyjną vehicle->Update( Deltatime, totaltime ); diff --git a/DynObj.h b/DynObj.h index e42c97c5..70544e58 100644 --- a/DynObj.h +++ b/DynObj.h @@ -187,12 +187,13 @@ public: std::string asDestination; // dokąd pojazd ma być kierowany "(stacja):(tor)" Math3D::matrix4x4 mMatrix; // macierz przekształcenia do renderowania modeli TMoverParameters *MoverParameters; // parametry fizyki ruchu oraz przeliczanie - TDynamicObject *NextConnected; // pojazd podłączony od strony sprzęgu 1 (kabina -1) - TDynamicObject *PrevConnected; // pojazd podłączony od strony sprzęgu 0 (kabina 1) - int NextConnectedNo; // numer sprzęgu podłączonego z tyłu - int PrevConnectedNo; // numer sprzęgu podłączonego z przodu - double fScanDist; // odległość skanowania torów na obecność innych pojazdów - double fTrackBlock; // odległość do przeszkody do dalszego ruchu (wykrywanie kolizji z innym pojazdem) + inline TDynamicObject *NextConnected() { return MoverParameters->Neighbours[ end::rear ].vehicle; }; // pojazd podłączony od strony sprzęgu 1 (kabina -1) + inline TDynamicObject *PrevConnected() { return MoverParameters->Neighbours[ end::front ].vehicle; }; // pojazd podłączony od strony sprzęgu 0 (kabina 1) + inline TDynamicObject *NextConnected() const { return MoverParameters->Neighbours[ end::rear ].vehicle; }; // pojazd podłączony od strony sprzęgu 1 (kabina -1) + inline TDynamicObject *PrevConnected() const { return MoverParameters->Neighbours[ end::front ].vehicle; }; // pojazd podłączony od strony sprzęgu 0 (kabina 1) + inline int NextConnectedNo() const { return MoverParameters->Neighbours[ end::rear ].vehicle_end; } + inline int PrevConnectedNo() const { return MoverParameters->Neighbours[ end::front ].vehicle_end; } +// double fTrackBlock; // odległość do przeszkody do dalszego ruchu (wykrywanie kolizji z innym pojazdem) TPowerSource ConnectedEnginePowerSource( TDynamicObject const *Caller ) const; @@ -458,11 +459,8 @@ private: int iNumAxles; // ilość osi std::string asModel; - public: - void ABuScanObjects(int ScanDir, double ScanDist); - private: - TDynamicObject *ABuFindObject( int &Foundcoupler, double &Distance, TTrack const *Track, int const Direction, int const Mycoupler ); + TDynamicObject *ABuFindObject( int &Foundcoupler, double &Distance, TTrack const *Track, int const Direction, int const Mycoupler ) const; void ABuCheckMyTrack(); public: @@ -471,7 +469,6 @@ private: TDynamicObject * PrevAny() const; TDynamicObject * Prev(int C = -1) const; TDynamicObject * Next(int C = -1) const; - double NextDistance(double d = -1.0); void SetdMoveLen(double dMoveLen) { MoverParameters->dMoveLen = dMoveLen; } void ResetdMoveLen() { @@ -486,6 +483,7 @@ private: return this ? asName : std::string(); }; + std::string asBaseDir; // std::ofstream PneuLogFile; //zapis parametrow pneumatycznych // youBy - dym @@ -507,7 +505,6 @@ private: bool bDisplayCab; // czy wyswietlac kabine w train.cpp int iCabs; // maski bitowe modeli kabin TTrack *MyTrack; // McZapkie-030303: tor na ktorym stoi, ABu - std::string asBaseDir; int iOverheadMask; // maska przydzielana przez AI pojazdom posiadającym pantograf, aby wymuszały jazdę bezprądową TTractionParam tmpTraction; double fAdjustment; // korekcja - docelowo przenieść do TrkFoll.cpp wraz z odległością od poprzedniego @@ -522,7 +519,7 @@ private: int init_sections( TModel3d const *Model, std::string const &Nameprefix ); void create_controller( std::string const Type, bool const Trainset ); void AttachPrev(TDynamicObject *Object, int iType = 1); - bool UpdateForce(double dt, double dt1, bool FullVer); + bool UpdateForce(double dt); // initiates load change by specified amounts, with a platform on specified side void LoadExchange( int const Disembark, int const Embark, int const Platform ); // calculates time needed to complete current load change @@ -606,14 +603,13 @@ private: Axle1.GetTranslation() : Axle0.GetTranslation(); }; // zwraca tor z aktywną osią - inline TTrack * RaTrackGet() { + inline TTrack * RaTrackGet() const { return iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack(); }; void couple( int const Side ); int uncouple( int const Side ); - void CouplersDettach(double MinDist, int MyScanDir); void RadioStop(); void Damage(char flag); void RaLightsSet(int head, int rear); @@ -627,8 +623,11 @@ private: return iDirection + iDirection - 1; }; int DettachStatus(int dir); int Dettach(int dir); - TDynamicObject * Neightbour(int &dir); - void CoupleDist(); + TDynamicObject * Neighbour(int &dir); + // updates potential collision sources + void update_neighbours(); + // locates potential collision source within specified range, scanning its route in specified direction + auto find_vehicle( int const Direction, double const Range ) const -> std::tuple; TDynamicObject * ControlledFind(); void ParamSet(int what, int into); // zapytanie do AI, po którym segmencie skrzyżowania jechać diff --git a/Event.cpp b/Event.cpp index beee4d92..38f9a173 100644 --- a/Event.cpp +++ b/Event.cpp @@ -960,7 +960,11 @@ whois_event::run_() { 1 : 0, // 1, gdy ma tu zatrzymanie m_input.flags ); - WriteLog( "Train detected: " + m_activator->Mechanik->TrainName() ); + WriteLog( + "Type: WhoIs (" + to_string( m_input.flags ) + ") - " + + "[train: " + m_activator->Mechanik->TrainName() + "], " + + "[stations left: " + to_string( m_activator->Mechanik->StationCount() - m_activator->Mechanik->StationIndex() ) + "], " + + "[stop at next: " + ( m_activator->Mechanik->IsStop() ? "yes" : "no") + "]" ); } } } diff --git a/Gauge.cpp b/Gauge.cpp index 79218ce8..0ee74581 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -360,21 +360,35 @@ void TGauge::AssignInt(int *iValue) iData = iValue; }; +void TGauge::AssignBool(bool *bValue) +{ + m_datatype = 'b'; + bData = bValue; +}; + void TGauge::UpdateValue() { // ustawienie wartości docelowej z parametru switch (m_datatype) { // to nie jest zbyt optymalne, można by zrobić osobne funkcje - case 'f': - UpdateValue( *fData ); - break; - case 'd': - UpdateValue( *dData ); - break; - case 'i': - UpdateValue( *iData ); - break; - default: - break; + case 'f': { + UpdateValue( *fData ); + break; + } + case 'd': { + UpdateValue( *dData ); + break; + } + case 'i': { + UpdateValue( *iData ); + break; + } + case 'b': { + UpdateValue( ( *bData ? 1.f : 0.f ) ); + break; + } + default: { + break; + } } }; diff --git a/Gauge.h b/Gauge.h index e812831b..0a84d5d9 100644 --- a/Gauge.h +++ b/Gauge.h @@ -48,6 +48,7 @@ public: void AssignFloat(float *fValue); void AssignDouble(double *dValue); void AssignInt(int *iValue); + void AssignBool(bool *bValue); void UpdateValue(); // returns offset of submodel associated with the button from the model centre glm::vec3 model_offset() const; @@ -82,6 +83,7 @@ private: float *fData; double *dData { nullptr }; int *iData; + bool *bData; }; int m_soundtype { 0 }; // toggle between exclusive and multiple sound generation sound_source m_soundtemplate { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // shared properties for control's sounds diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 33ebab2c..4ce7e394 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -141,7 +141,7 @@ static int const ctrain_depot = 128; //nie rozłączalny podczas zwykłyc // vehicle sides; exclusive enum end { front = 0, - rear + rear = 1 }; enum side { @@ -359,7 +359,7 @@ enum class TBrakeSystem { Individual, Pneumatic, ElectroPneumatic }; /*podtypy hamulcow zespolonych*/ enum class TBrakeSubSystem { ss_None, ss_W, ss_K, ss_KK, ss_Hik, ss_ESt, ss_KE, ss_LSt, ss_MT, ss_Dako }; enum class TBrakeValve { NoValve, W, W_Lu_VI, W_Lu_L, W_Lu_XR, K, Kg, Kp, Kss, Kkg, Kkp, Kks, Hikg1, Hikss, Hikp1, KE, SW, EStED, NESt3, ESt3, LSt, ESt4, ESt3AL2, EP1, EP2, M483, CV1_L_TR, CV1, CV1_R, Other }; -enum class TBrakeHandle { NoHandle, West, FV4a, M394, M254, FVel1, FVel6, D2, Knorr, FD1, BS2, testH, St113, MHZ_P, MHZ_T, MHZ_EN57, MHZ_K5P, MHZ_K8P }; +enum class TBrakeHandle { NoHandle, West, FV4a, M394, M254, FVE408, FVel6, D2, Knorr, FD1, BS2, testH, St113, MHZ_P, MHZ_T, MHZ_EN57, MHZ_K5P, MHZ_K8P }; /*typy hamulcow indywidualnych*/ enum class TLocalBrake { NoBrake, ManualBrake, PneumaticBrake, HydraulicBrake }; /*dla osob/towar: opoznienie hamowania/odhamowania*/ @@ -619,18 +619,16 @@ struct power_coupling { struct TCoupling { /*parametry*/ double SpringKB = 1.0; /*stala sprezystosci zderzaka/sprzegu, %tlumiennosci */ - double SpringKC = 1.0; - double beta = 0.0; double DmaxB = 0.1; /*tolerancja scisku/rozciagania, sila rozerwania*/ double FmaxB = 1000.0; + double SpringKC = 1.0; double DmaxC = 0.1; double FmaxC = 1000.0; - TCouplerType CouplerType = TCouplerType::NoCoupler; /*typ sprzegu*/ - /*zmienne*/ + double beta = 0.0; + TCouplerType CouplerType = TCouplerType::NoCoupler; /*typ sprzegu*/ + int AllowedFlag = 3; //Ra: maska dostępnych + /*zmienne*/ int CouplingFlag = 0; /*0 - wirtualnie, 1 - sprzegi, 2 - pneumatycznie, 4 - sterowanie, 8 - kabel mocy*/ - int AllowedFlag = 3; //Ra: znaczenie jak wyżej, maska dostępnych - bool Render = false; /*ABu: czy rysowac jak zaczepiony sprzeg*/ - double CoupleDist = 0.0; /*ABu: optymalizacja - liczenie odleglosci raz na klatkę, bez iteracji*/ class TMoverParameters *Connected = nullptr; /*co jest podlaczone*/ int ConnectedNr = 0; //Ra: od której strony podłączony do (Connected): 0=przód, 1=tył double CForce = 0.0; /*sila z jaka dzialal*/ @@ -638,16 +636,26 @@ struct TCoupling { bool CheckCollision = false; /*czy sprawdzac sile czy pedy*/ power_coupling power_high; - power_coupling power_low; // TODO: implement this +// power_coupling power_low; // TODO: implement this int sounds { 0 }; // sounds emitted by the coupling devices + bool Render = false; /*ABu: czy rysowac jak zaczepiony sprzeg*/ }; +class TDynamicObject; +struct neighbour_data { + TDynamicObject *vehicle { nullptr }; // detected obstacle + int vehicle_end { -1 }; // facing end of the obstacle + float distance { 10000.f }; // distance to the obstacle // NOTE: legacy value. TBD, TODO: use standard -1 instead? +}; + + + class TMoverParameters { // Ra: wrapper na kod pascalowy, przejmujący jego funkcje Q: 20160824 - juz nie wrapper a klasa bazowa :) private: // types - +/* TODO: implement // communication cable, exchanging control signals with adjacent vehicle struct jumper_cable { // types @@ -661,7 +669,7 @@ private: // integers // std::array values {}; }; - +*/ // basic approximation of a generic device // TBD: inheritance or composition? struct basic_device { @@ -726,6 +734,8 @@ private: bool is_closing { false }; // the door is currently closing bool is_opening { false }; // the door is currently opening bool is_open { false }; // the door is fully open + bool step_folding { false }; // the doorstep is currently closing + bool step_unfolding { false }; // the doorstep is currently opening }; struct door_data { @@ -751,6 +761,7 @@ private: std::vector permit_presets; // permit presets selectable with preset switch // ld inputs bool lock_enabled { true }; + bool step_enabled { true }; // internal data int permit_preset { -1 }; // curent position of preset selection switch // vehicle parts @@ -828,14 +839,12 @@ private: public: double dMoveLen = 0.0; - std::string filename; /*---opis lokomotywy, wagonu itp*/ /*--opis serii--*/ int CategoryFlag = 1; /*1 - pociag, 2 - samochod, 4 - statek, 8 - samolot*/ /*--sekcja stalych typowych parametrow*/ std::string TypeName; /*nazwa serii/typu*/ - //TrainType: string; {typ: EZT/elektrowoz - Winger 040304} - int TrainType = 0; /*Ra: powinno być szybciej niż string*/ + int TrainType = 0; /*typ: EZT/elektrowoz - Winger 040304 Ra: powinno być szybciej niż string*/ TEngineType EngineType = TEngineType::None; /*typ napedu*/ TPowerParameters EnginePowerSource; /*zrodlo mocy dla silnikow*/ TPowerParameters SystemPowerSource; /*zrodlo mocy dla systemow sterowania/przetwornic/sprezarek*/ @@ -968,16 +977,23 @@ public: int DynamicBrakeType = 0; /*patrz dbrake_**/ int DynamicBrakeAmpmeters = 2; /*liczba amperomierzy przy hamowaniu ED*/ double DynamicBrakeRes = 5.8; /*rezystancja oporników przy hamowaniu ED*/ - double TUHEX_Sum = 750; /*nastawa sterownika hamowania ED*/ + double DynamicBrakeRes1 = 5.8; /*rezystancja oporników przy hamowaniu ED - 1 tryb*/ + double DynamicBrakeRes2 = 5.8; /*rezystancja oporników przy hamowaniu ED - 2 tryb*/ + double TUHEX_Sum = 750; /*nastawa sterownika hamowania ED - aktualna*/ double TUHEX_Diff = 10; /*histereza działania sterownika hamowania ED*/ double TUHEX_MinIw = 60; /*minimalny prąd wzbudzenia przy hamowaniu ED - wynika z chk silnika*/ double TUHEX_MaxIw = 400; /*maksymalny prąd wzbudzenia przy hamowaniu ED - ograniczenie max siły*/ - + double TUHEX_Sum1 = 750; /*nastawa1 sterownika hamowania ED*/ + double TUHEX_Sum2 = 750; /*nastawa2 sterownika hamowania ED*/ + double TUHEX_Sum3 = 750; /*nastawa3 sterownika hamowania ED*/ + int TUHEX_Stages = 0; /*liczba stopni hamowania ED*/ + // TODO: wrap resistor fans variables and state into a device int RVentType = 0; /*0 - brak, 1 - jest, 2 - automatycznie wlaczany*/ double RVentnmax = 1.0; /*maks. obroty wentylatorow oporow rozruchowych*/ double RVentCutOff = 0.0; /*rezystancja wylaczania wentylatorow dla RVentType=2*/ double RVentSpeed { 0.5 }; //rozpedzanie sie wentylatora obr/s^2} double RVentMinI { 50.0 }; //przy jakim pradzie sie wylaczaja} + bool RVentForceOn { false }; // forced activation switch int CompressorPower = 1; // 0: main circuit, 1: z przetwornicy, reczne, 2: w przetwornicy, stale, 3: diesel engine, 4: converter of unit in front, 5: converter of unit behind int SmallCompressorPower = 0; /*Winger ZROBIC*/ bool Trafo = false; /*pojazd wyposażony w transformator*/ @@ -1051,6 +1067,12 @@ public: bool MED_EPVC = 0; // czy korekcja sily hamowania EP, gdy nie ma dostepnego ED double MED_EPVC_Time = 7; // czas korekcji sily hamowania EP, gdy nie ma dostepnego ED bool MED_Ncor = 0; // czy korekcja sily hamowania z uwzglednieniem nacisku + + int DCEMUED_CC; //na którym sprzęgu sprawdzać działanie ED + double DCEMUED_EP_max_Vel; //maksymalna prędkość, przy której działa EP przy włączonym ED w jednostce (dla tocznych) + double DCEMUED_EP_min_Im; //minimalny prąd, przy którym EP nie działa przy włączonym ED w członie (dla silnikowych) + double DCEMUED_EP_delay; //opóźnienie włączenia hamulca EP przy hamowaniu ED - zwłoka wstępna + /*-dla wagonow*/ struct load_attributes { std::string name; // name of the cargo @@ -1087,6 +1109,7 @@ public: TRotation Rot { 0.0, 0.0, 0.0 }; std::string Name; /*nazwa wlasna*/ TCoupling Couplers[2]; //urzadzenia zderzno-sprzegowe, polaczenia miedzy wagonami + std::array Neighbours; // potential collision sources bool EventFlag = false; /*!o true jesli cos nietypowego sie wydarzy*/ int SoundFlag = 0; /*!o patrz stale sound_ */ double DistCounter = 0.0; /*! licznik kilometrow */ @@ -1159,6 +1182,10 @@ public: int BrakeOpModeFlag = 0; /*nastawa trybu pracy PS/PN/EP/MED 1/2/4/8*/ int BrakeOpModes = 0; /*nastawy mozliwe do uzyskania*/ bool DynamicBrakeFlag = false; /*czy wlaczony hamulec elektrodymiczny*/ + bool DynamicBrakeEMUStatus = true; /*czy hamulec ED dziala w ezt*/ + int TUHEX_StageActual = 0; /*aktualny stopien tuhexa*/ + bool TUHEX_ResChange = false; /*czy zmiana rezystancji hamowania ED - odwzbudzanie*/ + bool TUHEX_Active = false; /*czy hamowanie ED wywołane z zewnątrz*/ // NapUdWsp: integer; double LimPipePress = 0.0; /*stabilizator cisnienia*/ double ActFlowSpeed = 0.0; /*szybkosc stabilizatora*/ @@ -1265,9 +1292,14 @@ public: double hydro_R_n = 0.0; /*predkosc obrotowa retardera*/ /*- zmienne dla lokomotyw z silnikami indukcyjnymi -*/ + double eimic = 0; /*aktualna pozycja zintegrowanego sterowania jazda i hamowaniem*/ + double eimic_real = 0; /*faktycznie uzywana pozycja zintegrowanego sterowania jazda i hamowaniem*/ + int EIMCtrlType = 0; /*rodzaj wariantu zadajnika jazdy*/ + double eimv_pr = 0; /*realizowany procent dostepnej sily rozruchu/hamowania*/ double eimv[21]; static std::vector const eimv_labels; double SpeedCtrlTimer = 0; /*zegar dzialania tempomatu z wybieralna predkoscia*/ + double eimicSpeedCtrl = 0; /*pozycja sugerowana przez tempomat*/ double NewSpeed = 0; /*nowa predkosc do zadania*/ double MED_EPVC_CurrentTime = 0; /*aktualny czas licznika czasu korekcji siły EP*/ @@ -1317,24 +1349,19 @@ public: double FrictConst2d= 0.0; double TotalMassxg = 0.0; /*TotalMass*g*/ - Math3D::vector3 vCoulpler[2]; // powtórzenie współrzędnych sprzęgów z DynObj :/ double fBrakeCtrlPos = -2.0; // płynna nastawa hamulca zespolonego bool bPantKurek3 = true; // kurek trójdrogowy (pantografu): true=połączenie z ZG, false=połączenie z małą sprężarką // domyślnie zbiornik pantografu połączony jest ze zbiornikiem głównym int iProblem = 0; // flagi problemów z taborem, aby AI nie musiało porównywać; 0=może jechać int iLights[2]; // bity zapalonych świateł tutaj, żeby dało się liczyć pobór prądu -private: - double CouplerDist(int Coupler); public: TMoverParameters(double VelInitial, std::string TypeNameInit, std::string NameInit, int Cab); // obsługa sprzęgów - double Distance(const TLocation &Loc1, const TLocation &Loc2, const TDimension &Dim1, const TDimension &Dim2); -/* double Distance(const vector3 &Loc1, const vector3 &Loc2, const vector3 &Dim1, const vector3 &Dim2); -*/ //bool AttachA(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced = false); + static double CouplerDist( TMoverParameters const *Left, TMoverParameters const *Right ); + static double Distance(const TLocation &Loc1, const TLocation &Loc2, const TDimension &Dim1, const TDimension &Dim2); bool Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced = false, bool Audible = true); int DettachStatus(int ConnectNo); bool Dettach(int ConnectNo); - void SetCoupleDist(); bool DirectionForward(); void BrakeLevelSet(double b); bool BrakeLevelAdd(double b); @@ -1350,7 +1377,7 @@ public: // Q ******************************************************************************************* double GetTrainsetVoltage(void); - bool Physic_ReActivation(void); + bool switch_physics(bool const State); double LocalBrakeRatio(void); double ManualBrakeRatio(void); double PipeRatio(void);/*ile napelniac*/ @@ -1421,17 +1448,17 @@ public: /*funkcje obliczajace sily*/ void ComputeConstans(void);//ABu: wczesniejsze wyznaczenie stalych dla liczenia sil double ComputeMass(void); - void ComputeTotalForce(double dt, double dt1, bool FullVer); + void ComputeTotalForce(double dt); double Adhesive(double staticfriction) const; double TractionForce(double dt); double FrictionForce(double R, int TDamage); double BrakeForceR(double ratio, double velocity); double BrakeForceP(double press, double velocity); double BrakeForce(const TTrackParam &Track); - double CouplerForce(int CouplerN, double dt); + double CouplerForce(int const End, double dt); void CollisionDetect(int CouplerN, double dt); /*obrot kol uwzgledniajacy poslizg*/ - double ComputeRotatingWheel(double WForce, double dt, double n); + double ComputeRotatingWheel(double WForce, double dt, double n) const; /*--funkcje dla lokomotyw*/ bool DirectionBackward(void);/*! kierunek ruchu*/ @@ -1485,6 +1512,9 @@ public: bool PantFront( bool const State, range_t const Notify = range_t::consist ); //obsluga pantografou przedniego bool PantRear( bool const State, range_t const Notify = range_t::consist ); //obsluga pantografu tylnego + void CheckEIMIC(double dt); //sprawdzenie i zmiana nastawy zintegrowanego nastawnika jazdy/hamowania + void CheckSpeedCtrl(); + /*-funkcje typowe dla lokomotywy spalinowej z przekladnia mechaniczna*/ bool dizel_EngageSwitch(double state); bool dizel_EngageChange(double dt); @@ -1501,6 +1531,7 @@ public: bool LoadingDone(double LSpeed, std::string const &Loadname); bool PermitDoors( side const Door, bool const State = true, range_t const Notify = range_t::consist ); bool ChangeDoorPermitPreset( int const Change, range_t const Notify = range_t::consist ); + bool PermitDoorStep( bool const State, range_t const Notify = range_t::consist ); bool OperateDoors( side const Door, bool const State, range_t const Notify = range_t::consist ); bool LockDoors( bool const State, range_t const Notify = range_t::consist ); bool signal_departure( bool const State, range_t const Notify = range_t::consist ); // toggles departure warning @@ -1524,6 +1555,7 @@ private: void LoadFIZ_TurboPos( std::string const &line ); void LoadFIZ_Cntrl( std::string const &line ); void LoadFIZ_Blending(std::string const &line); + void LoadFIZ_DCEMUED(std::string const &line); void LoadFIZ_Light( std::string const &line ); void LoadFIZ_Clima( std::string const &line ); void LoadFIZ_Power( std::string const &Line ); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 4c7217b4..a48a86b5 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "MOVER.h" +#include "DynObj.h" #include "Oerlikon_ESt.h" #include "utilities.h" #include "Globals.h" @@ -205,7 +206,7 @@ double TMoverParameters::Current(double n, double U) MotorCurrent = 0; // i dzialanie hamulca ED w EP09 - if (DynamicBrakeType == dbrake_automatic) + if ((DynamicBrakeType == dbrake_automatic)&&(TrainType != dt_EZT)) { if (((Hamulec->GetEDBCP() < 0.25) && (Vadd < 1)) || (BrakePress > 2.1)) DynamicBrakeFlag = false; @@ -213,6 +214,10 @@ double TMoverParameters::Current(double n, double U) DynamicBrakeFlag = true; DynamicBrakeFlag = (DynamicBrakeFlag && ConverterFlag); } + if ((DynamicBrakeType == dbrake_automatic) && (TrainType == dt_EZT)) + { + DynamicBrakeFlag = (ConverterFlag && (TUHEX_Active || (Vadd>TUHEX_MinIw)) && DynamicBrakeEMUStatus); + } // wylacznik cisnieniowy yBARC - to jest chyba niepotrzebne tutaj Q: no to usuwam... @@ -254,7 +259,7 @@ double TMoverParameters::Current(double n, double U) if (DynamicBrakeFlag && (!FuseFlag) && (DynamicBrakeType == dbrake_automatic) && ConverterFlag && Mains) // hamowanie EP09 //TUHEX { - // TODO: zrobic bardziej uniwersalne nie tylko dla EP09 + // TODO: zrobic bardziej uniwersalne nie tylko dla EP09 MotorCurrent = -Max0R(MotorParam[0].fi * (Vadd / (Vadd + MotorParam[0].Isat) - MotorParam[0].fi0), 0) * n * 2.0 / DynamicBrakeRes; } @@ -476,168 +481,118 @@ double TMoverParameters::Distance(const TLocation &Loc1, const TLocation &Loc2, { // zwraca odległość pomiędzy pojazdami (Loc1) i (Loc2) z uwzględnieneim ich długości (kule!) return hypot(Loc2.X - Loc1.X, Loc1.Y - Loc2.Y) - 0.5 * (Dim2.L + Dim1.L); }; -/* -double TMoverParameters::Distance(const vector3 &s1, const vector3 &s2, const vector3 &d1, - const vector3 &d2){ - // obliczenie odległości prostopadłościanów o środkach (s1) i (s2) i wymiarach (d1) i (d2) - // return 0.0; //będzie zgłaszać warning - funkcja do usunięcia, chyba że się przyda... -}; -*/ -double TMoverParameters::CouplerDist(int Coupler) -{ // obliczenie odległości pomiędzy sprzęgami (kula!) - Couplers[Coupler].CoupleDist = - Distance( - Loc, Couplers[Coupler].Connected->Loc, - Dim, Couplers[Coupler].Connected->Dim); // odległość pomiędzy sprzęgami (kula!) - return Couplers[ Coupler ].CoupleDist; +double TMoverParameters::CouplerDist(TMoverParameters const *Left, TMoverParameters const *Right) +{ // obliczenie odległości pomiędzy sprzęgami (kula!) + return + Distance( + Left->Loc, Right->Loc, + Left->Dim, Right->Dim); // odległość pomiędzy sprzęgami (kula!) }; bool TMoverParameters::Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced, bool Audible) { //łączenie do swojego sprzęgu (ConnectNo) pojazdu (ConnectTo) stroną (ConnectToNr) // Ra: zwykle wykonywane dwukrotnie, dla każdego pojazdu oddzielnie - // Ra: trzeba by odróżnić wymóg dociśnięcia od uszkodzenia sprzęgu przy podczepianiu AI do - // składu - if (ConnectTo) // jeśli nie pusty - { - auto &coupler = Couplers[ ConnectNo ]; - if (ConnectToNr != 2) - coupler.ConnectedNr = ConnectToNr; // 2=nic nie podłączone - coupler.Connected = ConnectTo; // tak podpiąć (do siebie) zawsze można, najwyżej będzie wirtualny - CouplerDist( ConnectNo ); // przeliczenie odległości pomiędzy sprzęgami + // Ra: trzeba by odróżnić wymóg dociśnięcia od uszkodzenia sprzęgu przy podczepianiu AI do składu - if (CouplingType == coupling::faux) - return false; // wirtualny więcej nic nie robi - - auto &othercoupler = ConnectTo->Couplers[ coupler.ConnectedNr ]; - if( ( Forced ) - || ( ( coupler.CoupleDist <= dEpsilon ) - && ( coupler.CouplerType != TCouplerType::NoCoupler ) - && ( coupler.CouplerType == othercoupler.CouplerType ) ) ) - { // stykaja sie zderzaki i kompatybilne typy sprzegow, chyba że łączenie na starcie - if( coupler.CouplingFlag == ctrain_virtual ) { - // jeśli wcześniej nie było połączone, ustalenie z której strony rysować sprzęg - coupler.Render = true; // tego rysować - othercoupler.Render = false; // a tego nie - }; - auto const couplingchange { CouplingType ^ coupler.CouplingFlag }; - coupler.CouplingFlag = CouplingType; // ustawienie typu sprzęgu - // if (CouplingType!=ctrain_virtual) //Ra: wirtualnego nie łączymy zwrotnie! - //{//jeśli łączenie sprzęgiem niewirtualnym, ustawiamy połączenie zwrotne - othercoupler.CouplingFlag = CouplingType; - othercoupler.Connected = this; - othercoupler.CoupleDist = coupler.CoupleDist; - - if( ( true == Audible ) && ( couplingchange != 0 ) ) { - // set sound event flag - int soundflag{ sound::none }; - std::vector> const soundmappings = { - { coupling::coupler, sound::attachcoupler }, - { coupling::brakehose, sound::attachbrakehose }, - { coupling::mainhose, sound::attachmainhose }, - { coupling::control, sound::attachcontrol}, - { coupling::gangway, sound::attachgangway}, - { coupling::heating, sound::attachheating} }; - for( auto const &soundmapping : soundmappings ) { - if( ( couplingchange & soundmapping.first ) != 0 ) { - soundflag |= soundmapping.second; - } - } - SetFlag( coupler.sounds, soundflag ); - } - - return true; - //} - // podłączenie nie udało się - jest wirtualne - } + if( ( ConnectTo == nullptr ) + || ( CouplingType == coupling::faux ) ) { + return false; } - return false; // brak podłączanego pojazdu, zbyt duża odległość, niezgodny typ sprzęgu, brak - // sprzęgu, brak haka -}; + + auto &coupler { Couplers[ ConnectNo ] }; + auto &othercoupler = ConnectTo->Couplers[ ( ConnectToNr != 2 ? ConnectToNr : coupler.ConnectedNr ) ]; + auto const distance { CouplerDist( this, ConnectTo ) }; -// to jest już niepotrzebne bo nie ma Delphi -//bool TMoverParameters::Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, -// int CouplingType, bool Forced) -//{ //łączenie do (ConnectNo) pojazdu (ConnectTo) stroną (ConnectToNr) -// return Attach(ConnectNo, ConnectToNr, (TMoverParameters *)ConnectTo, CouplingType, Forced); -//}; + auto const couplercheck { + ( Forced ) + || ( ( distance <= dEpsilon ) + && ( coupler.CouplerType != TCouplerType::NoCoupler ) + && ( coupler.CouplerType == othercoupler.CouplerType ) ) }; + + if( false == couplercheck ) { return false; } + + // stykaja sie zderzaki i kompatybilne typy sprzegow, chyba że łączenie na starcie + if( coupler.CouplingFlag == coupling::faux ) { + // jeśli wcześniej nie było połączone, ustalenie z której strony rysować sprzęg + coupler.Render = true; // tego rysować + othercoupler.Render = false; // a tego nie + }; + auto const couplingchange { CouplingType ^ coupler.CouplingFlag }; + coupler.Connected = ConnectTo; + coupler.CouplingFlag = CouplingType; // ustawienie typu sprzęgu + if( ConnectToNr != 2 ) { + coupler.ConnectedNr = ConnectToNr; // 2=nic nie podłączone + } + othercoupler.Connected = this; + othercoupler.CouplingFlag = CouplingType; + othercoupler.ConnectedNr = ConnectNo; + + if( ( true == Audible ) && ( couplingchange != 0 ) ) { + // set sound event flag + int soundflag{ sound::none }; + std::vector> const soundmappings = { + { coupling::coupler, sound::attachcoupler }, + { coupling::brakehose, sound::attachbrakehose }, + { coupling::mainhose, sound::attachmainhose }, + { coupling::control, sound::attachcontrol}, + { coupling::gangway, sound::attachgangway}, + { coupling::heating, sound::attachheating} }; + for( auto const &soundmapping : soundmappings ) { + if( ( couplingchange & soundmapping.first ) != 0 ) { + soundflag |= soundmapping.second; + } + } + SetFlag( coupler.sounds, soundflag ); + } + + return true; +} int TMoverParameters::DettachStatus(int ConnectNo) { // Ra: sprawdzenie, czy odległość jest dobra do rozłączania - // powinny być 3 informacje: =0 sprzęg już rozłączony, <0 da się rozłączyć. >0 nie da się - // rozłączyć + // powinny być 3 informacje: =0 sprzęg już rozłączony, <0 da się rozłączyć. >0 nie da się rozłączyć if (!Couplers[ConnectNo].Connected) return 0; // nie ma nic, to rozłączanie jest OK if ((Couplers[ConnectNo].CouplingFlag & ctrain_coupler) == 0) return -Couplers[ConnectNo].CouplingFlag; // hak nie połączony - rozłączanie jest OK if (TestFlag(DamageFlag, dtrain_coupling)) return -Couplers[ConnectNo].CouplingFlag; // hak urwany - rozłączanie jest OK - // ABu021104: zakomentowane 'and (CouplerType<>Articulated)' w warunku, nie wiem co to bylo, ale - // za to teraz dziala odczepianie... :) } - // if (CouplerType==Articulated) return false; //sprzęg nie do rozpięcia - może być tylko urwany - // Couplers[ConnectNo].CoupleDist=Distance(Loc,Couplers[ConnectNo].Connected->Loc,Dim,Couplers[ConnectNo].Connected->Dim); - CouplerDist(ConnectNo); - if (Couplers[ConnectNo].CouplerType == TCouplerType::Screw ? Couplers[ConnectNo].CoupleDist < 0.01 : true) +// CouplerDist(ConnectNo); + if (Couplers[ConnectNo].CouplerType == TCouplerType::Screw ? Neighbours[ConnectNo].distance < 0.01 : true) return -Couplers[ConnectNo].CouplingFlag; // można rozłączać, jeśli dociśnięty - return (Couplers[ConnectNo].CoupleDist > 0.2) ? -Couplers[ConnectNo].CouplingFlag : + return (Neighbours[ConnectNo].distance > 0.2) ? -Couplers[ConnectNo].CouplingFlag : Couplers[ConnectNo].CouplingFlag; }; bool TMoverParameters::Dettach(int ConnectNo) { // rozlaczanie - if (!Couplers[ConnectNo].Connected) - return true; // nie ma nic, to odczepiono - // with Couplers[ConnectNo] do - int i = DettachStatus(ConnectNo); // stan sprzęgu - if (i < 0) - { // gdy scisniete zderzaki, chyba ze zerwany sprzeg (wirtualnego nie odpinamy z drugiej strony) - // Couplers[ConnectNo].Connected=NULL; //lepiej zostawic bo przeciez trzeba kontrolowac - // zderzenia odczepionych - Couplers[ConnectNo].Connected->Couplers[Couplers[ConnectNo].ConnectedNr].CouplingFlag = - 0; // pozostaje sprzęg wirtualny - Couplers[ConnectNo].CouplingFlag = 0; // pozostaje sprzęg wirtualny + auto &coupler { Couplers[ ConnectNo ] }; + auto &othervehicle { coupler.Connected }; + auto &othercoupler { othervehicle->Couplers[ coupler.ConnectedNr ] }; + + if( othervehicle == nullptr ) { return true; } // nie ma nic, to odczepiono + + auto const i = DettachStatus(ConnectNo); // stan sprzęgu + if (i < 0) { + // gdy scisniete zderzaki, chyba ze zerwany sprzeg (wirtualnego nie odpinamy z drugiej strony) + std::tie( coupler.Connected, coupler.ConnectedNr, coupler.CouplingFlag ) + = std::tie( othercoupler.Connected, othercoupler.ConnectedNr, othercoupler.CouplingFlag ) + = std::make_tuple( nullptr, -1, coupling::faux ); // set sound event flag - SetFlag( Couplers[ ConnectNo ].sounds, sound::detachall ); + SetFlag( coupler.sounds, sound::detachall ); return true; } else if (i > 0) - { // odłączamy węże i resztę, pozostaje sprzęg fizyczny, który wymaga dociśnięcia (z wirtualnym - // nic) - Couplers[ConnectNo].CouplingFlag &= ctrain_coupler; - Couplers[ConnectNo].Connected->Couplers[Couplers[ConnectNo].ConnectedNr].CouplingFlag = - Couplers[ConnectNo].CouplingFlag; + { // odłączamy węże i resztę, pozostaje sprzęg fizyczny, który wymaga dociśnięcia (z wirtualnym nic) + coupler.CouplingFlag &= coupling::coupler; + othercoupler.CouplingFlag &= coupling::coupler; } return false; // jeszcze nie rozłączony }; -// przeliczenie odległości sprzęgów -void TMoverParameters::SetCoupleDist() { -/* - double const MaxDist = 100.0; // 2x average max proximity distance. TODO: rearrange ito something more elegant -*/ - for( int coupleridx = 0; coupleridx <= 1; ++coupleridx ) { - - if( Couplers[ coupleridx ].Connected == nullptr ) { continue; } - - CouplerDist( coupleridx ); - if( CategoryFlag & 2 ) { - // Ra: dla samochodów zderzanie kul to za mało - // NOTE: whatever calculation was supposed to be here, ain't - } -/* - if( ( Couplers[ coupleridx ].CouplingFlag == coupling::faux ) - && ( Couplers[ coupleridx ].CoupleDist > MaxDist ) ) { - // zerwij kontrolnie wirtualny sprzeg - // Connected.Couplers[CNext].Connected:=nil; //Ra: ten podłączony niekoniecznie jest wirtualny - Couplers[ coupleridx ].Connected = nullptr; - Couplers[ coupleridx ].ConnectedNr = 2; - } -*/ - } -}; - bool TMoverParameters::DirectionForward() { if ((MainCtrlPosNo > 0) && (ActiveDir < 1) && (MainCtrlPos == 0)) @@ -1157,7 +1112,8 @@ void TMoverParameters::CollisionDetect(int CouplerN, double dt) CCF = ComputeCollision( coupler.Connected->V, - V, coupler.Connected->TotalMass, + V, + coupler.Connected->TotalMass, TotalMass, (coupler.beta + coupler.Connected->Couplers[coupler.ConnectedNr].beta) / 2.0, VirtualCoupling) @@ -1551,6 +1507,9 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { UpdatePipePressure(Deltatime); UpdateBatteryVoltage(Deltatime); UpdateScndPipePressure(Deltatime); // druga rurka, youBy + + if( ( ( DCEMUED_CC & 1 ) != 0 ) && ( ( Couplers[ end::front ].CouplingFlag & coupling::control ) != 0 ) ) { DynamicBrakeEMUStatus &= Couplers[ end::front ].Connected->DynamicBrakeEMUStatus; } + if( ( ( DCEMUED_CC & 2 ) != 0 ) && ( ( Couplers[ end::rear ].CouplingFlag & coupling::control ) != 0 ) ) { DynamicBrakeEMUStatus &= Couplers[ end::rear ].Connected->DynamicBrakeEMUStatus; } if( ( BrakeSlippingTimer > 0.8 ) && ( ASBType != 128 ) ) { // ASBSpeed=0.8 // hamulec antypoślizgowy - wyłączanie @@ -1747,7 +1706,7 @@ double TMoverParameters::ShowCurrent(int AmpN) const case 1: return WindingRes * Mm / Vadd; case 2: - return dizel_fill * WindingRes; + return eimv_pr * WindingRes; default: return ShowCurrentP(AmpN); // T_MoverParameters:: } @@ -3626,6 +3585,7 @@ void TMoverParameters::UpdatePipePressure(double dt) } case TBrakeValve::EP2: + case TBrakeValve::EP1: { Hamulec->PLC( TotalMass-Mred ); break; @@ -3668,7 +3628,7 @@ void TMoverParameters::UpdatePipePressure(double dt) } } // switch - if ((BrakeHandle == TBrakeHandle::FVel6) && (ActiveCab != 0)) + if (((BrakeHandle == TBrakeHandle::FVel6)||(BrakeHandle == TBrakeHandle::FVE408)) && (ActiveCab != 0)) { if ((Battery) && (ActiveDir != 0) @@ -3677,7 +3637,16 @@ void TMoverParameters::UpdatePipePressure(double dt) temp = Handle->GetCP(); else temp = 0.0; - Hamulec->SetEPS(temp); + if (temp < 0.001) + DynamicBrakeEMUStatus = true; + double temp1 = temp; + if ((DCEMUED_EP_max_Vel > 0.001) && (Vel > DCEMUED_EP_max_Vel) && (DynamicBrakeEMUStatus)) + temp1 = 0; + if ((DCEMUED_EP_min_Im > 0.001) && (abs(Im) > DCEMUED_EP_min_Im) && (DynamicBrakeEMUStatus)) + temp1 = 0; + Hamulec->SetEPS(temp1); + TUHEX_StageActual = temp; + TUHEX_Active = TUHEX_StageActual > 0; // Ra 2014-11: na tym się wysypuje, ale nie wiem, w jakich warunkach SendCtrlToNext("Brake", temp, CabNo); } @@ -3737,7 +3706,7 @@ void TMoverParameters::UpdateScndPipePressure(double dt) c = Couplers[0].Connected; // skrot dv1 = 0.5 * dt * PF(ScndPipePress, c->ScndPipePress, Spz * 0.75); if (dv1 * dv1 > 0.00000000000001) - c->Physic_ReActivation(); + c->switch_physics( true ); c->Pipe2->Flow(-dv1); } // sprzeg 2 @@ -3747,7 +3716,7 @@ void TMoverParameters::UpdateScndPipePressure(double dt) c = Couplers[1].Connected; // skrot dv2 = 0.5 * dt * PF(ScndPipePress, c->ScndPipePress, Spz * 0.75); if (dv2 * dv2 > 0.00000000000001) - c->Physic_ReActivation(); + c->switch_physics( true ); c->Pipe2->Flow(-dv2); } if ((Couplers[1].Connected != NULL) && (Couplers[0].Connected != NULL)) @@ -3798,7 +3767,7 @@ double TMoverParameters::GetDVc(double dt) c = Couplers[0].Connected; // skrot //0.08 //e/D * L/D = e/D^2 * L dv1 = 0.5 * dt * PF(PipePress, c->PipePress, (Spg) / (1.0 + 0.015 / Spg * Dim.L)); if (dv1 * dv1 > 0.00000000000001) - c->Physic_ReActivation(); + c->switch_physics( true ); c->Pipe->Flow(-dv1); } // sprzeg 2 @@ -3808,7 +3777,7 @@ double TMoverParameters::GetDVc(double dt) c = Couplers[1].Connected; // skrot dv2 = 0.5 * dt * PF(PipePress, c->PipePress, (Spg) / (1.0 + 0.015 / Spg * Dim.L)); if (dv2 * dv2 > 0.00000000000001) - c->Physic_ReActivation(); + c->switch_physics( true ); c->Pipe->Flow(-dv2); } //if ((Couplers[1].Connected != NULL) && (Couplers[0].Connected != NULL)) @@ -3904,143 +3873,138 @@ double TMoverParameters::ComputeMass(void) // Q: 20160713 // Obliczanie wypadkowej siły z wszystkich działających sił // ************************************************************************************************* -void TMoverParameters::ComputeTotalForce(double dt, double dt1, bool FullVer) -{ - int b; - double Fwheels = 0.0; +// TBD, TODO: move some of the calculations out of the method, they're relevant to more than just force calculations +void TMoverParameters::ComputeTotalForce(double dt) { - if (PhysicActivation) - { - // EventFlag:=false; {jesli cos sie zlego - // wydarzy to ustawiane na true} - // SoundFlag:=0; {jesli ma byc jakis dzwiek - // to zapalany jet odpowiedni bit} - // to powinno byc zerowane na zewnatrz - - // juz zoptymalizowane: - FStand = FrictionForce(RunningShape.R, RunningTrack.DamageFlag); // siła oporów ruchu - Vel = abs(V) * 3.6; // prędkość w km/h - nrot = v2n(); // przeliczenie prędkości liniowej na obrotową - - if( (true == TestFlag(BrakeMethod, bp_MHS)) - && (PipePress < 3.0) - && (Vel > 45) - && (true == TestFlag(BrakeDelayFlag, bdelay_M))) // ustawione na sztywno na 3 bar - FStand += TrackBrakeForce; // doliczenie hamowania hamulcem szynowym - // w charakterystykach jest wartość siły hamowania zamiast nacisku - // if(FullVer=true) then - // ABu: to dla optymalizacji, bo chyba te rzeczy wystarczy sprawdzac 1 raz na - // klatke? - LastSwitchingTime += dt1; - if (EngineType == TEngineType::ElectricSeriesMotor) - LastRelayTime += dt1; - if( Mains && /*(abs(CabNo) < 2) &&*/ ( EngineType == TEngineType::ElectricSeriesMotor ) ) // potem ulepszyc! pantogtrafy! - { // Ra 2014-03: uwzględnienie kierunku jazdy w napięciu na silnikach, a powinien być - // zdefiniowany nawrotnik - if( CabNo == 0 ) - Voltage = RunningTraction.TractionVoltage * ActiveDir; - else - Voltage = RunningTraction.TractionVoltage * DirAbsolute; // ActiveDir*CabNo; - } // bo nie dzialalo - else if( ( EngineType == TEngineType::ElectricInductionMotor ) - || ( ( ( Couplers[ end::front ].CouplingFlag & ctrain_power ) == ctrain_power ) - || ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power ) == ctrain_power ) ) ) { - // potem ulepszyc! pantogtrafy! - Voltage = - std::max( - RunningTraction.TractionVoltage, - std::max( - Couplers[ end::front ].power_high.voltage, - Couplers[ end::rear ].power_high.voltage ) ); - } - else { - Voltage = 0; - } - - if (Power > 0) - FTrain = TractionForce(dt); - else - FTrain = 0; - - Fb = BrakeForce(RunningTrack); - Fwheels = FTrain - Fb * Sign(V); - if( ( Vel > 0.001 ) // crude trap, to prevent braked stationary vehicles from passing fb > mass * adhesive test - && ( std::abs(Fwheels) > TotalMassxg * Adhesive( RunningTrack.friction ) ) ) // poslizg - { - SlippingWheels = true; - } - if( true == SlippingWheels ) { - // TrainForce:=TrainForce-Fb; - double temp_nrot = ComputeRotatingWheel(Fwheels - - Sign(nrot * M_PI * WheelDiameter - V) * - Adhesive(RunningTrack.friction) * TotalMassxg, - dt, nrot); - Fwheels = Sign(temp_nrot * M_PI * WheelDiameter - V) * TotalMassxg * Adhesive(RunningTrack.friction); - if (Fwheels*Sign(V)>0) - { - FTrain = Fwheels + Fb*Sign(V); - } - else if (FTrain*Sign(V)>0) - { - Fb = FTrain*Sign(V) - Fwheels*Sign(V); - } - else - { - Fb = -Fwheels*Sign(V); - FTrain = 0; - } - if (nrot < 0.1) - { - WheelFlat = sqrt(square(WheelFlat) + abs(Fwheels) / NAxles*Vel*0.000002); - } - if (Sign(nrot * M_PI * WheelDiameter - V)*Sign(temp_nrot * M_PI * WheelDiameter - V) < 0) - { - SlippingWheels = false; - temp_nrot = V / M_PI / WheelDiameter; - } - - - nrot = temp_nrot; - } - // else SlippingWheels:=false; - // FStand:=0; - for (b = 0; b < 2; ++b) - if (Couplers[b].Connected != NULL) /*and (Couplers[b].CouplerType<>Bare) and - (Couplers[b].CouplerType<>Articulated)*/ - { // doliczenie sił z innych pojazdów - Couplers[b].CForce = CouplerForce(b, dt); - FTrain += Couplers[b].CForce; - } - else - Couplers[b].CForce = 0; - // FStand:=Fb+FrictionForce(RunningShape.R,RunningTrack.DamageFlag); - FStand += Fb; - FTrain += - TotalMassxg * RunningShape.dHtrack; // doliczenie składowej stycznej grawitacji - //!niejawne przypisanie zmiennej! - FTotal = FTrain - Sign(V) * FStand; - } + Vel = std::abs(V) * 3.6; // prędkość w km/h // McZapkie-031103: sprawdzanie czy warto liczyc fizyke i inne updaty // ABu 300105: cos tu mieszalem , dziala teraz troche lepiej, wiec zostawiam - if ((CabNo == 0) && (Vel < 0.0001) && (abs(AccS) < 0.0001) && (TrainType != dt_EZT)) { - if (!PhysicActivation) - { - if (Couplers[0].Connected != NULL) - if ((Couplers[0].Connected->Vel > 0.0001) || - (abs(Couplers[0].Connected->AccS) > 0.0001)) - Physic_ReActivation(); - if (Couplers[1].Connected != NULL) - if ((Couplers[1].Connected->Vel > 0.0001) || - (abs(Couplers[1].Connected->AccS) > 0.0001)) - Physic_ReActivation(); - } - if (LastSwitchingTime > 5) // 10+Random(100) then - PhysicActivation = false; // zeby nie brac pod uwage braku V po uruchomieniu programu + auto const vehicleisactive { + ( CabNo != 0 ) + || ( Vel > 0.0001 ) + || ( std::abs( AccS ) > 0.0001 ) + || ( LastSwitchingTime < 5 ) + || ( TrainType == dt_EZT ) + || ( TrainType == dt_DMU ) }; + + auto const movingvehicleahead { + ( Neighbours[ end::front ].vehicle != nullptr ) + && ( ( Neighbours[ end::front ].vehicle->MoverParameters->Vel > 0.0001 ) + || ( std::abs( Neighbours[ end::front ].vehicle->MoverParameters->AccS ) > 0.0001 ) ) }; + + auto const movingvehiclebehind { + ( Neighbours[ end::rear ].vehicle != nullptr ) + && ( ( Neighbours[ end::rear ].vehicle->MoverParameters->Vel > 0.0001 ) + || ( std::abs( Neighbours[ end::rear ].vehicle->MoverParameters->AccS ) > 0.0001 ) ) }; + + auto const calculatephysics { vehicleisactive || movingvehicleahead || movingvehiclebehind }; + + switch_physics( calculatephysics ); } - else - PhysicActivation = true; + + if( false == PhysicActivation ) { return; } + + // juz zoptymalizowane: + FStand = FrictionForce(RunningShape.R, RunningTrack.DamageFlag); // siła oporów ruchu + nrot = v2n(); // przeliczenie prędkości liniowej na obrotową + + if( ( true == TestFlag( BrakeMethod, bp_MHS ) ) + && ( PipePress < 3.0 ) // ustawione na sztywno na 3 bar + && ( Vel > 45 ) + && ( true == TestFlag( BrakeDelayFlag, bdelay_M ) ) ) { + // doliczenie hamowania hamulcem szynowym + FStand += TrackBrakeForce; + } + // w charakterystykach jest wartość siły hamowania zamiast nacisku + + LastSwitchingTime += dt; + if( EngineType == TEngineType::ElectricSeriesMotor ) { + LastRelayTime += dt; + } + if( Mains && /*(abs(CabNo) < 2) &&*/ ( EngineType == TEngineType::ElectricSeriesMotor ) ) // potem ulepszyc! pantogtrafy! + { // Ra 2014-03: uwzględnienie kierunku jazdy w napięciu na silnikach, a powinien być zdefiniowany nawrotnik + if( CabNo == 0 ) + Voltage = RunningTraction.TractionVoltage * ActiveDir; + else + Voltage = RunningTraction.TractionVoltage * DirAbsolute; // ActiveDir*CabNo; + } // bo nie dzialalo + else if( ( EngineType == TEngineType::ElectricInductionMotor ) + || ( ( ( Couplers[ end::front ].CouplingFlag & ctrain_power ) == ctrain_power ) + || ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power ) == ctrain_power ) ) ) { + // potem ulepszyc! pantogtrafy! + Voltage = + std::max( + RunningTraction.TractionVoltage, + std::max( + Couplers[ end::front ].power_high.voltage, + Couplers[ end::rear ].power_high.voltage ) ); + } + else { + Voltage = 0; + } + + FTrain = ( + Power > 0 ? + TractionForce( dt ) : + 0 ); + + Fb = BrakeForce(RunningTrack); + // poslizg + auto Fwheels { FTrain - Fb * Sign( V ) }; + if( ( Vel > 0.001 ) // crude trap, to prevent braked stationary vehicles from passing fb > mass * adhesive test + && ( std::abs(Fwheels) > TotalMassxg * Adhesive( RunningTrack.friction ) ) ) { + SlippingWheels = true; + } + if( true == SlippingWheels ) { + + double temp_nrot = ComputeRotatingWheel(Fwheels - + Sign(nrot * M_PI * WheelDiameter - V) * + Adhesive(RunningTrack.friction) * TotalMassxg, + dt, nrot); + Fwheels = Sign(temp_nrot * M_PI * WheelDiameter - V) * TotalMassxg * Adhesive(RunningTrack.friction); + if (Fwheels*Sign(V)>0) + { + FTrain = Fwheels + Fb*Sign(V); + } + else if (FTrain*Sign(V)>0) + { + Fb = FTrain*Sign(V) - Fwheels*Sign(V); + } + else + { + Fb = -Fwheels*Sign(V); + FTrain = 0; + } + if (nrot < 0.1) + { + WheelFlat = sqrt(square(WheelFlat) + abs(Fwheels) / NAxles*Vel*0.000002); + } + if (Sign(nrot * M_PI * WheelDiameter - V)*Sign(temp_nrot * M_PI * WheelDiameter - V) < 0) + { + SlippingWheels = false; + temp_nrot = V / M_PI / WheelDiameter; + } + + + nrot = temp_nrot; + } + // doliczenie sił z innych pojazdów + for( int end = end::front; end <= end::rear; ++end ) { + if( Neighbours[ end ].vehicle != nullptr ) { + Couplers[ end ].CForce = CouplerForce( end, dt ); + FTrain += Couplers[ end ].CForce; + } + else + Couplers[ end ].CForce = 0; + } + + FStand += Fb; + // doliczenie składowej stycznej grawitacji + FTrain += TotalMassxg * RunningShape.dHtrack; + //!niejawne przypisanie zmiennej! + FTotal = FTrain - Sign(V) * FStand; } double TMoverParameters::BrakeForceR(double ratio, double velocity) @@ -4217,175 +4181,121 @@ double TMoverParameters::Adhesive(double staticfriction) const return adhesion; } -// poprawka dla liczenia sil przy ustawieniu przeciwnym obiektow: -/* -double DirPatch(int Coupler1, int Coupler2) -{ - if (Coupler1 != Coupler2) return 1; - else return -1; -} - - -double DirF(int CouplerN) -{ - double rDirF; - switch (CouplerN) - { - case 0: return -1; break; - case 1: return 1; break; - default: return 0; - } - // if (CouplerN == 0) return -1; -// else if (CouplerN == 0) return 1; -// else return 0; -} -*/ - // ************************************************************************************************* // Q: 20160713 // Obliczanie sił dzialających na sprzęgach // ************************************************************************************************* -double TMoverParameters::CouplerForce(int CouplerN, double dt) -{ - // wyliczenie siły na sprzęgu - double tempdist = 0, newdist = 0, distDelta = 0, CF = 0, dV = 0, absdV = 0, Fmax = 0; - double BetaAvg = 0; - int CNext = 0; - const double MaxDist = 405.0; // ustawione + 5 m, bo skanujemy do 400 m - const double MinDist = 0.5; // ustawione +.5 m, zeby nie rozlaczac przy malych odleglosciach - const int MaxCount = 1500; - bool rCF = false; - // distDelta:=0; //Ra: value never used - CNext = Couplers[CouplerN].ConnectedNr; - // if (Couplers[CouplerN].CForce == 0) //nie bylo uzgadniane wiec policz +double TMoverParameters::CouplerForce( int const End, double dt ) { - Couplers[CouplerN].CheckCollision = false; - newdist = Couplers[CouplerN].CoupleDist; // odległość od sprzęgu sąsiada - // newdist:=Distance(Loc,Connected^.Loc,Dim,Connected^.Dim); - if (CouplerN == 0) - { - // ABu: bylo newdist+10*((... - tempdist = ((Couplers[CouplerN].Connected->dMoveLen * - DirPatch(0, Couplers[CouplerN].ConnectedNr)) - - dMoveLen); - newdist += 10.0 * tempdist; - // tempdist:=tempdist+CoupleDist; //ABu: proby szybkiego naprawienia bledu - } - else - { - // ABu: bylo newdist+10*((... - tempdist = ((dMoveLen - (Couplers[CouplerN].Connected->dMoveLen * - DirPatch(1, Couplers[CouplerN].ConnectedNr)))); - newdist += 10.0 * tempdist; - // tempdist:=tempdist+CoupleDist; //ABu: proby szybkiego naprawienia bledu - } + auto &coupler { Couplers[ End ] }; + auto *othervehicle { Neighbours[ End ].vehicle->MoverParameters }; + auto const otherend { Neighbours[ End ].vehicle_end }; + auto &othercoupler { othervehicle->Couplers[ otherend ] }; + + auto const othervehiclemove { ( othervehicle->dMoveLen * DirPatch( End, otherend ) ) }; + auto const distancedelta { ( + End == end::front ? + othervehiclemove - dMoveLen : + dMoveLen - othervehiclemove ) }; + auto const initialdistance { Neighbours[ End ].distance }; // odległość od sprzęgu sąsiada + + auto const newdistance = + initialdistance + + 10.0 * distancedelta; + + auto const dV { V - ( othervehicle->V * DirPatch( End, otherend ) ) }; + auto const absdV { std::abs( dV ) }; - dV = V - (double)DirPatch( CouplerN, CNext ) * Couplers[ CouplerN ].Connected->V; - absdV = abs( dV ); // potentially generate sounds on clash or stretch - if( ( newdist < 0.0 ) - && ( Couplers[ CouplerN ].Dist > newdist ) + if( ( newdistance < 0.0 ) + && ( coupler.Dist > newdistance ) && ( dV < -0.5 ) ) { // 090503: dzwieki pracy zderzakow SetFlag( - Couplers[ CouplerN ].sounds, + coupler.sounds, ( absdV > 5.0 ? ( sound::bufferclash | sound::loud ) : sound::bufferclash ) ); } - else if( ( Couplers[ CouplerN ].CouplingFlag != coupling::faux ) - && ( newdist > 0.001 ) - && ( Couplers[ CouplerN ].Dist <= 0.001 ) + else if( ( coupler.CouplingFlag != coupling::faux ) + && ( newdistance > 0.001 ) + && ( coupler.Dist <= 0.001 ) && ( absdV > 0.005 ) && ( Vel > 1.0 ) ) { // 090503: dzwieki pracy sprzegu SetFlag( - Couplers[ CouplerN ].sounds, + coupler.sounds, ( absdV > 0.035 ? ( sound::couplerstretch | sound::loud ) : sound::couplerstretch ) ); } - // blablabla - // ABu: proby znalezienia problemu ze zle odbijajacymi sie skladami - //if (Couplers[CouplerN].CouplingFlag=ctrain_virtual) and (newdist>0) then - if( ( Couplers[ CouplerN ].CouplingFlag != coupling::faux ) - || ( Couplers[ CouplerN ].CoupleDist < 0 ) ) { + coupler.CheckCollision = false; + double CF { 0.0 }; - if( Couplers[ CouplerN ].CouplingFlag == coupling::faux ) { + if( ( coupler.CouplingFlag != coupling::faux ) + || ( initialdistance < 0 ) ) { - BetaAvg = Couplers[CouplerN].beta; - Fmax = (Couplers[CouplerN].FmaxC + Couplers[CouplerN].FmaxB) * CouplerTune; + double BetaAvg = 0; + double Fmax = 0; + + if( coupler.CouplingFlag == coupling::faux ) { + + BetaAvg = coupler.beta; + Fmax = (coupler.FmaxC + coupler.FmaxB) * CouplerTune; } - else // usrednij bo wspolny sprzeg - { - BetaAvg = - (Couplers[CouplerN].beta + Couplers[CouplerN].Connected->Couplers[CNext].beta) / - 2.0; - Fmax = (Couplers[CouplerN].FmaxC + Couplers[CouplerN].FmaxB + - Couplers[CouplerN].Connected->Couplers[CNext].FmaxC + - Couplers[CouplerN].Connected->Couplers[CNext].FmaxB) * - CouplerTune / 2.0; + else { + // usrednij bo wspolny sprzeg + BetaAvg = 0.5 * ( coupler.beta + othercoupler.beta ); + Fmax = 0.5 * ( coupler.FmaxC + coupler.FmaxB + othercoupler.FmaxC + othercoupler.FmaxB ) * CouplerTune; } - distDelta = - abs(newdist) - abs(Couplers[CouplerN].Dist); // McZapkie-191103: poprawka na histereze - Couplers[CouplerN].Dist = newdist; - if (Couplers[CouplerN].Dist > 0) - { - if (distDelta > 0) - CF = (-(Couplers[CouplerN].SpringKC + - Couplers[CouplerN].Connected->Couplers[CNext].SpringKC) * - Couplers[CouplerN].Dist / 2.0) * - DirF(CouplerN) - - Fmax * dV * BetaAvg; - else - CF = (-(Couplers[CouplerN].SpringKC + - Couplers[CouplerN].Connected->Couplers[CNext].SpringKC) * - Couplers[CouplerN].Dist / 2.0) * - DirF(CouplerN) * BetaAvg - - Fmax * dV * BetaAvg; + auto const distDelta { std::abs( newdistance ) - std::abs( coupler.Dist ) }; // McZapkie-191103: poprawka na histereze + coupler.Dist = newdistance; + if (coupler.Dist > 0) { + + if( distDelta > 0 ) { + CF = ( -( coupler.SpringKC + othercoupler.SpringKC ) * coupler.Dist / 2.0 ) * DirF( End ) + - Fmax * dV * BetaAvg; + } + else { + CF = ( -( coupler.SpringKC + othercoupler.SpringKC ) * coupler.Dist / 2.0 ) * DirF( End ) * BetaAvg + - Fmax * dV * BetaAvg; + } // liczenie sily ze sprezystosci sprzegu - if (Couplers[CouplerN].Dist > - (Couplers[CouplerN].DmaxC + - Couplers[CouplerN].Connected->Couplers[CNext].DmaxC)) // zderzenie - //***if tempdist>(DmaxC+Connected^.Couplers[CNext].DmaxC) then {zderzenie} - Couplers[CouplerN].CheckCollision = true; + if( coupler.Dist > ( coupler.DmaxC + othercoupler.DmaxC ) ) { + // zderzenie + coupler.CheckCollision = true; + } } - if (Couplers[CouplerN].Dist < 0) - { - if (distDelta > 0) - CF = (-(Couplers[CouplerN].SpringKB + - Couplers[CouplerN].Connected->Couplers[CNext].SpringKB) * - Couplers[CouplerN].Dist / 2.0) * - DirF(CouplerN) - - Fmax * dV * BetaAvg; - else - CF = (-(Couplers[CouplerN].SpringKB + - Couplers[CouplerN].Connected->Couplers[CNext].SpringKB) * - Couplers[CouplerN].Dist / 2.0) * - DirF(CouplerN) * BetaAvg - - Fmax * dV * BetaAvg; + if( coupler.Dist < 0 ) { + + if( distDelta > 0 ) { + CF = ( -( coupler.SpringKB + othercoupler.SpringKB ) * coupler.Dist / 2.0 ) * DirF( End ) + - Fmax * dV * BetaAvg; + } + else { + CF = ( -( coupler.SpringKB + othercoupler.SpringKB ) * coupler.Dist / 2.0 ) * DirF( End ) * BetaAvg + - Fmax * dV * BetaAvg; + } // liczenie sily ze sprezystosci zderzaka - if (-Couplers[CouplerN].Dist > - (Couplers[CouplerN].DmaxB + - Couplers[CouplerN].Connected->Couplers[CNext].DmaxB)) // zderzenie - //***if -tempdist>(DmaxB+Connected^.Couplers[CNext].DmaxB)/10 then {zderzenie} - { - Couplers[CouplerN].CheckCollision = true; - if( ( Couplers[ CouplerN ].CouplerType == TCouplerType::Automatic ) - && ( Couplers[ CouplerN ].CouplingFlag == coupling::faux ) ) { - // sprzeganie wagonow z samoczynnymi sprzegami} - // CouplingFlag:=ctrain_coupler+ctrain_pneumatic+ctrain_controll+ctrain_passenger+ctrain_scndpneumatic; + if( -coupler.Dist > ( coupler.DmaxB + othercoupler.DmaxB ) ) { + // zderzenie + coupler.CheckCollision = true; + if( ( coupler.CouplerType == TCouplerType::Automatic ) + && ( coupler.CouplingFlag == coupling::faux ) ) { + // sprzeganie wagonow z samoczynnymi sprzegami // EN57 - Couplers[ CouplerN ].CouplingFlag = coupling::coupler /*| coupling::brakehose | coupling::mainhose | coupling::control*/; - SetFlag( Couplers[ CouplerN ].sounds, sound::attachcoupler ); + // TBD, TODO: configurable flag for automatic coupling + coupler.CouplingFlag = coupling::coupler /*| coupling::brakehose | coupling::mainhose | coupling::control*/; + SetFlag( coupler.sounds, sound::attachcoupler ); } } } } - if( Couplers[ CouplerN ].CouplingFlag != coupling::faux ) { + + if( coupler.CouplingFlag != coupling::faux ) { // uzgadnianie prawa Newtona - Couplers[ CouplerN ].Connected->Couplers[ 1 - CouplerN ].CForce = -CF; + othervehicle->Couplers[ 1 - End ].CForce = -CF; } return CF; @@ -4472,8 +4382,9 @@ double TMoverParameters::TractionForce( double dt ) { switch( RVentType ) { case 1: { // manual - if( ( ActiveDir != 0 ) - && ( RList[ MainCtrlActualPos ].R > RVentCutOff ) ) { + if( ( true == RVentForceOn ) + || ( ( ActiveDir != 0 ) + && ( RList[ MainCtrlActualPos ].R > RVentCutOff ) ) ) { RventRot += ( RVentnmax - RventRot ) * RVentSpeed * dt; } else { @@ -4495,9 +4406,12 @@ double TMoverParameters::TractionForce( double dt ) { * RVentSpeed * dt; } else if( ( DynamicBrakeType == dbrake_automatic ) - && ( true == DynamicBrakeFlag ) ) { + && ( true == DynamicBrakeFlag ) ) { RventRot += ( RVentnmax * motorcurrent / ImaxLo - RventRot ) * RVentSpeed * dt; } + else if( RVentForceOn ) { + RventRot += ( RVentnmax - RventRot ) * RVentSpeed * dt; + } else { RventRot *= std::max( 0.0, 1.0 - RVentSpeed * dt ); } @@ -4667,11 +4581,43 @@ double TMoverParameters::TractionForce( double dt ) { } if ((DynamicBrakeType == dbrake_automatic) && (DynamicBrakeFlag)) { - if (((Vadd + abs(Im)) > TUHEX_Sum + TUHEX_Diff) || (Hamulec->GetEDBCP() < 0.25)) + if (TUHEX_Stages > 0) //hamowanie wielostopniowe, nadpisuje wartości domyślne + { + if (Vel > 100) TUHEX_StageActual = std::min(TUHEX_StageActual, 1); + switch (TUHEX_StageActual) + { + case 1: + TUHEX_Sum = TUHEX_Sum1; + DynamicBrakeRes = DynamicBrakeRes1; + break; + case 2: + TUHEX_Sum = TUHEX_Sum2; + DynamicBrakeRes = DynamicBrakeRes1; + break; + case 3: + TUHEX_Sum = TUHEX_Sum3; + if ((Vadd > 0.99*TUHEX_MaxIw) && (DynamicBrakeRes == DynamicBrakeRes1)) + TUHEX_ResChange = true; + if (TUHEX_ResChange && Vadd < 0.5*TUHEX_MaxIw) + { + TUHEX_ResChange = false; + DynamicBrakeRes = DynamicBrakeRes2; + } + break; + default: + DynamicBrakeRes = DynamicBrakeRes1; + TUHEX_Sum = 0; + break; + } + } + if (((Vadd + abs(Im)) > TUHEX_Sum + TUHEX_Diff) || (Hamulec->GetEDBCP() < 0.25) || (TUHEX_ResChange) || (TUHEX_StageActual==0 && TUHEX_Stages>0)) { Vadd -= 500.0 * dt; - if (Vadd < 1) - Vadd = 0; + if (Vadd < TUHEX_MinIw) + { + Vadd = 0; + DynamicBrakeFlag = false; + } } else if ((DynamicBrakeFlag) && ((Vadd + abs(Im)) < TUHEX_Sum - TUHEX_Diff)) { @@ -5038,24 +4984,24 @@ double TMoverParameters::TractionForce( double dt ) { if (NewSCAP != ScndCtrlActualPos) { ScndCtrlActualPos = NewSCAP; - SendCtrlToNext("SpeedCntrl", ScndCtrlActualPos, CabNo); +// SendCtrlToNext("SpeedCntrl", ScndCtrlActualPos, CabNo); } } } } - - dtrans = Hamulec->GetEDBCP(); - if( ( ( false == Doors.instances[ side::left ].is_closed ) - || ( false == Doors.instances[ side::right ].is_closed ) ) ) { + double edBCP = Hamulec->GetEDBCP(); + if( ( false == Doors.instances[ side::left ].is_closed ) + || ( false == Doors.instances[ side::right ].is_closed ) + || ( Doors.permit_needed && ( Doors.instances[ side::left ].open_permit || Doors.instances[ side::right ].open_permit ) ) ) { DynamicBrakeFlag = true; } - else if (((dtrans < 0.25) && (LocHandle->GetCP() < 0.25) && (AnPos < 0.01)) || - ((dtrans < 0.25) && (ShuntModeAllow) && (LocalBrakePosA < 0.01))) + else if (((edBCP < 0.25) && (LocHandle->GetCP() < 0.25) && (AnPos < 0.01)) || + ((edBCP < 0.25) && (ShuntModeAllow) && (LocalBrakePosA < 0.01))) DynamicBrakeFlag = false; - else if ((((BrakePress > 0.25) && (dtrans > 0.25) || (LocHandle->GetCP() > 0.25))) || + else if ((((BrakePress > 0.25) && (edBCP > 0.25) || (LocHandle->GetCP() > 0.25))) || (AnPos > 0.02)) DynamicBrakeFlag = true; - dtrans = Hamulec->GetEDBCP() * eimc[eimc_p_abed]; // stala napedu + edBCP = Hamulec->GetEDBCP() * eimc[eimc_p_abed]; // stala napedu if ((DynamicBrakeFlag)) { // ustalanie współczynnika blendingu do luzowania hamulca PN @@ -5063,7 +5009,7 @@ double TMoverParameters::TractionForce( double dt ) { { PosRatio = -Sign(V) * DirAbsolute * eimv[eimv_Fr] / (eimc[eimc_p_Fh] * - Max0R(dtrans,Max0R(0.01,Hamulec->GetEDBCP())) / MaxBrakePress[0]); + Max0R(edBCP,Max0R(0.01,Hamulec->GetEDBCP())) / MaxBrakePress[0]); PosRatio = clamp(PosRatio, 0.0, 1.0); } else @@ -5082,29 +5028,29 @@ double TMoverParameters::TractionForce( double dt ) { } else { - PosRatio = -std::max(std::min(dtrans * 1.0 / MaxBrakePress[0], 1.0), AnPos) * + PosRatio = -std::max(std::min(edBCP * 1.0 / MaxBrakePress[0], 1.0), AnPos) * std::max(0.0, std::min(1.0, (Vel - eimc[eimc_p_Vh0]) / (eimc[eimc_p_Vh1] - eimc[eimc_p_Vh0]))); - eimv[eimv_Fzad] = -std::max(LocalBrakeRatio(), dtrans / MaxBrakePress[0]); + eimv[eimv_Fzad] = -std::min(1.0,std::max(LocalBrakeRatio(), edBCP / MaxBrakePress[0])); } tmp = 5; } else { - PosRatio = static_cast( MainCtrlPos ) / static_cast( MainCtrlPosNo ); + PosRatio = Max0R(eimic_real, 0); eimv[eimv_Fzad] = PosRatio; if ((Flat) && (eimc[eimc_p_F0] * eimv[eimv_Fful] > 0)) PosRatio = Min0R(PosRatio * eimc[eimc_p_F0] / eimv[eimv_Fful], 1); - if (ScndCtrlActualPos > 0) //speed control +/* if (ScndCtrlActualPos > 0) //speed control if (Vmax < 250) PosRatio = Min0R(PosRatio, Max0R(-1, 0.5 * (ScndCtrlActualPos - Vel))); else PosRatio = - Min0R(PosRatio, Max0R(-1, 0.5 * (ScndCtrlActualPos * 2 - Vel))); + Min0R(PosRatio, Max0R(-1, 0.5 * (ScndCtrlActualPos * 2 - Vel))); */ // PosRatio = 1.0 * (PosRatio * 0 + 1) * PosRatio; // 1 * 1 * PosRatio = PosRatio Hamulec->SetED(0); // (Hamulec as TLSt).SetLBP(LocBrakePress); - if ((PosRatio > dizel_fill)) + if ((PosRatio > eimv_pr)) tmp = 4; else tmp = 4; // szybkie malenie, powolne wzrastanie @@ -5122,8 +5068,8 @@ double TMoverParameters::TractionForce( double dt ) { Sandbox( false, range_t::unit ); } - dizel_fill += Max0R(Min0R(PosRatio - dizel_fill, 0.02), -0.02) * 12 * - (tmp /*2{+4*byte(PosRatio 0.01 ? -LocalBrakeRatio() : (double)MainCtrlPos / (double)MainCtrlPosNo); + break; + case 1: + switch (MainCtrlPos) + { + case 0: //B+ + eimic -= clamp(1.0 + eimic, 0.0, dt*0.3); //odejmuj do -1 + break; + case 1: //B + eimic -= clamp(0.0 + eimic, 0.0, dt*0.3); //odejmuj do 0 + break; + case 2: //B- + case 3: //0 + case 4: //T- + eimic -= clamp(0.0 + eimic, 0.0, dt*0.3); //odejmuj do 0 + eimic += clamp(0.0 - eimic, 0.0, dt*0.3); //dodawaj do 0 + break; + case 5: //T + eimic += clamp(0.0 - eimic, 0.0, dt*0.3); //dodawaj do 0 + break; + case 6: //T+ + eimic += clamp(1.0 - eimic, 0.0, dt*0.3); //dodawaj do 1 + break; + case 7: //TMax + eimic += clamp(1.0 - eimic, 0.0, dt*0.3); //dodawaj do 1, max + break; + } + if (MainCtrlPos >= 3 && eimic < 0) eimic = 0; + if (MainCtrlPos <= 3 && eimic > 0) eimic = 0; + break; + case 2: + switch (MainCtrlPos) + { + case 0: + eimic = -1.0; + break; + case 1: + eimic -= clamp(1.0 + eimic, 0.0, dt*0.15); //odejmuj do -1 + if (eimic > 0) eimic = 0; + break; + case 2: + eimic -= clamp(0.0 + eimic, 0.0, dt*0.15); //odejmuj do 0 + break; + case 3: + eimic += clamp(0.0 - eimic, 0.0, dt*0.15); //dodawaj do 0 + break; + case 4: + eimic += clamp(1.0 - eimic, 0.0, dt*0.15); //dodawaj do 1 + if (eimic < 0) eimic = 0; + break; + } + break; + } + eimic = clamp(eimic, -1.0, 1.0); +} + +void TMoverParameters::CheckSpeedCtrl() +{ + if (ScndCtrlActualPos > 0) //speed control + if (Vmax < 250) + eimicSpeedCtrl = clamp(0.5 * (ScndCtrlActualPos - Vel), -1.0, 1.0); + else + eimicSpeedCtrl = clamp(0.5 * (ScndCtrlActualPos * 2 - Vel), -1.0, 1.0); + else + eimicSpeedCtrl = 1; +} + // ************************************************************************************************* // Q: 20160715 // Zmienia parametr do którego dąży sprzęgło @@ -6726,6 +6744,27 @@ bool TMoverParameters::ChangeDoorPermitPreset( int const Change, range_t const N return ( Doors.permit_preset != initialstate ); } +bool TMoverParameters::PermitDoorStep( bool const State, range_t const Notify ) { + + auto const initialstate { Doors.step_enabled }; + + Doors.step_enabled = State; + if( Notify != range_t::local ) { + // wysłanie wyłączenia do pozostałych? + SendCtrlToNext( + "DoorStep", + ( State == true ? + 1 : + 0 ), + CabNo, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( Doors.step_enabled != initialstate ); +} + bool TMoverParameters::PermitDoors( side const Door, bool const State, range_t const Notify ) { bool const initialstate { Doors.instances[Door].open_permit }; @@ -6808,6 +6847,8 @@ bool TMoverParameters::OperateDoors( side const Door, bool const State, range_t // toggle door lock bool TMoverParameters::LockDoors( bool const State, range_t const Notify ) { + auto const initialstate { Doors.lock_enabled }; + Doors.lock_enabled = State; if( Notify != range_t::local ) { // wysłanie wyłączenia do pozostałych? @@ -6822,7 +6863,7 @@ bool TMoverParameters::LockDoors( bool const State, range_t const Notify ) { coupling::control ) ); } - return true; + return ( Doors.lock_enabled != initialstate ); } // toggles departure warning @@ -6888,7 +6929,8 @@ TMoverParameters::update_doors( double const Deltatime ) { door.is_open = ( door.position >= Doors.range ) - && ( door.step_position >= ( Doors.step_range != 0.f ? 1.f : 0.f ) ); + && ( ( false == Doors.step_enabled ) + || ( door.step_position >= ( Doors.step_range != 0.f ? 1.f : 0.f ) ) ); door.is_closed = ( door.position <= 0.f ) && ( door.step_position <= 0.f ); @@ -6922,6 +6964,19 @@ TMoverParameters::update_doors( double const Deltatime ) { && ( true == Battery ) && ( false == openrequest ) && ( door.is_closing || closerequest ); + door.step_unfolding = ( + ( Doors.step_range != 0.f ) + && ( Doors.step_enabled ) + && ( false == Doors.is_locked ) + && ( door.step_position < 1.f ) + && ( door.is_opening ) ); + door.step_folding = ( + ( door.step_position > 0.f ) // is unfolded + && ( ( false == Doors.step_enabled ) // we lost permission to stay open or our door is calling the shots + || ( Doors.permit_needed ? + ( false == door.open_permit ) : + door.is_closing ) ) + && ( ( door.close_delay > Doors.close_delay ) || door.position <= 0.f ) ); // door is about to close, or already done if( true == door.is_opening ) { door.auto_timer = ( @@ -6929,17 +6984,14 @@ TMoverParameters::update_doors( double const Deltatime ) { ( remoteopencontrol && door.remote_open && Doors.auto_include_remote ) ? Doors.auto_duration : -1.f ); } - // doors - if( ( true == door.is_opening ) - && ( door.position < Doors.range ) ) { + if( true == door.is_opening ) { // open door if( ( TrainType == dt_EZT ) || ( TrainType == dt_DMU ) ) { // multi-unit vehicles typically open door only after unfolding the doorstep - if( ( Doors.step_range == 0.f ) // no wait if no doorstep - || ( Doors.step_type == 2 ) // no wait for rotating doorstep - || ( door.step_position == 1.f ) ) { + if( ( false == door.step_unfolding ) // no wait if no doorstep + || ( Doors.step_type == 2 ) ) { // no wait for rotating doorstep door.position = std::min( Doors.range, door.position + Doors.open_rate * Deltatime ); @@ -6952,8 +7004,7 @@ TMoverParameters::update_doors( double const Deltatime ) { } door.close_delay = 0.f; } - if( ( true == door.is_closing ) - && ( door.position > 0.f ) ) { + if( true == door.is_closing ) { // close door door.close_delay += Deltatime; if( door.close_delay > Doors.close_delay ) { @@ -6963,22 +7014,18 @@ TMoverParameters::update_doors( double const Deltatime ) { } } // doorsteps - if( ( true == door.is_opening ) - && ( Doors.step_range != 0.f ) - && ( door.step_position < 1.f ) ) { + if( door.step_unfolding ) { // unfold left doorstep door.step_position = std::min( 1.f, door.step_position + Doors.step_rate * Deltatime ); } - if( ( true == door.is_closing ) - && ( door.step_position > 0.f ) - && ( door.close_delay > Doors.close_delay ) ) { + if( door.step_folding ) { // fold left doorstep if( ( TrainType == dt_EZT ) || ( TrainType == dt_DMU ) ) { // multi-unit vehicles typically fold the doorstep only after closing the door - if( door.position == 0.f ) { + if( door.position <= 0.f ) { door.step_position = std::max( 0.f, door.step_position - Doors.step_rate * Deltatime ); @@ -7132,17 +7179,16 @@ double TMoverParameters::GetTrainsetVoltage(void) // ************************************************************************************************* // Kasowanie zmiennych pracy fizyki // ************************************************************************************************* -bool TMoverParameters::Physic_ReActivation(void) // DO PRZETLUMACZENIA NA KONCU +bool TMoverParameters::switch_physics(bool const State) // DO PRZETLUMACZENIA NA KONCU { -// bool pr; - if (PhysicActivation) - return false; - else - { - PhysicActivation = true; + if( PhysicActivation == State ) { return false; } + + PhysicActivation = State; + if( true == State ) { LastSwitchingTime = 0; - return true; } + + return true; } // ************************************************************************************************* @@ -7707,6 +7753,14 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) continue; } + if (issection("DCEMUED:", inputline)) { + + startBPT = true; LISTLINE = 0; + fizlines.emplace("DCEMUED", inputline); + LoadFIZ_DCEMUED(inputline); + continue; + } + if( issection( "Light:", inputline ) ) { startBPT = false; @@ -8349,6 +8403,7 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { { "Knorr", TBrakeHandle::Knorr }, { "Westinghouse", TBrakeHandle::West }, { "FVel6", TBrakeHandle::FVel6 }, + { "FVE408", TBrakeHandle::FVE408 }, { "St113", TBrakeHandle::St113 } }; auto lookup = brakehandles.find( extract_value( "BrakeHandle", line ) ); @@ -8434,6 +8489,8 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { else { AutoRelayType = 0; } extract_value( CoupledCtrl, "CoupledCtrl", line, "" ); + extract_value( EIMCtrlType, "EIMCtrlType", line, "" ); + clamp( EIMCtrlType, 0, 2 ); extract_value( ScndS, "ScndS", line, "" ); // brak pozycji rownoleglej przy niskiej nastawie PSR @@ -8527,6 +8584,16 @@ void TMoverParameters::LoadFIZ_Blending(std::string const &line) { extract_value(MED_Ncor, "MED_Ncor", line, ""); } + +void TMoverParameters::LoadFIZ_DCEMUED(std::string const &line) { + + extract_value(DCEMUED_CC, "CouplerCheck", line, "0"); + extract_value(DCEMUED_EP_max_Vel, "EP_max_Vel", line, "0"); + extract_value(DCEMUED_EP_min_Im, "EP_min_Im", line, "0"); + extract_value(DCEMUED_EP_delay, "EP_delay", line, "0"); + +} + void TMoverParameters::LoadFIZ_Light( std::string const &line ) { LightPowerSource.SourceType = LoadFIZ_SourceDecode( extract_value( "Light", line ) ); @@ -8797,6 +8864,11 @@ void TMoverParameters::LoadFIZ_Circuit( std::string const &Input ) { extract_value( TUHEX_Diff, "TUHEX_Diff", Input, "" ); extract_value( TUHEX_MaxIw, "TUHEX_MaxIw", Input, "" ); extract_value( TUHEX_MinIw, "TUHEX_MinIw", Input, "" ); + extract_value( TUHEX_Sum1, "TUHEX_Sum1", Input, ""); + extract_value( TUHEX_Sum2, "TUHEX_Sum2", Input, "" ); + extract_value( TUHEX_Sum3, "TUHEX_Sum3", Input, "" ); + extract_value( TUHEX_Stages, "TUHEX_Stages", Input, "0" ); + } void TMoverParameters::LoadFIZ_RList( std::string const &Input ) { @@ -8825,6 +8897,8 @@ void TMoverParameters::LoadFIZ_RList( std::string const &Input ) { extract_value( RVentMinI, "RVentMinI", Input, "" ); extract_value( RVentSpeed, "RVentSpeed", Input, "" ); extract_value( DynamicBrakeRes, "DynBrakeRes", Input, ""); + extract_value( DynamicBrakeRes1, "DynBrakeRes1", Input, ""); + extract_value( DynamicBrakeRes2, "DynBrakeRes2", Input, ""); } void TMoverParameters::LoadFIZ_DList( std::string const &Input ) { @@ -9095,6 +9169,13 @@ bool TMoverParameters::CheckLocomotiveParameters(bool ReadyFlag, int Dir) Hamulec->SetLP( Mass, MBPM, MaxBrakePress[ 1 ] ); break; } + case TBrakeValve::EP1: + { + WriteLog("XBT EP1"); + Hamulec = std::make_shared(MaxBrakePress[3], BrakeCylRadius, BrakeCylDist, BrakeVVolume, BrakeCylNo, BrakeDelays, BrakeMethod, NAxles, NBpA); + Hamulec->SetLP(Mass, MBPM, MaxBrakePress[1]); + break; + } case TBrakeValve::CV1: { WriteLog( "XBT CV1" ); @@ -9124,6 +9205,9 @@ bool TMoverParameters::CheckLocomotiveParameters(bool ReadyFlag, int Dir) case TBrakeHandle::FVel6: Handle = std::make_shared(); break; + case TBrakeHandle::FVE408: + Handle = std::make_shared(); + break; case TBrakeHandle::testH: Handle = std::make_shared(); break; @@ -9437,9 +9521,25 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C OK:=SendCtrlToNext(command,CValue1,CValue2); end; end */ + else if (Command == "EIMIC") // ElectricInductionMotor Integrated Control - propulsion and brakes in relative values + { + eimic_real = CValue1; + OK = SendCtrlToNext(Command, CValue1, CValue2, Couplertype); + } else if (Command == "Brake") // youBy - jak sie EP hamuje, to trza sygnal wyslac... { - Hamulec->SetEPS(CValue1); + if (CValue1 < 0.001) + DynamicBrakeEMUStatus = true; + double temp1 = CValue1; + if ((DCEMUED_EP_max_Vel > 0.001) && (Vel > DCEMUED_EP_max_Vel) && (DynamicBrakeEMUStatus)) + temp1 = 0; + if ((DCEMUED_EP_min_Im > 0.001) && (abs(Im) > DCEMUED_EP_min_Im) && (DynamicBrakeEMUStatus)) + temp1 = 0; + Hamulec->SetEPS(temp1); + TUHEX_StageActual = CValue1; + TUHEX_Active = TUHEX_StageActual > 0; + if (CValue1 < 0.001) + DynamicBrakeEMUStatus = true; // fBrakeCtrlPos:=BrakeCtrlPos; //to powinnno być w jednym miejscu, aktualnie w C++!!! BrakePressureActual = BrakePressureTable[BrakeCtrlPos]; OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); @@ -9714,6 +9814,13 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C false ); OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } + else if( Command == "DoorStep" ) { + Doors.step_enabled = ( + CValue1 == 1 ? + true : + false ); + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } else if( Command == "DepartureSignal" ) { DepartureSignal = ( CValue1 == 1 ? @@ -9927,8 +10034,7 @@ bool TMoverParameters::RunInternalCommand() CommandIn.Location.X = 0; CommandIn.Location.Y = 0; CommandIn.Location.Z = 0; - if (!PhysicActivation) - Physic_ReActivation(); + switch_physics( true ); } } else diff --git a/McZapkie/hamulce.cpp b/McZapkie/hamulce.cpp index a58dc3d1..f01d6697 100644 --- a/McZapkie/hamulce.cpp +++ b/McZapkie/hamulce.cpp @@ -31,6 +31,7 @@ double const TSt113::BPT_K[6][2] = {{10, 0}, {4, 1}, {0, 1}, {4, 0}, {4, -1}, {1 double const TSt113::BEP_K[7] = {0, -1, 1, 0, 0, 0, 0}; double const TSt113::pos_table[11] = {-1, 5, -1, 0, 2, 3, 4, 5, 0, 0, 1}; double const TFVel6::pos_table[11] = {-1, 6, -1, 0, 6, 4, 4.7, 5, -1, 0, 1}; +double const TFVE408::pos_table[11] = { 0, 10, 0, 0, 10, 7, 8, 9, 0, 1, 5 }; double PR(double P1, double P2) { @@ -133,6 +134,11 @@ double PFVd( double PH, double PL, double const S, double LIM, double const DP ) return 0; } +bool is_EQ(double pos, double i_pos) +{ + return (pos <= i_pos + 0.5) && (pos > i_pos - 0.5); +} + //---ZBIORNIKI--- double TReservoir::pa() @@ -863,10 +869,7 @@ double TEStEP2::GetPF( double const PP, double const dt, double const Vel ) dV1 = dV1 - 0.96 * dv; // hamulec EP - temp = BVP * int(EPS > 0); - dv = PF(temp, LBP, 0.00053 + 0.00060 * int(EPS < 0)) * dt * EPS * EPS * - int(LBP * EPS < MaxBP * LoadC); - LBP = LBP - dv; + EPCalc(dt); // luzowanie KI if ((BrakeStatus & b_hld) == b_off) @@ -936,6 +939,24 @@ void TEStEP2::SetLP( double const TM, double const LM, double const TBP ) TareBP = TBP; } +void TEStEP2::EPCalc(double dt) +{ + double temp = BrakeRes->P() * int(EPS > 0); + double dv = PF(temp, LBP, 0.00053 + 0.00060 * int(EPS < 0)) * dt * EPS * EPS * + int(LBP * EPS < MaxBP * LoadC); + LBP = LBP - dv; +} + + +void TEStEP1::EPCalc(double dt) +{ + double temp = EPS - std::floor(EPS); //część ułamkowa jest hamulcem EP + double LBPLim = Min0R(MaxBP * LoadC * temp, BrakeRes->P()); //do czego dążymy + double S = 10 * clamp(LBPLim - LBP, -0.1, 0.1); //przymykanie zaworku + double dv = PF(( S > 0 ? BrakeRes->P() : 0 ), LBP, abs(S)*(0.00053 + 0.00060 * int(S < 0))) * dt; //przepływ + LBP = LBP - dv; +} + //---EST3-- double TESt3::GetPF( double const PP, double const dt, double const Vel ) @@ -3133,4 +3154,71 @@ void TFVel6::Init(double Press) TimeEP = true; } +//---FVE408--- + +double TFVE408::GetPF(double i_bcp, double PP, double HP, double dt, double ep) +{ + static int const LBDelay = 100; + + double LimPP; + double dpMainValve; + double ActFlowSpeed; + + LimPP = Min0R(5 * int(i_bcp < 6.5), HP); + if ((i_bcp >= 6.5) && ((i_bcp < 7.5) || (i_bcp > 9.5))) + ActFlowSpeed = 0; + else if ((i_bcp > 7.5) && (i_bcp < 8.5)) + ActFlowSpeed = 2; // konsultacje wawa1 - bylo 8; + else if ((i_bcp < 6.5)) + ActFlowSpeed = 2; + else + ActFlowSpeed = 4; + dpMainValve = PF(LimPP, PP, ActFlowSpeed / LBDelay) * dt; + + Sounds[s_fv4a_e] = 0; + Sounds[s_fv4a_u] = 0; + Sounds[s_fv4a_b] = 0; + if ((i_bcp < 6.5)) + Sounds[s_fv4a_u] = -dpMainValve; + else if ((i_bcp < 8.5)) + Sounds[s_fv4a_b] = dpMainValve; + else if ((i_bcp < 9.5)) + Sounds[s_fv4a_e] = dpMainValve; + + if (is_EQ(i_bcp, 1)) EPS = 1.15; else + if (is_EQ(i_bcp, 2)) EPS = 1.40; else + if (is_EQ(i_bcp, 3)) EPS = 2.64; else + if (is_EQ(i_bcp, 4)) EPS = 3.84; else + if (is_EQ(i_bcp, 5)) EPS = 3.99; else + EPS = 0; + + return dpMainValve; +} + +double TFVE408::GetCP() +{ + return EPS; +} + +double TFVE408::GetPos(int i) +{ + return pos_table[i]; +} + +double TFVE408::GetSound(int i) +{ + if (i > 2) + return 0; + else + return Sounds[i]; +} + +void TFVE408::Init(double Press) +{ + Time = true; + TimeEP = false; +} + // END + + diff --git a/McZapkie/hamulce.h b/McZapkie/hamulce.h index 07134e18..ecec4c14 100644 --- a/McZapkie/hamulce.h +++ b/McZapkie/hamulce.h @@ -381,7 +381,7 @@ class TEStED : public TLSt { //zawor z EP09 - Est4 z oddzielnym przekladnikiem, class TEStEP2 : public TLSt { -private: +protected: double TareM = 0.0; //masa proznego double LoadM = 0.0; //masa pelnego double TareBP = 0.0; //cisnienie dla proznego @@ -394,12 +394,23 @@ public: void PLC( double const mass ); //wspolczynnik cisnienia przystawki wazacej void SetEPS( double const nEPS )/*override*/; //stan hamulca EP void SetLP( double const TM, double const LM, double const TBP ); //parametry przystawki wazacej + void virtual EPCalc(double dt); inline TEStEP2(double i_mbp, double i_bcr, double i_bcd, double i_brc, int i_bcn, int i_BD, int i_mat, int i_ba, int i_nbpa) : TLSt( i_mbp, i_bcr, i_bcd, i_brc, i_bcn, i_BD, i_mat, i_ba, i_nbpa) {} }; +class TEStEP1 : public TEStEP2 { + +public: + void EPCalc(double dt); + + inline TEStEP1(double i_mbp, double i_bcr, double i_bcd, double i_brc, int i_bcn, int i_BD, int i_mat, int i_ba, int i_nbpa) : + TEStEP2(i_mbp, i_bcr, i_bcd, i_brc, i_bcn, i_BD, i_mat, i_ba, i_nbpa) + {} +}; + class TCV1 : public TBrake { private: @@ -775,6 +786,24 @@ class TFVel6 : public TDriverHandle { {} }; +class TFVE408 : public TDriverHandle { + +private: + double EPS = 0.0; + static double const pos_table[11]; // = {-1, 6, -1, 0, 6, 4, 4.7, 5, -1, 0, 1}; + +public: + double GetPF(double i_bcp, double PP, double HP, double dt, double ep)/*override*/; + double GetCP()/*override*/; + double GetPos(int i)/*override*/; + double GetSound(int i)/*override*/; + void Init(double Press)/*override*/; + + inline TFVE408(void) : + TDriverHandle() + {} +}; + extern double PF( double const P1, double const P2, double const S, double const DP = 0.25 ); extern double PF1( double const P1, double const P2, double const S ); diff --git a/Train.cpp b/Train.cpp index 4de220bd..94c348aa 100644 --- a/Train.cpp +++ b/Train.cpp @@ -138,28 +138,17 @@ TButton &TCab::Button(int n) } }; -void TCab::Update() +void TCab::Update( bool const Power ) { // odczyt parametrów i ustawienie animacji submodelom -/* - int i; - for (i = 0; i < iGauges; ++i) - { // animacje izometryczne - ggList[i].UpdateValue(); // odczyt parametru i przeliczenie na kąt - ggList[i].Update(); // ustawienie animacji - } - for (i = 0; i < iButtons; ++i) - { // animacje dwustanowe - btList[i].Update(); // odczyt parametru i wybór submodelu - } -*/ 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(); // odczyt parametru i wybór submodelu + button.Update( Power ); // odczyt parametru i wybór submodelu } }; @@ -169,6 +158,7 @@ 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 }, @@ -180,6 +170,7 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::secondcontrollerdecreasefast, &TTrain::OnCommand_secondcontrollerdecreasefast }, { user_command::secondcontrollerset, &TTrain::OnCommand_secondcontrollerset }, { 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 }, @@ -272,6 +263,7 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { 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 }, @@ -333,6 +325,7 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { 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::carcouplingincrease, &TTrain::OnCommand_carcouplingincrease }, { user_command::carcouplingdisconnect, &TTrain::OnCommand_carcouplingdisconnect }, { user_command::departureannounce, &TTrain::OnCommand_departureannounce }, @@ -536,13 +529,16 @@ dictionary_source *TTrain::GetTrainState() { dict->insert( "unit_no", iUnitNo ); for( int i = 0; i < 20; i++ ) { - dict->insert( ( "doors_" + std::to_string( i + 1 ) ), bDoors[ i ][ 0 ] ); - dict->insert( ( "doors_r_" + std::to_string( i + 1 ) ), bDoors[ i ][ 1 ] ); - dict->insert( ( "doors_l_" + std::to_string( i + 1 ) ), bDoors[ i ][ 2 ] ); - dict->insert( ( "doors_no_" + std::to_string( i + 1 ) ), iDoorNo[ i ] ); - dict->insert( ( "code_" + std::to_string( i + 1 ) ), ( std::to_string( iUnits[ i ] ) + cCode[ i ] ) ); - dict->insert( ( "car_name" + std::to_string( i + 1 ) ), asCarName[ i ] ); - dict->insert( ( "slip_" + std::to_string( i + 1 ) ), bSlip[ i ] ); + auto const caridx { std::to_string( i + 1 ) }; + dict->insert( ( "doors_" + caridx ), bDoors[ i ][ 0 ] ); + dict->insert( ( "doors_l_" + caridx ), bDoors[ i ][ 1 ] ); + dict->insert( ( "doors_r_" + caridx ), bDoors[ i ][ 2 ] ); + dict->insert( ( "doorstep_l_" + caridx ), bDoors[ i ][ 3 ] ); + dict->insert( ( "doorstep_r_" + caridx ), bDoors[ i ][ 4 ] ); + dict->insert( ( "doors_no_" + caridx ), iDoorNo[ i ] ); + dict->insert( ( "code_" + caridx ), ( std::to_string( iUnits[ i ] ) + cCode[ i ] ) ); + dict->insert( ( "car_name" + caridx ), asCarName[ i ] ); + dict->insert( ( "slip_" + caridx ), bSlip[ i ] ); } // ai state data auto const *driver = DynamicObject->Mechanik; @@ -774,12 +770,49 @@ void TTrain::OnCommand_aidriverdisable( TTrain *Train, command_data const &Comma 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; + Train->mvOccupied->LocalBrakePosA = 0; + } + else { + Train->mvOccupied->LocalBrakePosA = ( + clamp( + 1.0 - ( Command.param1 * 2 ), + 0.0, 1.0 ) ); + if( Train->mvControlled->MainCtrlPos > 0 ) { + Train->set_master_controller( 0 ); + } + } + } + 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 - Train->mvControlled->IncMainCtrl( 1 ); - Train->m_mastercontrollerinuse = true; + if( ( Train->ggJointCtrl.SubModel != nullptr ) + && ( Train->mvControlled->LocalBrakePosA > 0.0 ) ) { + OnCommand_independentbrakedecrease( Train, Command ); + } + else { + Train->mvControlled->IncMainCtrl( 1 ); + Train->m_mastercontrollerinuse = true; + } } else if (Command.action == GLFW_RELEASE) { // release @@ -792,7 +825,19 @@ void TTrain::OnCommand_mastercontrollerincreasefast( TTrain *Train, command_data if( Command.action != GLFW_RELEASE ) { // on press or hold - Train->mvControlled->IncMainCtrl( 2 ); + if( ( Train->ggJointCtrl.SubModel != nullptr ) + && ( Train->mvControlled->LocalBrakePosA > 0.0 ) ) { + OnCommand_independentbrakedecreasefast( Train, Command ); + } + else { + Train->mvControlled->IncMainCtrl( 2 ); + Train->m_mastercontrollerinuse = true; + } + } + else { + // release + Train->m_mastercontrollerinuse = false; + Train->m_mastercontrollerreturndelay = EU07_CONTROLLER_KEYBOARDETURNDELAY + EU07_CONTROLLER_BASERETURNDELAY; } } @@ -800,8 +845,14 @@ void TTrain::OnCommand_mastercontrollerdecrease( TTrain *Train, command_data con if( Command.action != GLFW_RELEASE ) { // on press or hold - Train->mvControlled->DecMainCtrl( 1 ); - Train->m_mastercontrollerinuse = true; + if( ( Train->ggJointCtrl.SubModel != nullptr ) + && ( Train->mvControlled->MainCtrlPos == 0 ) ) { + OnCommand_independentbrakeincrease( Train, Command ); + } + else { + Train->mvControlled->DecMainCtrl( 1 ); + Train->m_mastercontrollerinuse = true; + } } else if (Command.action == GLFW_RELEASE) { // release @@ -814,7 +865,19 @@ void TTrain::OnCommand_mastercontrollerdecreasefast( TTrain *Train, command_data if( Command.action != GLFW_RELEASE ) { // on press or hold - Train->mvControlled->DecMainCtrl( 2 ); + if( ( Train->ggJointCtrl.SubModel != nullptr ) + && ( Train->mvControlled->MainCtrlPos == 0 ) ) { + OnCommand_independentbrakeincreasefast( Train, Command ); + } + else { + Train->mvControlled->DecMainCtrl( 2 ); + Train->m_mastercontrollerinuse = true; + } + } + else { + // release + Train->m_mastercontrollerinuse = false; + Train->m_mastercontrollerreturndelay = EU07_CONTROLLER_KEYBOARDETURNDELAY + EU07_CONTROLLER_BASERETURNDELAY; } } @@ -877,6 +940,45 @@ void TTrain::OnCommand_notchingrelaytoggle( TTrain *Train, command_data const &C } } +void TTrain::OnCommand_tempomattoggle( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggScndCtrlButton.type() == TGaugeType::push ) { + // impulse switch + if( Command.action == GLFW_RELEASE ) { + // just move the button back to default position + // visual feedback + Train->ggScndCtrlButton.UpdateValue( 0.0, Train->dsbSwitch ); + return; + } + // glfw_press + if( Train->mvControlled->ScndCtrlPos == 0 ) { + // turn on if needed + Train->mvControlled->IncScndCtrl( 1 ); + } + // visual feedback + 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_mucurrentindicatorothersourceactivate( TTrain *Train, command_data const &Command ) { if( Train->ggNextCurrentButton.SubModel == nullptr ) { @@ -997,7 +1099,7 @@ void TTrain::OnCommand_independentbrakeset( TTrain *Train, command_data const &C if( Command.action != GLFW_RELEASE ) { - Train->mvControlled->LocalBrakePosA = ( + Train->mvOccupied->LocalBrakePosA = ( clamp( Command.param1, 0.0, 1.0 ) ); @@ -1524,7 +1626,7 @@ void TTrain::OnCommand_reverserincrease( TTrain *Train, command_data const &Comm if( ( Train->mvOccupied->ActiveDir ) && ( Train->DynamicObject->Mechanik ) ) { - Train->DynamicObject->Mechanik->CheckVehicles( Change_direction ); + Train->DynamicObject->Mechanik->DirectionChange(); } } } @@ -1539,7 +1641,7 @@ void TTrain::OnCommand_reverserdecrease( TTrain *Train, command_data const &Comm if( ( Train->mvOccupied->ActiveDir ) && ( Train->DynamicObject->Mechanik ) ) { - Train->DynamicObject->Mechanik->CheckVehicles( Change_direction ); + Train->DynamicObject->Mechanik->DirectionChange();; } } } @@ -1570,7 +1672,7 @@ void TTrain::OnCommand_reverserforward( TTrain *Train, command_data const &Comma if( ( Train->mvOccupied->ActiveDir == 1 ) && ( Train->DynamicObject->Mechanik ) ) { - Train->DynamicObject->Mechanik->CheckVehicles( Change_direction ); + Train->DynamicObject->Mechanik->DirectionChange(); } } } @@ -1605,7 +1707,7 @@ void TTrain::OnCommand_reverserbackward( TTrain *Train, command_data const &Comm if( ( Train->mvOccupied->ActiveDir == -1 ) && ( Train->DynamicObject->Mechanik ) ) { - Train->DynamicObject->Mechanik->CheckVehicles( Change_direction ); + Train->DynamicObject->Mechanik->DirectionChange(); } } } @@ -2985,6 +3087,13 @@ void TTrain::OnCommand_motorblowersdisableall( TTrain *Train, command_data const } } +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 @@ -4088,12 +4197,12 @@ void TTrain::OnCommand_generictoggle( TTrain *Train, command_data const &Command if( item.GetDesiredValue() < 0.5 ) { // turn on // visual feedback - item.UpdateValue( 1.0, Train->dsbSwitch ); + item.UpdateValue( 1.0 ); } else { // turn off // visual feedback - item.UpdateValue( 0.0, Train->dsbSwitch ); + item.UpdateValue( 0.0 ); } } } @@ -4591,6 +4700,13 @@ void TTrain::OnCommand_doorcloseall( TTrain *Train, command_data const &Command } } +void TTrain::OnCommand_doorsteptoggle( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + Train->mvOccupied->PermitDoorStep( false == Train->mvOccupied->Doors.step_enabled ); + } +} + void TTrain::OnCommand_carcouplingincrease( TTrain *Train, command_data const &Command ) { if( ( true == Command.freefly ) @@ -4860,11 +4976,11 @@ void TTrain::OnCommand_cabchangeforward( TTrain *Train, command_data const &Comm if( false == Train->CabChange( 1 ) ) { if( TestFlag( Train->DynamicObject->MoverParameters->Couplers[ end::front ].CouplingFlag, coupling::gangway ) ) { // przejscie do nastepnego pojazdu - TDynamicObject *dynobj = Train->DynamicObject->PrevConnected; + TDynamicObject *dynobj = Train->DynamicObject->PrevConnected(); dynobj->MoverParameters->ActiveCab = ( - Train->DynamicObject->PrevConnectedNo ? - -1 : - 1 ); + Train->DynamicObject->MoverParameters->Neighbours[end::front].vehicle_end ? + -1 : + 1 ); Train->MoveToVehicle(dynobj); } } @@ -4877,11 +4993,11 @@ void TTrain::OnCommand_cabchangebackward( TTrain *Train, command_data const &Com if( false == Train->CabChange( -1 ) ) { if( TestFlag( Train->DynamicObject->MoverParameters->Couplers[ end::rear ].CouplingFlag, coupling::gangway ) ) { // przejscie do nastepnego pojazdu - TDynamicObject *dynobj = Train->DynamicObject->NextConnected; + TDynamicObject *dynobj = Train->DynamicObject->NextConnected(); dynobj->MoverParameters->ActiveCab = ( - Train->DynamicObject->NextConnectedNo ? - -1 : - 1 ); + Train->DynamicObject->MoverParameters->Neighbours[end::rear].vehicle_end ? + -1 : + 1 ); Train->MoveToVehicle(dynobj); } } @@ -5147,8 +5263,10 @@ bool TTrain::Update( double const Deltatime ) fPress[i][0] = p->MoverParameters->BrakePress; fPress[i][1] = p->MoverParameters->PipePress; fPress[i][2] = p->MoverParameters->ScndPipePress; - bDoors[i][1] = ( false == p->MoverParameters->Doors.instances[ side::right ].is_closed ); - bDoors[i][2] = ( false == p->MoverParameters->Doors.instances[ side::left ].is_closed ); + 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; @@ -5195,12 +5313,16 @@ bool TTrain::Update( double const Deltatime ) } else { - fPress[i][0] = 0; - fPress[i][1] = 0; - fPress[i][2] = 0; - bDoors[i][0] = false; - bDoors[i][1] = false; - bDoors[i][2] = false; + fPress[i][0] + = fPress[i][1] + = fPress[i][2] + = 0; + bDoors[i][0] + = bDoors[i][1] + = bDoors[i][2] + = bDoors[i][3] + = bDoors[i][4] + = false; bSlip[i] = false; iUnits[i] = 0; cCode[i] = 0; //'0'; @@ -5208,11 +5330,12 @@ bool TTrain::Update( double const Deltatime ) } } - if (mvControlled == mvOccupied) - fEIMParams[0][3] = mvControlled->eimv[eimv_Fzad]; // procent zadany - else - fEIMParams[0][3] = - mvControlled->eimv[eimv_Fzad] - mvOccupied->LocalBrakeRatio(); // procent zadany +// if (mvControlled == mvOccupied) +// fEIMParams[0][3] = mvControlled->eimv[eimv_Fzad]; // procent zadany +// else +// fEIMParams[0][3] = +// mvControlled->eimv[eimv_Fzad] - mvOccupied->LocalBrakeRatio(); // procent zadany + fEIMParams[0][3] = mvOccupied->eimic_real; fEIMParams[0][4] = Max0R(fEIMParams[0][3], 0); fEIMParams[0][5] = -Min0R(fEIMParams[0][3], 0); fEIMParams[0][1] = fEIMParams[0][4] * mvControlled->eimv[eimv_Fful]; @@ -5286,14 +5409,14 @@ bool TTrain::Update( double const Deltatime ) { TDynamicObject *tmp; tmp = NULL; - if (DynamicObject->NextConnected) + if (DynamicObject->NextConnected()) if ((TestFlag(mvControlled->Couplers[1].CouplingFlag, ctrain_controll)) && (mvOccupied->ActiveCab == 1)) - tmp = DynamicObject->NextConnected; - if (DynamicObject->PrevConnected) + tmp = DynamicObject->NextConnected(); + if (DynamicObject->PrevConnected()) if ((TestFlag(mvControlled->Couplers[0].CouplingFlag, ctrain_controll)) && (mvOccupied->ActiveCab == -1)) - tmp = DynamicObject->PrevConnected; + tmp = DynamicObject->PrevConnected(); if( tmp ) { if( tmp->MoverParameters->Power > 0 ) { if( ggI1B.SubModel ) { @@ -5334,7 +5457,7 @@ bool TTrain::Update( double const Deltatime ) ggClockHInd.Update(); } - Cabine[iCabn].Update(); // nowy sposób ustawienia animacji + Cabine[iCabn].Update( mvControlled->Battery || mvControlled->ConverterFlag ); // nowy sposób ustawienia animacji if (ggZbS.SubModel) { ggZbS.UpdateValue(mvOccupied->Handle->GetCP()); @@ -5681,10 +5804,10 @@ bool TTrain::Update( double const Deltatime ) tmp = NULL; if ((TestFlag(mvControlled->Couplers[1].CouplingFlag, ctrain_controll)) && (mvOccupied->ActiveCab > 0)) - tmp = DynamicObject->NextConnected; + tmp = DynamicObject->NextConnected(); if ((TestFlag(mvControlled->Couplers[0].CouplingFlag, ctrain_controll)) && (mvOccupied->ActiveCab < 0)) - tmp = DynamicObject->PrevConnected; + tmp = DynamicObject->PrevConnected(); if (tmp) if ( mvControlled->Battery || mvControlled->ConverterFlag ) { @@ -5751,7 +5874,21 @@ bool TTrain::Update( double const Deltatime ) } } // McZapkie-080602: obroty (albo translacje) regulatorow - if (ggMainCtrl.SubModel) { + if( ggJointCtrl.SubModel != nullptr ) { + // joint master controller moves forward to adjust power and backward to adjust brakes + auto const brakerangemultiplier { + ( mvControlled->CoupledCtrl ? + mvControlled->MainCtrlPosNo + mvControlled->ScndCtrlPosNo : + mvControlled->MainCtrlPosNo ) + / static_cast(LocalBrakePosNo) }; + ggJointCtrl.UpdateValue( + ( mvOccupied->LocalBrakePosA > 0.0 ? mvOccupied->LocalBrakePosA * LocalBrakePosNo * -1 * brakerangemultiplier : + mvControlled->CoupledCtrl ? double( mvControlled->MainCtrlPos + mvControlled->ScndCtrlPos ) : + double( mvControlled->MainCtrlPos ) ), + dsbNastawnikJazdy ); + ggJointCtrl.Update(); + } + if ( ggMainCtrl.SubModel != nullptr ) { #ifdef _WIN32 if( ( DynamicObject->Mechanik != nullptr ) @@ -5792,6 +5929,7 @@ bool TTrain::Update( double const Deltatime ) dsbNastawnikBocz ); ggScndCtrl.Update(); } + ggScndCtrlButton.Update(); if (ggDirKey.SubModel) { if (mvControlled->TrainType != dt_EZT) ggDirKey.UpdateValue( @@ -5837,7 +5975,8 @@ bool TTrain::Update( double const Deltatime ) ggBrakeCtrl.UpdateValue(mvOccupied->fBrakeCtrlPos); ggBrakeCtrl.Update(); } - if( ggLocalBrake.SubModel ) { + + if( ggLocalBrake.SubModel != nullptr ) { #ifdef _WIN32 if( ( DynamicObject->Mechanik != nullptr ) && ( false == DynamicObject->Mechanik->AIControllFlag ) // nie blokujemy AI @@ -6424,7 +6563,7 @@ bool TTrain::CabChange(int iDirection) DynamicObject->asBaseDir + DynamicObject->MoverParameters->TypeName + ".mmd" ) ) { // zmiana kabiny w ramach tego samego pojazdu DynamicObject->MoverParameters->CabActivisation(); // załączenie rozrządu (wirtualne kabiny) - DynamicObject->Mechanik->CheckVehicles( Change_direction ); + DynamicObject->Mechanik->DirectionChange(); return true; // udało się zmienić kabinę } } @@ -6889,6 +7028,9 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) if( dsbReverserKey.offset() == nullvector ) { dsbReverserKey.offset( ggDirKey.model_offset() ); } + if( dsbNastawnikJazdy.offset() == nullvector ) { + dsbNastawnikJazdy.offset( ggJointCtrl.model_offset() ); + } if( dsbNastawnikJazdy.offset() == nullvector ) { dsbNastawnikJazdy.offset( ggMainCtrl.model_offset() ); } @@ -6987,26 +7129,25 @@ void TTrain::DynamicSet(TDynamicObject *d) mvOccupied = mvControlled = d ? DynamicObject->MoverParameters : NULL; // albo silnikowy w EZT if (!DynamicObject) return; - if (mvControlled->TrainType & dt_EZT) // na razie dotyczy to EZT - if (DynamicObject->NextConnected ? mvControlled->Couplers[1].AllowedFlag & ctrain_depot : - false) - { // gdy jest człon od sprzęgu 1, a sprzęg łączony - // warsztatowo (powiedzmy) - if ((mvControlled->Power < 1.0) && (mvControlled->Couplers[1].Connected->Power > - 1.0)) // my nie mamy mocy, ale ten drugi ma - mvControlled = - DynamicObject->NextConnected->MoverParameters; // będziemy sterować tym z mocą + // TODO: leverage code already present in TDynamicObject::ControlledFind() + if( ( d->MoverParameters->TrainType == dt_EZT ) + || ( d->MoverParameters->TrainType == dt_DMU ) ) { + + if( ( d->NextConnected() != nullptr ) + && ( true == TestFlag( d->MoverParameters->Couplers[ end::rear ].AllowedFlag, coupling::permanent ) ) ) { + if( ( mvControlled->Power < 1.0 ) && ( mvControlled->Couplers[ 1 ].Connected->Power > 1.0 ) ) { + // my nie mamy mocy, ale ten drugi ma + mvControlled = DynamicObject->NextConnected()->MoverParameters; // będziemy sterować tym z mocą + } } - else if (DynamicObject->PrevConnected ? - mvControlled->Couplers[0].AllowedFlag & ctrain_depot : - false) - { // gdy jest człon od sprzęgu 0, a sprzęg łączony - // warsztatowo (powiedzmy) - if ((mvControlled->Power < 1.0) && (mvControlled->Couplers[0].Connected->Power > - 1.0)) // my nie mamy mocy, ale ten drugi ma - mvControlled = - DynamicObject->PrevConnected->MoverParameters; // będziemy sterować tym z mocą + else if( ( d->PrevConnected() != nullptr ) + && ( true == TestFlag( d->MoverParameters->Couplers[ end::front ].AllowedFlag, coupling::permanent ) ) ) { + if( ( mvControlled->Power < 1.0 ) && ( mvControlled->Couplers[ 0 ].Connected->Power > 1.0 ) ) { + // my nie mamy mocy, ale ten drugi ma + mvControlled = DynamicObject->PrevConnected()->MoverParameters; // będziemy sterować tym z mocą + } } + } mvSecond = NULL; // 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 ? @@ -7226,9 +7367,11 @@ void TTrain::clear_cab_controls() // other cab controls // TODO: arrange in more readable manner, and eventually refactor + ggJointCtrl.Clear(); ggMainCtrl.Clear(); ggMainCtrlAct.Clear(); ggScndCtrl.Clear(); + ggScndCtrlButton.Clear(); ggDirKey.Clear(); ggBrakeCtrl.Clear(); ggLocalBrake.Clear(); @@ -7697,6 +7840,13 @@ void TTrain::set_cab_controls( int const Cab ) { 1.f : 0.f ); } + // tempomat + if( ggScndCtrlButton.type() != TGaugeType::push ) { + ggScndCtrlButton.PutValue( + ( mvControlled->ScndCtrlPos > 0 ) ? + 1.f : + 0.f ); + } // 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 @@ -7807,7 +7957,8 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co // TODO: move viable dedicated lights to the automatic light array std::unordered_map const autolights = { { "i-doorpermit_left:", &mvOccupied->Doors.instances[side::left].open_permit }, - { "i-doorpermit_right:", &mvOccupied->Doors.instances[ side::right ].open_permit } + { "i-doorpermit_right:", &mvOccupied->Doors.instances[ side::right ].open_permit }, + { "i-doorstep:", &mvOccupied->Doors.step_enabled } }; { auto lookup = autolights.find( Label ); @@ -7860,6 +8011,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int const Cabindex) { std::unordered_map const gauges = { + { "jointctrl:", ggJointCtrl }, { "mainctrl:", ggMainCtrl }, { "scndctrl:", ggScndCtrl }, { "dirkey:" , ggDirKey }, @@ -7953,6 +8105,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "cablight_sw:", ggCabLightButton }, { "cablightdim_sw:", ggCabLightDimButton }, { "battery_sw:", ggBatteryButton }, + { "tempomat_sw:", ggScndCtrlButton }, { "universal0:", ggUniversals[ 0 ] }, { "universal1:", ggUniversals[ 1 ] }, { "universal2:", ggUniversals[ 2 ] }, @@ -7964,14 +8117,32 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "universal8:", ggUniversals[ 8 ] }, { "universal9:", ggUniversals[ 9 ] } }; - auto lookup = gauges.find( Label ); - if( lookup != gauges.end() ) { - lookup->second.Load( Parser, DynamicObject); - m_controlmapper.insert( lookup->second, lookup->first ); - return true; + { + auto lookup = gauges.find( Label ); + if( lookup != gauges.end() ) { + lookup->second.Load( Parser, DynamicObject ); + m_controlmapper.insert( lookup->second, lookup->first ); + return true; + } } + // TODO: move viable dedicated gauges to the automatic array + std::unordered_map const autoboolgauges = { + { "doorstep_sw:", &mvOccupied->Doors.step_enabled }, + { "coolingfans_sw:", &mvControlled->RVentForceOn } + }; + { + 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; + } + } + // ABu 090305: uniwersalne przyciski lub inne rzeczy - else if( Label == "mainctrlact:" ) { + if( Label == "mainctrlact:" ) { ggMainCtrlAct.Load( Parser, DynamicObject); } // SEKCJA WSKAZNIKOW @@ -8053,7 +8224,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con Parser.getTokens(2, false); Parser >> i >> j; auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject); + gauge.Load(Parser, DynamicObject, 0.1); gauge.AssignFloat(&fPress[i - 1][j]); } else if ((Label == "brakepress:") || (Label == "brakepressb:")) @@ -8161,6 +8332,9 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con } } } + else if( Label == "clock_seconds:" ) { + ggClockSInd.Load( Parser, DynamicObject ); + } else if (Label == "evoltage:") { // woltomierz napiecia silnikow diff --git a/Train.h b/Train.h index 654cad36..1386357d 100644 --- a/Train.h +++ b/Train.h @@ -18,6 +18,8 @@ http://mozilla.org/MPL/2.0/. #include "command.h" #include "pythonscreenviewer.h" +#undef snprintf // pyint.h->python + // typedef enum {st_Off, st_Starting, st_On, st_ShuttingDown} T4State; const int maxcab = 2; @@ -33,7 +35,7 @@ class TCab { public: // methods void Load(cParser &Parser); - void Update(); + void Update( bool const Power ); TGauge &Gauge( int n = -1 ); // pobranie adresu obiektu TButton &Button( int n = -1 ); // pobranie adresu obiektu // members @@ -52,7 +54,7 @@ public: private: // members - std::vector ggList; + std::deque ggList; // need a container which doesn't invalidate references std::vector btList; }; @@ -160,6 +162,7 @@ class TTrain // 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 static void OnCommand_aidriverenable( TTrain *Train, command_data const &Command ); static void OnCommand_aidriverdisable( TTrain *Train, command_data const &Command ); + static void OnCommand_jointcontrollerset( TTrain *Train, command_data const &Command ); static void OnCommand_mastercontrollerincrease( TTrain *Train, command_data const &Command ); static void OnCommand_mastercontrollerincreasefast( TTrain *Train, command_data const &Command ); static void OnCommand_mastercontrollerdecrease( TTrain *Train, command_data const &Command ); @@ -171,6 +174,7 @@ class TTrain static void OnCommand_secondcontrollerdecreasefast( TTrain *Train, command_data const &Command ); static void OnCommand_secondcontrollerset( TTrain *Train, command_data const &Command ); static void OnCommand_notchingrelaytoggle( TTrain *Train, command_data const &Command ); + static void OnCommand_tempomattoggle( TTrain *Train, command_data const &Command ); static void OnCommand_mucurrentindicatorothersourceactivate( TTrain *Train, command_data const &Command ); static void OnCommand_independentbrakeincrease( TTrain *Train, command_data const &Command ); static void OnCommand_independentbrakeincreasefast( TTrain *Train, command_data const &Command ); @@ -267,6 +271,7 @@ class TTrain static void OnCommand_motorblowersenablerear( TTrain *Train, command_data const &Command ); static void OnCommand_motorblowersdisablerear( TTrain *Train, command_data const &Command ); static void OnCommand_motorblowersdisableall( TTrain *Train, command_data const &Command ); + static void OnCommand_coolingfanstoggle( TTrain *Train, command_data const &Command ); static void OnCommand_motorconnectorsopen( TTrain *Train, command_data const &Command ); static void OnCommand_motorconnectorsclose( TTrain *Train, command_data const &Command ); static void OnCommand_motordisconnect( TTrain *Train, command_data const &Command ); @@ -328,6 +333,7 @@ class TTrain static void OnCommand_doorcloseright( TTrain *Train, command_data const &Command ); static void OnCommand_dooropenall( TTrain *Train, command_data const &Command ); static void OnCommand_doorcloseall( TTrain *Train, command_data const &Command ); + static void OnCommand_doorsteptoggle( TTrain *Train, command_data const &Command ); static void OnCommand_carcouplingincrease( TTrain *Train, command_data const &Command ); static void OnCommand_carcouplingdisconnect( TTrain *Train, command_data const &Command ); static void OnCommand_departureannounce( TTrain *Train, command_data const &Command ); @@ -380,10 +386,11 @@ public: // reszta może by?publiczna TGauge ggWater1TempB; // McZapkie: definicje regulatorow + TGauge ggJointCtrl; TGauge ggMainCtrl; TGauge ggMainCtrlAct; TGauge ggScndCtrl; - TGauge ggScndCtrlButton; // NOTE: not used? + TGauge ggScndCtrlButton; TGauge ggDirKey; TGauge ggBrakeCtrl; TGauge ggLocalBrake; @@ -646,7 +653,7 @@ private: float fHCurrent[ 4 ] = { 0.0f, 0.0f, 0.0f, 0.0f }; // pr?dy: suma i amperomierze 1,2,3 float fEngine[ 4 ] = { 0.0f, 0.0f, 0.0f, 0.0f }; // obroty te? trzeba pobra? int iCarNo, iPowerNo, iUnitNo; // liczba pojazdow, czlonow napednych i jednostek spiętych ze sobą - bool bDoors[20][3]; // drzwi dla wszystkich czlonow + bool bDoors[20][5]; // drzwi dla wszystkich czlonow; left+right, left, right, step_left, step_right int iUnits[20]; // numer jednostki int iDoorNo[20]; // liczba drzwi char cCode[20]; // kod pojazdu diff --git a/command.cpp b/command.cpp index d63d134f..e10f1171 100644 --- a/command.cpp +++ b/command.cpp @@ -23,6 +23,7 @@ commanddescription_sequence Commands_descriptions = { { "aidriverenable", command_target::vehicle, command_mode::oneoff }, { "aidriverdisable", command_target::vehicle, command_mode::oneoff }, + { "jointcontrollerset", command_target::vehicle, command_mode::oneoff }, { "mastercontrollerincrease", command_target::vehicle, command_mode::oneoff }, { "mastercontrollerincreasefast", command_target::vehicle, command_mode::oneoff }, { "mastercontrollerdecrease", command_target::vehicle, command_mode::oneoff }, @@ -153,6 +154,7 @@ commanddescription_sequence Commands_descriptions = { { "doorcloseleft", command_target::vehicle, command_mode::oneoff }, { "doorcloseright", command_target::vehicle, command_mode::oneoff }, { "doorcloseall", command_target::vehicle, command_mode::oneoff }, + { "doorsteptoggle", command_target::vehicle, command_mode::oneoff }, { "departureannounce", command_target::vehicle, command_mode::oneoff }, { "doorlocktoggle", command_target::vehicle, command_mode::oneoff }, { "pantographcompressorvalvetoggle", command_target::vehicle, command_mode::oneoff }, @@ -224,6 +226,8 @@ commanddescription_sequence Commands_descriptions = { { "motorblowerstogglefront", command_target::vehicle, command_mode::oneoff }, { "motorblowerstogglerear", command_target::vehicle, command_mode::oneoff }, { "motorblowersdisableall", command_target::vehicle, command_mode::oneoff }, + { "coolingfanstoggle", command_target::vehicle, command_mode::oneoff }, + { "tempomattoggle", command_target::vehicle, command_mode::oneoff }, { "timejump", command_target::simulation, command_mode::oneoff }, { "timejumplarge", command_target::simulation, command_mode::oneoff }, { "timejumpsmall", command_target::simulation, command_mode::oneoff }, diff --git a/command.h b/command.h index c0c9ace6..1d97b9b3 100644 --- a/command.h +++ b/command.h @@ -17,6 +17,7 @@ enum class user_command { aidriverenable = 0, aidriverdisable, + jointcontrollerset, mastercontrollerincrease, mastercontrollerincreasefast, mastercontrollerdecrease, @@ -146,6 +147,7 @@ enum class user_command { doorcloseleft, doorcloseright, doorcloseall, + doorsteptoggle, departureannounce, doorlocktoggle, pantographcompressorvalvetoggle, @@ -217,6 +219,8 @@ enum class user_command { motorblowerstogglefront, motorblowerstogglerear, motorblowersdisableall, + coolingfanstoggle, + tempomattoggle, timejump, timejumplarge, diff --git a/driverkeyboardinput.cpp b/driverkeyboardinput.cpp index 0f4d7e84..9a098b51 100644 --- a/driverkeyboardinput.cpp +++ b/driverkeyboardinput.cpp @@ -27,6 +27,7 @@ driverkeyboard_input::default_bindings() { m_bindingsetups = { { user_command::aidriverenable, GLFW_KEY_Q | keymodifier::shift }, { user_command::aidriverdisable, GLFW_KEY_Q }, + // jointcontrollerset, { user_command::mastercontrollerincrease, GLFW_KEY_KP_ADD }, { user_command::mastercontrollerincreasefast, GLFW_KEY_KP_ADD | keymodifier::shift }, { user_command::mastercontrollerdecrease, GLFW_KEY_KP_SUBTRACT }, @@ -154,6 +155,7 @@ driverkeyboard_input::default_bindings() { // doorcloseleft, // doorcloseright, { user_command::doorcloseall, GLFW_KEY_SLASH | keymodifier::control }, + // doorsteptoggle, { user_command::departureannounce, GLFW_KEY_SLASH }, { user_command::doorlocktoggle, GLFW_KEY_S | keymodifier::control }, { user_command::pantographcompressorvalvetoggle, GLFW_KEY_V | keymodifier::control }, @@ -225,6 +227,9 @@ driverkeyboard_input::default_bindings() { { user_command::motorblowerstogglefront, GLFW_KEY_N | keymodifier::shift }, { user_command::motorblowerstogglerear, GLFW_KEY_M | keymodifier::shift }, { user_command::motorblowersdisableall, GLFW_KEY_M | keymodifier::control }, + // coolingfanstoggle + // tempomattoggle + // admin_timejump, { user_command::timejumplarge, GLFW_KEY_F1 | keymodifier::control }, { user_command::timejumpsmall, GLFW_KEY_F1 | keymodifier::shift }, diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index bb7e5b1a..9bcb0d91 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -22,6 +22,8 @@ http://mozilla.org/MPL/2.0/. #include "uilayer.h" #include "Logs.h" +auto const EU07_CONTROLLER_MOUSESLIDERSIZE{ 0.65 }; + void mouse_slider::bind( user_command const &Command ) { @@ -30,6 +32,7 @@ mouse_slider::bind( user_command const &Command ) { auto const *train { simulation::Train }; TMoverParameters const *vehicle { nullptr }; switch( m_command ) { + case user_command::jointcontrollerset: case user_command::mastercontrollerset: case user_command::secondcontrollerset: { vehicle = ( train ? train->Controlled() : nullptr ); @@ -48,6 +51,29 @@ mouse_slider::bind( user_command const &Command ) { // calculate initial value and accepted range switch( m_command ) { + case user_command::jointcontrollerset: { + // NOTE: special case, merges two separate controls + auto const *occupied { train ? train->Occupied() : nullptr }; + if( occupied == nullptr ) { return; } + + auto const powerrange { static_cast( + vehicle->CoupledCtrl ? + vehicle->MainCtrlPosNo + vehicle->ScndCtrlPosNo : + vehicle->MainCtrlPosNo ) }; + // for simplicity upper half of the range controls power, lower controls brakes + auto const brakerangemultiplier { powerrange / LocalBrakePosNo }; + + m_valuerange = 1.0; + m_value = + 0.5 + + 0.5 * ( vehicle->CoupledCtrl ? + vehicle->MainCtrlPos + vehicle->ScndCtrlPos : + vehicle->MainCtrlPos ) / powerrange + - 0.5 * occupied->LocalBrakePosA; + m_analogue = true; + m_invertrange = false; + break; + } case user_command::mastercontrollerset: { m_valuerange = ( vehicle->CoupledCtrl ? @@ -58,24 +84,28 @@ mouse_slider::bind( user_command const &Command ) { vehicle->MainCtrlPos + vehicle->ScndCtrlPos : vehicle->MainCtrlPos ); m_analogue = false; + m_invertrange = false; break; } case user_command::secondcontrollerset: { m_valuerange = vehicle->ScndCtrlPosNo; m_value = vehicle->ScndCtrlPos; m_analogue = false; + m_invertrange = true; break; } case user_command::trainbrakeset: { m_valuerange = 1.0; m_value = ( vehicle->fBrakeCtrlPos - vehicle->Handle->GetPos( bh_MIN ) ) / ( vehicle->Handle->GetPos( bh_MAX ) - vehicle->Handle->GetPos( bh_MIN ) ); m_analogue = true; + m_invertrange = true; break; } case user_command::independentbrakeset: { m_valuerange = 1.0; m_value = vehicle->LocalBrakePosA; m_analogue = true; + m_invertrange = true; break; } default: { @@ -87,14 +117,18 @@ mouse_slider::bind( user_command const &Command ) { Application.get_cursor_pos( m_cursorposition.x, m_cursorposition.y ); Application.set_cursor( GLFW_CURSOR_DISABLED ); - auto const controlsize { Global.iWindowHeight * 0.75 }; + auto const controlsize { Global.iWindowHeight * EU07_CONTROLLER_MOUSESLIDERSIZE }; auto const controledge { Global.iWindowHeight * 0.5 + controlsize * 0.5 }; auto const stepsize { controlsize / m_valuerange }; + if( m_invertrange ) { + m_value = ( m_analogue ? 1.0 : m_valuerange ) - m_value; + } + Application.set_cursor_pos( Global.iWindowWidth * 0.5, ( m_analogue ? - controledge - ( 1.0 - m_value ) * controlsize : + controledge - m_value * controlsize : controledge - m_value * stepsize - 0.5 * stepsize ) ); } @@ -109,15 +143,17 @@ mouse_slider::release() { void mouse_slider::on_move( double const Mousex, double const Mousey ) { - auto const controlsize { Global.iWindowHeight * 0.75 }; + auto const controlsize { Global.iWindowHeight * EU07_CONTROLLER_MOUSESLIDERSIZE }; auto const controledge { Global.iWindowHeight * 0.5 + controlsize * 0.5 }; auto const stepsize { controlsize / m_valuerange }; auto mousey = clamp( Mousey, controledge - controlsize, controledge ); m_value = ( m_analogue ? - 1.0 - ( ( controledge - mousey ) / controlsize ) : + ( controledge - mousey ) / controlsize : std::floor( ( controledge - mousey ) / stepsize ) ); + if( m_invertrange ) { + m_value = ( m_analogue ? 1.0 : m_valuerange ) - m_value; } } @@ -359,47 +395,49 @@ drivermouse_input::button( int const Button, int const Action ) { } } - switch( mousecommand ) { - case user_command::mastercontrollerincrease: - case user_command::mastercontrollerdecrease: - case user_command::secondcontrollerincrease: - case user_command::secondcontrollerdecrease: - case user_command::trainbrakeincrease: - case user_command::trainbrakedecrease: - case user_command::independentbrakeincrease: - case user_command::independentbrakedecrease: { - // these commands trigger varying repeat rate mode, - // which scales the rate based on the distance of the cursor from its point when the command was first issued - m_varyingpollrateorigin = m_cursorposition; - m_varyingpollrate = true; - break; - } - case user_command::mastercontrollerset: - case user_command::secondcontrollerset: - case user_command::trainbrakeset: - case user_command::independentbrakeset: { - m_slider.bind( mousecommand ); - mousecommand = user_command::none; - return; - } - default: { - break; - } - } - // NOTE: basic keyboard controls don't have any parameters - // NOTE: as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 - // TODO: pass correct entity id once the missing systems are in place + switch( mousecommand ) { + case user_command::mastercontrollerincrease: + case user_command::mastercontrollerdecrease: + case user_command::secondcontrollerincrease: + case user_command::secondcontrollerdecrease: + case user_command::trainbrakeincrease: + case user_command::trainbrakedecrease: + case user_command::independentbrakeincrease: + case user_command::independentbrakedecrease: { + // these commands trigger varying repeat rate mode, + // which scales the rate based on the distance of the cursor from its point when the command was first issued + m_varyingpollrateorigin = m_cursorposition; + m_varyingpollrate = true; + break; + } + case user_command::jointcontrollerset: + case user_command::mastercontrollerset: + case user_command::secondcontrollerset: + case user_command::trainbrakeset: + case user_command::independentbrakeset: { + m_slider.bind( mousecommand ); + mousecommand = user_command::none; + return; + } + default: { + break; + } + } + + // NOTE: basic keyboard controls don't have any parameters + // NOTE: as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 + // TODO: pass correct entity id once the missing systems are in place m_relay.post( mousecommand, 0, 0, Action, 0 ); - if (!pickwaiting) // already depressed + if (!pickwaiting) // already depressed m_relay.post( mousecommand, 0, 0, GLFW_RELEASE, 0 ); - m_updateaccumulator = -0.25; // prevent potential command repeat right after issuing one - } - else { - // if we don't have any recognized element under the cursor and the right button was pressed, enter view panning mode - if( Button == GLFW_MOUSE_BUTTON_RIGHT ) { - m_pickmodepanning = true; - } - } + m_updateaccumulator = -0.25; // prevent potential command repeat right after issuing one + } + else { + // if we don't have any recognized element under the cursor and the right button was pressed, enter view panning mode + if( Button == GLFW_MOUSE_BUTTON_RIGHT ) { + m_pickmodepanning = true; + } + } }); } } @@ -452,6 +490,9 @@ void drivermouse_input::default_bindings() { m_buttonbindings = { + { "jointctrl:", { + user_command::jointcontrollerset, + user_command::none } }, { "mainctrl:", { user_command::mastercontrollerset, user_command::none } }, @@ -461,6 +502,9 @@ drivermouse_input::default_bindings() { { "shuntmodepower:", { user_command::secondcontrollerincrease, user_command::secondcontrollerdecrease } }, + { "tempomat_sw:", { + user_command::tempomattoggle, + user_command::none } }, { "dirkey:", { user_command::reverserincrease, user_command::reverserdecrease } }, @@ -519,6 +563,9 @@ drivermouse_input::default_bindings() { { "motorblowersalloff_sw:", { user_command::motorblowersdisableall, user_command::none } }, + { "coolingfans_sw:", { + user_command::coolingfanstoggle, + user_command::none } }, { "main_off_bt:", { user_command::linebreakeropen, user_command::none } }, @@ -591,6 +638,9 @@ drivermouse_input::default_bindings() { { "dooralloff_sw:", { user_command::doorcloseall, user_command::none } }, + { "doorstep_sw:", { + user_command::doorsteptoggle, + user_command::none } }, { "departure_signal_bt:", { user_command::departureannounce, user_command::none } }, diff --git a/drivermouseinput.h b/drivermouseinput.h index 2847fecb..e2b22bd4 100644 --- a/drivermouseinput.h +++ b/drivermouseinput.h @@ -39,6 +39,7 @@ private: double m_value { 0.0 }; double m_valuerange { 0.0 }; bool m_analogue { false }; + bool m_invertrange { false }; glm::dvec2 m_cursorposition { 0.0 }; }; diff --git a/driveruilayer.cpp b/driveruilayer.cpp index e44deff6..4fa84329 100644 --- a/driveruilayer.cpp +++ b/driveruilayer.cpp @@ -23,6 +23,7 @@ driver_ui::driver_ui() { clear_panels(); // bind the panels with ui object. maybe not the best place for this but, eh push_back( &m_aidpanel ); + push_back( &m_scenariopanel ); push_back( &m_timetablepanel ); push_back( &m_debugpanel ); push_back( &m_transcriptspanel ); @@ -36,6 +37,10 @@ driver_ui::driver_ui() { m_aidpanel.title = locale::strings[ locale::string::driver_aid_header ]; + m_scenariopanel.title = locale::strings[ locale::string::driver_scenario_header ]; + m_scenariopanel.size_min = { 435, 85 }; + m_scenariopanel.size_max = { Global.iWindowWidth * 0.95, Global.iWindowHeight * 0.95 }; + m_timetablepanel.title = locale::strings[ locale::string::driver_timetable_header ]; m_timetablepanel.size_min = { 435, 110 }; m_timetablepanel.size_max = { 435, Global.iWindowHeight * 0.95 }; @@ -51,7 +56,7 @@ void driver_ui::render_menu_contents() { if (ImGui::BeginMenu(locale::strings[locale::string::ui_mode_windows].c_str())) { ImGui::MenuItem(m_aidpanel.title.c_str(), "F1", &m_aidpanel.is_open); - ImGui::MenuItem(m_timetablepanel.title.c_str(), "F2", &m_timetablepanel.is_open); + ImGui::MenuItem(locale::strings[locale::string::driver_timetable_name].c_str(), "F2", &m_timetablepanel.is_open); ImGui::MenuItem(m_debugpanel.get_name().c_str(), "F12", &m_debugpanel.is_open); ImGui::MenuItem(m_mappanel.get_name().c_str(), "Tab", &m_mappanel.is_open); @@ -75,6 +80,7 @@ driver_ui::on_key( int const Key, int const Action ) { case GLFW_KEY_TAB: case GLFW_KEY_F1: case GLFW_KEY_F2: + case GLFW_KEY_F3: case GLFW_KEY_F10: case GLFW_KEY_F12: { // ui mode selectors @@ -128,6 +134,12 @@ driver_ui::on_key( int const Key, int const Action ) { return true; } + case GLFW_KEY_F3: { + // debug panel + m_scenariopanel.is_open = !m_scenariopanel.is_open; + return true; + } + case GLFW_KEY_F12: { // debug panel m_debugpanel.is_open = !m_debugpanel.is_open; diff --git a/driveruilayer.h b/driveruilayer.h index 8ec28f5f..b3dd3bce 100644 --- a/driveruilayer.h +++ b/driveruilayer.h @@ -51,6 +51,7 @@ private: // members drivingaid_panel m_aidpanel { "Driving Aid", true }; + scenario_panel m_scenariopanel { "Scenario", true }; timetable_panel m_timetablepanel { "Timetable", false }; debug_panel m_debugpanel { "Debug Data", false }; transcripts_panel m_transcriptspanel { "Transcripts", true }; // voice transcripts diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 553aff9e..a91c95f3 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -27,8 +27,6 @@ http://mozilla.org/MPL/2.0/. #include "utilities.h" #include "Logs.h" -#undef snprintf // defined by train.h->pyint.h->python - void drivingaid_panel::update() { @@ -149,6 +147,79 @@ drivingaid_panel::update() { } } +void +scenario_panel::update() { + + if( false == is_open ) { return; } + + text_lines.clear(); + + auto const *train { simulation::Train }; + auto const *controlled { ( train ? train->Dynamic() : nullptr ) }; + auto const &camera { Global.pCamera }; + m_nearest = ( + false == FreeFlyModeFlag ? controlled : + camera.m_owner != nullptr ? camera.m_owner : + std::get( simulation::Region->find_vehicle( camera.Pos, 20, false, false ) ) ); // w trybie latania lokalizujemy wg mapy + if( m_nearest == nullptr ) { return; } + auto const *owner { ( + ( ( m_nearest->Mechanik != nullptr ) && ( m_nearest->Mechanik->Primary() ) ) ? + m_nearest->Mechanik : + m_nearest->ctOwner ) }; + if( owner == nullptr ) { return; } + + std::string textline = + locale::strings[ locale::string::driver_scenario_currenttask ] + "\n " + + owner->OrderCurrent(); + + text_lines.emplace_back( textline, Global.UITextColor ); +} + +void +scenario_panel::render() { + + if( false == is_open ) { return; } + if( true == text_lines.empty() ) { return; } + if( m_nearest == nullptr ) { return; } // possibly superfluous given the above but, eh + + auto flags = + ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_NoCollapse + | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); + + if( size.x > 0 ) { + ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) ); + } + if( size_min.x > 0 ) { + ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); + } + auto const panelname { ( + title.empty() ? + name : + title ) + + "###" + name }; + if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { + // potential assignment section + auto const *owner { ( + ( ( m_nearest->Mechanik != nullptr ) && ( m_nearest->Mechanik->Primary() ) ) ? + m_nearest->Mechanik : + m_nearest->ctOwner ) }; + if( owner != nullptr ) { + auto const assignmentheader { locale::strings[ locale::string::driver_scenario_assignment ] }; + if( ( false == owner->assignment().empty() ) + && ( true == ImGui::CollapsingHeader( assignmentheader.c_str() ) ) ) { + ImGui::TextWrapped( owner->assignment().c_str() ); + ImGui::Separator(); + } + } + // current task + for( auto const &line : text_lines ) { + ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); + } + } + ImGui::End(); +} + void timetable_panel::update() { @@ -164,12 +235,14 @@ timetable_panel::update() { { // current time std::snprintf( m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::driver_timetable_time ].c_str(), + locale::strings[ locale::string::driver_timetable_header ].c_str(), + 40, 40, + locale::strings[ locale::string::driver_timetable_name ].c_str(), time.wHour, time.wMinute, time.wSecond ); - text_lines.emplace_back( m_buffer.data(), Global.UITextColor ); + title = m_buffer.data(); } auto *vehicle { ( @@ -215,8 +288,7 @@ timetable_panel::update() { auto consistmass { owner->fMass }; auto consistlength { owner->fLength }; if( ( owner->mvControlling->TrainType != dt_DMU ) - && ( owner->mvControlling->TrainType != dt_EZT ) - && ( owner->pVehicles[end::front] != owner->pVehicles[end::rear] ) ) { + && ( owner->mvControlling->TrainType != dt_EZT ) ) { //odejmij lokomotywy czynne, a przynajmniej aktualną consistmass -= owner->pVehicle->MoverParameters->TotalMass; // subtract potential other half of a two-part vehicle @@ -224,7 +296,6 @@ timetable_panel::update() { if( previous != nullptr ) { consistmass -= previous->MoverParameters->TotalMass; } auto const *next { owner->pVehicle->Next( coupling::permanent ) }; if( next != nullptr ) { consistmass -= next->MoverParameters->TotalMass; } -// consistlength -= owner->pVehicle->MoverParameters->Dim.L; } std::snprintf( m_buffer.data(), m_buffer.size(), @@ -369,7 +440,12 @@ debug_panel::render() { if( size_min.x > 0 ) { ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); } - if( true == ImGui::Begin( name.c_str(), &is_open, flags ) ) { + auto const panelname { ( + title.empty() ? + name : + title ) + + "###" + name }; + if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { // header section for( auto const &line : text_lines ) { ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); @@ -568,10 +644,7 @@ debug_panel::update_vehicle_coupler( int const Side ) { // NOTE: mover and vehicle are guaranteed to be valid by the caller std::string couplerstatus { locale::strings[ locale::string::debug_vehicle_none ] }; - auto const *connected { ( - Side == end::front ? - m_input.vehicle->PrevConnected : - m_input.vehicle->NextConnected ) }; + auto const *connected { m_input.vehicle->MoverParameters->Neighbours[ Side ].vehicle }; if( connected == nullptr ) { return couplerstatus; } @@ -579,10 +652,10 @@ debug_panel::update_vehicle_coupler( int const Side ) { std::snprintf( m_buffer.data(), m_buffer.size(), - "%s [%d]%s", + "%s [%d] (%.1f m)", connected->name().c_str(), mover.Couplers[ Side ].CouplingFlag, - std::string( mover.Couplers[ Side ].CouplingFlag == 0 ? " (" + to_string( mover.Couplers[ Side ].CoupleDist, 1 ) + " m)" : "" ).c_str() ); + mover.Neighbours[ Side ].distance ); return { m_buffer.data() }; } @@ -686,7 +759,9 @@ debug_panel::update_section_ai( std::vector &Output ) { auto const &mechanik{ *m_input.mechanik }; // biezaca komenda dla AI - auto textline = "Current order: " + mechanik.OrderCurrent(); + auto textline = + "Current order: [" + std::to_string( mechanik.OrderPos ) + "] " + + mechanik.OrderCurrent(); if( mechanik.fStopTime < 0.0 ) { textline += "\n stop time: " + to_string( std::abs( mechanik.fStopTime ), 1 ); @@ -705,6 +780,12 @@ debug_panel::update_section_ai( std::vector &Output ) { "Distances:\n proximity: " + to_string( mechanik.ActualProximityDist, 0 ) + ", braking: " + to_string( mechanik.fBrakeDist, 0 ); + if( mechanik.Obstacle.distance < 5000 ) { + textline += + "\n obstacle: " + to_string( mechanik.Obstacle.distance, 0 ) + + " (" + mechanik.Obstacle.vehicle->asName + ")"; + } + Output.emplace_back( textline, Global.UITextColor ); // velocity factors diff --git a/driveruipanels.h b/driveruipanels.h index f118110a..117da7e3 100644 --- a/driveruipanels.h +++ b/driveruipanels.h @@ -39,6 +39,21 @@ private: std::array m_buffer; }; +class scenario_panel : public ui_panel { + +public: + scenario_panel( std::string const &Name, bool const Isopen ) + : ui_panel( Name, Isopen ) {} + + void update() override; + void render() override; + +private: +// members +// std::array m_buffer; + TDynamicObject const *m_nearest { nullptr }; +}; + class debug_panel : public ui_panel { public: diff --git a/scene.h b/scene.h index a42b0e73..2eafc3d7 100644 --- a/scene.h +++ b/scene.h @@ -53,6 +53,7 @@ struct scratch_data { std::vector couplings; TDynamicObject * driver { nullptr }; bool is_open { false }; + std::unordered_map assignment; } trainset; std::string name; diff --git a/scenenode.cpp b/scenenode.cpp index 9664c0a4..31cc7b96 100644 --- a/scenenode.cpp +++ b/scenenode.cpp @@ -46,10 +46,12 @@ bounding_area::serialize( std::ostream &Output ) const { // restores content of the struct from provided input stream void -bounding_area::deserialize( std::istream &Input ) { +bounding_area::deserialize( std::istream &Input, bool const Preserveradius ) { center = sn_utils::d_dvec3( Input ); - radius = std::max(radius, sn_utils::ld_float32( Input )); + radius = ( Preserveradius ? + std::max( radius, sn_utils::ld_float32( Input ) ) : + sn_utils::ld_float32( Input ) ); } diff --git a/scenenode.h b/scenenode.h index 18da3158..66c1487d 100644 --- a/scenenode.h +++ b/scenenode.h @@ -59,9 +59,9 @@ struct bounding_area { // stores content of the struct in provided output stream void serialize( std::ostream &Output ) const; - // restores content of the struct from provided input stream + // restores content of the struct from provided input stream. void - deserialize( std::istream &Input ); + deserialize( std::istream &Input, bool const Preserveradius = true ); }; //using group_handle = std::size_t; diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index db534c6a..1f8898ef 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -56,6 +56,7 @@ state_serializer::deserialize_begin( std::string const &Scenariofile ) { std::string, deserializefunction> > functionlist = { { "area", &state_serializer::deserialize_area }, + { "assignment", &state_serializer::deserialize_assignment }, { "atmo", &state_serializer::deserialize_atmo }, { "camera", &state_serializer::deserialize_camera }, { "config", &state_serializer::deserialize_config }, @@ -143,6 +144,20 @@ state_serializer::deserialize_area( cParser &Input, scene::scratch_data &Scratch } } +void +state_serializer::deserialize_assignment( cParser &Input, scene::scratch_data &Scratchpad ) { + + std::string token { Input.getToken() }; + while( ( false == token.empty() ) + && ( token != "endassignment" ) ) { + // assignment is expected to come as string pairs: language id and the actual assignment enclosed in quotes to form a single token + auto assignment{ Input.getToken() }; + win1250_to_ascii( assignment ); + Scratchpad.trainset.assignment.emplace( token, assignment ); + token = Input.getToken(); + } +} + void state_serializer::deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ) { @@ -640,6 +655,11 @@ state_serializer::deserialize_endtrainset( cParser &Input, scene::scratch_data & && ( vehicle->Mechanik->Primary() ) ) { // primary driver will receive the timetable for this trainset Scratchpad.trainset.driver = vehicle; + // they'll also receive assignment data if there's any + auto const lookup { Scratchpad.trainset.assignment.find( Global.asLang ) }; + if( lookup != Scratchpad.trainset.assignment.end() ) { + vehicle->Mechanik->assignment() = lookup->second; + } } if( vehicleindex > 0 ) { // from second vehicle on couple it with the previous one diff --git a/simulationstateserializer.h b/simulationstateserializer.h index 97e1fc4d..8ea5c71e 100644 --- a/simulationstateserializer.h +++ b/simulationstateserializer.h @@ -48,6 +48,7 @@ private: // methods // restores class data from provided stream void deserialize_area( cParser &Input, scene::scratch_data &Scratchpad ); + void deserialize_assignment( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_atmo( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_camera( cParser &Input, scene::scratch_data &Scratchpad ); void deserialize_config( cParser &Input, scene::scratch_data &Scratchpad ); diff --git a/simulationtime.cpp b/simulationtime.cpp index 3d2e72e4..e18c0216 100644 --- a/simulationtime.cpp +++ b/simulationtime.cpp @@ -114,7 +114,7 @@ scenario_time::update( double const Deltatime ) { } m_time.wHour -= 24; } - int leap { ( m_time.wYear % 4 == 0 ) && ( m_time.wYear % 100 != 0 ) || ( m_time.wYear % 400 == 0 ) }; + int leap { is_leap( m_time.wYear ) }; while( m_time.wDay > m_monthdaycounts[ leap ][ m_time.wMonth ] ) { m_time.wDay -= m_monthdaycounts[ leap ][ m_time.wMonth ]; @@ -123,7 +123,7 @@ scenario_time::update( double const Deltatime ) { if( m_time.wMonth > 12 ) { ++m_time.wYear; - leap = ( m_time.wYear % 4 == 0 ) && ( m_time.wYear % 100 != 0 ) || ( m_time.wYear % 400 == 0 ); + leap = is_leap( m_time.wYear ); m_time.wMonth -= 12; } } @@ -137,7 +137,7 @@ scenario_time::year_day( int Day, const int Month, const int Year ) const { { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; - int const leap { ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ) }; + int const leap { is_leap( Year ) }; for( int i = 1; i < Month; ++i ) Day += daytab[ leap ][ i ]; @@ -152,7 +152,7 @@ scenario_time::daymonth( WORD &Day, WORD &Month, WORD const Year, WORD const Yea { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; - int const leap { ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ) }; + int const leap { is_leap( Year ) }; WORD idx = 1; while( ( idx < 13 ) && ( Yearday >= daytab[ leap ][ idx ] ) ) { @@ -221,7 +221,7 @@ scenario_time::day_of_month( int const Week, int const Weekday, int const Month, { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; - int const leap { ( Year % 4 == 0 ) && ( Year % 100 != 0 ) || ( Year % 400 == 0 ) }; + int const leap { is_leap( Year ) }; while( day > daytab[ leap ][ Month ] ) { day -= 7; @@ -246,3 +246,11 @@ scenario_time::convert_transition_time( SYSTEMTIME &Time ) const { // NOTE: windows uses 0-6 range for days of week numbering, our methods use 1-7 Time.wDay = day_of_month( Time.wDay, Time.wDayOfWeek + 1, Time.wMonth, m_time.wYear ); } + +bool +scenario_time::is_leap( int const Year ) const { + + return ( ( Year % 4 == 0 ) && ( ( Year % 100 != 0 ) || ( Year % 400 == 0 ) ) ); + +} +//--------------------------------------------------------------------------- diff --git a/simulationtime.h b/simulationtime.h index 436a0948..7d268242 100644 --- a/simulationtime.h +++ b/simulationtime.h @@ -69,6 +69,9 @@ private: // returns number of days between specified days of week int weekdays( int const First, int const Second ) const; + // returns true if specified year is a leap year, false otherwise + bool + is_leap( int const Year ) const; SYSTEMTIME m_time; double m_milliseconds{ 0.0 }; diff --git a/sun.cpp b/sun.cpp index e0535c79..642ca278 100644 --- a/sun.cpp +++ b/sun.cpp @@ -173,17 +173,11 @@ void cSun::move() { m_body.rascen = clamp_circular( radtodeg * std::atan2( top, bottom ) ); - // Greenwich mean sidereal time - m_observer.gmst = 6.697375 + 0.0657098242 * daynumber + m_observer.utime; + // Greenwich mean sidereal time (hours) + m_observer.gmst = clamp_circular( 6.697375 + 0.0657098242 * daynumber + m_observer.utime, 24.0 ); - m_observer.gmst -= 24.0 * (int)( m_observer.gmst / 24.0 ); - if( m_observer.gmst < 0.0 ) m_observer.gmst += 24.0; - - // local mean sidereal time - m_observer.lmst = m_observer.gmst * 15.0 + m_observer.longitude; - - m_observer.lmst -= 360.0 * (int)( m_observer.lmst / 360.0 ); - if( m_observer.lmst < 0.0 ) m_observer.lmst += 360.0; + // local mean sidereal time (deg) + m_observer.lmst = clamp_circular( m_observer.gmst * 15.0 + m_observer.longitude ); // hour angle m_body.hrang = m_observer.lmst - m_body.rascen; diff --git a/translation.cpp b/translation.cpp index cc89d803..2c6e08ab 100644 --- a/translation.cpp +++ b/translation.cpp @@ -38,8 +38,26 @@ init() { " Loading/unloading in progress (%d s left)", " Another vehicle ahead (distance: %.1f m)", + "Scenario", + "Assignment", + "Current task:", + "Wait for orders", + "Start the engine", + "Shut down the engine", + "Change direction", + "Couple to consist ahead", + "Uncouple %s", + "the engine", + "the engine plus the next vehicle", + "the engine plus %d next vehicles", + "the engine plus %d next vehicles", + "Shunt according to signals", + "Loose shunt according to signals", + "Drive according to signals and timetable", + "Bank consist ahead", + + "%-*.*s Time: %d:%02d:%02d", // HACK: some manual padding to account for longer 'time' equivalent in polish version "Timetable", - "Time: %d:%02d:%02d", "(no timetable)", "Consist weight: %d t (specified) %d t (actual)\nConsist length: %d m", @@ -93,9 +111,11 @@ init() { "Vehicle parameters", + "master controller", "master controller", "second controller", "shunt mode power", + "tempomat", "reverser", "train brake", "independent brake", @@ -116,6 +136,7 @@ init() { "motor blowers A", "motor blowers B", "all motor blowers", + "cooling fans", "line breaker", "line breaker", "alerter", @@ -140,6 +161,7 @@ init() { "right door (close)", "all doors (open)", "all doors (close)", + "doorstep", "departure signal", "upper headlight", "left headlight", @@ -212,8 +234,26 @@ init() { u8" Wsiadanie/wysiadanie pasażerów (do zakończenia %d s)", u8" Inny pojazd na drodze (odległość: %.1f m)", + u8"Scenariusz", + u8"Zlecenie", + u8"Bieżace zadanie:", + u8"Oczekiwać dalszych poleceń", + u8"Przygotować pojazd do jazdy", + u8"Wyłączyc pojazd", + u8"Zmienić kierunek jazdy", + u8"Sprząc się ze składem z przodu", + u8"Odpiąć %s", + u8"pojazd prowadzący", + u8"pojazd prowadzący plus kolejny", + u8"pojazd prowadzący plus %d kolejne", + u8"pojazd prowadzący plus %d kolejnych", + u8"Prowadzić manewry według sygnałów", + u8"Prowadzić manewry przetaczania odrzutem", + u8"Prowadzić skład według sygnałów i rozkładu", + u8"Popychać skład z przodu", + + u8"%-*.*s Godzina: %d:%02d:%02d", u8"Rozkład jazdy", - u8"Godzina: %d:%02d:%02d", u8"(brak rozkładu)", u8"Brutto: %d t (rozkładowe) %d t (rzeczywiste)\nDługość pociągu: %d m", @@ -267,9 +307,11 @@ init() { u8"Parametry pojazdu", + u8"nastawnik jazdy", u8"nastawnik jazdy", u8"nastawnik dodatkowy", u8"sterowanie analogowe", + u8"tempomat", u8"nastawnik kierunku", u8"hamulec zespolony", u8"hamulec pomocniczy", @@ -290,6 +332,7 @@ init() { u8"wentylatory silników trakcyjnych A", u8"wentylatory silników trakcyjnych B", u8"wszystkie wentylatory silników trakcyjnych", + u8"wentylatory oporów rozruchowych", u8"wyłącznik szybki", u8"wyłącznik szybki", u8"czuwak", @@ -314,6 +357,7 @@ init() { u8"drzwi prawe (zamknij)", u8"drzwi (otwórz)", u8"drzwi (zamknij)", + u8"stopień drzwi", u8"sygnal odjazdu", u8"reflektor gorny", u8"reflektor lewy", @@ -382,8 +426,10 @@ init() { { std::vector cabcontrols = { "mainctrl:", + "jointctrl:", "scndctrl:", "shuntmodepower:", + "tempomat_sw:", "dirkey:", "brakectrl:", "localbrake:", @@ -404,6 +450,7 @@ init() { "motorblowersfront_sw:", "motorblowersrear_sw:", "motorblowersalloff_sw:", + "coolingfans_sw:", "main_off_bt:", "main_on_bt:", "security_reset_bt:", @@ -428,6 +475,7 @@ init() { "doorrightoff_sw:", "doorallon_sw:", "dooralloff_sw:", + "doorstep_sw:", "departure_signal_bt:", "upperlight_sw:", "leftlight_sw:", diff --git a/translation.h b/translation.h index be91774e..f64d5830 100644 --- a/translation.h +++ b/translation.h @@ -27,8 +27,26 @@ enum string { driver_aid_loadinginprogress, driver_aid_vehicleahead, + driver_scenario_header, + driver_scenario_assignment, + driver_scenario_currenttask, + driver_scenario_waitfororders, + driver_scenario_prepareengine, + driver_scenario_releaseengine, + driver_scenario_changedirection, + driver_scenario_connect, + driver_scenario_disconnect, + driver_scenario_allvehicles, + driver_scenario_onevehicle, + driver_scenario_fewvehicles, + driver_scenario_somevehicles, + driver_scenario_shunt, + driver_scenario_looseshunt, + driver_scenario_obeytrain, + driver_scenario_bank, + driver_timetable_header, - driver_timetable_time, + driver_timetable_name, driver_timetable_notimetable, driver_timetable_consistdata, @@ -83,8 +101,10 @@ enum string { vehicleparams_window, cab_mainctrl, + cab_jointctrl, cab_scndctrl, cab_shuntmodepower, + cab_tempomat, cab_dirkey, cab_brakectrl, cab_localbrake, @@ -105,6 +125,7 @@ enum string { cab_motorblowersfront_sw, cab_motorblowersrear_sw, cab_motorblowersalloff_sw, + cab_coolingfans_sw, cab_main_off_bt, cab_main_on_bt, cab_security_reset_bt, @@ -129,6 +150,7 @@ enum string { cab_doorrightoff_sw, cab_doorallon_sw, cab_dooralloff_sw, + cab_doorstep_sw, cab_departure_signal_bt, cab_upperlight_sw, cab_leftlight_sw, diff --git a/version.h b/version.h index 8f820de2..b845d427 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION_INFO "M7 (GL3 NET) 21.02.2018, based on milek-267c4472a, tmj-bc60b5ac6" +#define VERSION_INFO "M7 (GL3 NET) 16.03.2019, based on tmj-8d67338"