From 1e52bb43baef62af617f9a15f6847ead7b9689ab Mon Sep 17 00:00:00 2001 From: maj00r Date: Tue, 15 Sep 2026 21:59:50 +0200 Subject: [PATCH] Keep a shut down AI out of the engine start hack, stop standing at spent cells A vehicle left alone in a consist was starting its engine on its own: the Wait_for_orders hack in handle_engine() takes a live 24V battery as a human switching the vehicle on, and every vehicle a scenery starts with has one. The guard for that was in master, commented out. With the engine running the driver counted as active again, so a permitting signal went on to hand it Obey_train - measured on td.scn: Prepare_engine 12 ms after the coupler was released, with the signal still at stop. A command cell keeps its text once the command has been delivered, which left the point at zero velocity and hid the rest of the table behind it. The point is now marked as sent and stops braking for it. Also drops the parameter CommandClaimsPrimary() no longer reads, and puts the brake release blocks in determine_consist_state() back at their original indentation - the scope never changed, but the diff read as if it had. --- vehicle/Driver.cpp | 70 ++++++++++++++++++++-------------------------- vehicle/Driver.h | 2 +- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/vehicle/Driver.cpp b/vehicle/Driver.cpp index e3ea6d5f..738aa84d 100644 --- a/vehicle/Driver.cpp +++ b/vehicle/Driver.cpp @@ -274,13 +274,9 @@ void TSpeedPos::CommandCheck() // inna komenda w evencie skanowanym powoduje zatrzymanie i wysłanie tej komendy // nie manewrowa, nie przystanek, nie zatrzymać na SBL // jak nieznana komenda w komórce sygnałowej, to zatrzymujemy - fVelNext = 0.0; -/* - fVelNext = ( - evEvent->is_command() ? 0.0 : // ask for a stop if we have a command for the vehicle - ( iFlags & ( spSemaphor | spShuntSemaphor )) != 0 ? fVelNext : // don't change semafor velocity - -1.0 ); // otherwise don't be a bother -*/ + // a cell keeps its text after the command has been delivered - going on standing in + // front of it would make every command cell a permanent stop point + fVelNext = ( ( iFlags & spCommandSent ) != 0 ? -1.0 : 0.0 ); // TODO: check whether clearing spShuntSemaphor flag doesn't cause problems // potentially it can invalidate shunt semaphor used to transmit timetable or similar command iFlags &= ~(spShuntSemaphor | spPassengerStopPoint | spStopOnSBL); @@ -4443,7 +4439,7 @@ void TController::PutCommand(std::string NewCommand, double NewValue1, double Ne mvOccupied->PutCommand(NewCommand, NewValue1, NewValue2, NewLocation); } -bool TController::CommandClaimsPrimary( std::string const &Command, double const Value ) +bool TController::CommandClaimsPrimary( std::string const &Command ) { // a driving/startup order wakes a secondary crew up to lead, otherwise nobody moves the consist auto const isdrivingorder = [&Command]( std::initializer_list const names ) { return std::ranges::any_of( @@ -4521,7 +4517,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N if( auto const shouldtakeprimary{ AIControllFlag && !primary() - && CommandClaimsPrimary( NewCommand, NewValue1 ) + && CommandClaimsPrimary( NewCommand ) && ActiveEnginePrimary() == nullptr }; shouldtakeprimary ) { ClaimConsistPrimary(); @@ -5099,7 +5095,7 @@ TController::Update( double const Timedelta ) { determine_braking_distance(); determine_proximity_ranges(); - // we scan while dormant too, to see a permission and wake up + // a dormant driver scans too, so a command cell put in front of it lands in the table scan_route( awarenessrange ); scan_obstacles( awarenessrange ); // generic actions @@ -6199,23 +6195,23 @@ TController::determine_consist_state() { Ready = false; } // Ra: odluźnianie przeładowanych lokomotyw, ciągniętych na zimno - prowizorka... - if( bp >= 0.4 ) { // wg UIC określone sztywno na 0.04 - if( AIControllFlag || (Global.AITrainman && mvOccupied->Vel < EU07_AI_NOMOVEMENT && !is_emu() && !is_dmu())) { - if( BrakeCtrlPosition == gbh_RP // jest pozycja jazdy - && false == TestFlag(vehicle->Hamulec->GetBrakeStatus(), b_dmg) // brake isn't broken - && vehicle->PipePress - mvOccupied->Handle->GetRP() > -0.1 // jeśli ciśnienie jak dla jazdy - && vehicle->Hamulec->GetCRP() > vehicle->PipePress + 0.12 ) { // za dużo w zbiorniku - // indywidualne luzowanko - vehicle->BrakeReleaser( 1 ); + if( bp >= 0.4 ) { // wg UIC określone sztywno na 0.04 + if( AIControllFlag || (Global.AITrainman && mvOccupied->Vel < EU07_AI_NOMOVEMENT && !is_emu() && !is_dmu())) { + if( BrakeCtrlPosition == gbh_RP // jest pozycja jazdy + && false == TestFlag(vehicle->Hamulec->GetBrakeStatus(), b_dmg) // brake isn't broken + && vehicle->PipePress - mvOccupied->Handle->GetRP() > -0.1 // jeśli ciśnienie jak dla jazdy + && vehicle->Hamulec->GetCRP() > vehicle->PipePress + 0.12 ) { // za dużo w zbiorniku + // indywidualne luzowanko + vehicle->BrakeReleaser( 1 ); + } } } - } - if (bp < 0.1) { - if ( AIControllFlag || Global.AITrainman ) { - if (false == TestFlag(vehicle->Hamulec->GetBrakeStatus(), b_dmg) // brake isn't broken - && vehicle->Hamulec->GetCRP() < vehicle->PipePress - 0.1 ) { // już nie jest za dużo w zbiorniku - // koniec indywidualnego luzowanka - vehicle->BrakeReleaser( 0 ); + if (bp < 0.1) { + if ( AIControllFlag || Global.AITrainman ) { + if (false == TestFlag(vehicle->Hamulec->GetBrakeStatus(), b_dmg) // brake isn't broken + && vehicle->Hamulec->GetCRP() < vehicle->PipePress - 0.1 ) { // już nie jest za dużo w zbiorniku + // koniec indywidualnego luzowanka + vehicle->BrakeReleaser( 0 ); } } } @@ -7376,21 +7372,14 @@ TController::UpdateDisconnect() { void TController::handle_engine() { + // HACK: activate route scanning if an idling vehicle is activated by a human user. + // AI is left out on purpose - a battery is on in every vehicle the scenery starts with, + // so this would start any shut down AI the moment it ends up alone in a consist if( OrderCurrentGet() == Wait_for_orders - && false == iEngineActive ) { - // scanning is geometric: the driver sees a signal also with the battery off - iDrivigFlags |= moveActive; - if( AIControllFlag - && primary() - && mvOccupied->DirActive == 0 - && SemNextIndex != -1 ) { - // there is a signal ahead and no direction set yet - set it, so the scan runs forwards - if( mvOccupied->CabActive == 0 ) { - mvOccupied->CabActivisationAuto(); - } - DirectionForward( true ); - DirectionChange(); - } + && false == iEngineActive + && false == AIControllFlag + && true == mvOccupied->Power24vIsAvailable ) { + OrderNext( Prepare_engine ); } // basic engine preparation if( OrderCurrentGet() == Prepare_engine ) { @@ -8536,6 +8525,9 @@ TController::check_route_ahead( double const Range ) { eSignNext->StopCommandSent(); // się wykonało już */ // replacement of the above eSignNext->send_command( *this ); + for( auto &point : sSpeedTable ) { + if( point.evEvent == eSignNext ) { point.iFlags |= spCommandSent; } + } } } break; diff --git a/vehicle/Driver.h b/vehicle/Driver.h index b41163c7..0375c0a7 100644 --- a/vehicle/Driver.h +++ b/vehicle/Driver.h @@ -518,7 +518,7 @@ public: private: void RecognizeCommand(); // odczytuje komende przekazana lokomotywie // whether the command wakes a secondary crew up to lead (a driving or startup order) - static bool CommandClaimsPrimary( std::string const &Command, double const Value ); + static bool CommandClaimsPrimary( std::string const &Command ); void JumpToNextOrder( bool const Skipmergedchangedirection = false ); void JumpToFirstOrder(); void OrderPush( TOrders NewOrder );