diff --git a/Classes.h b/Classes.h index ecbfd1b4..8c4209cc 100644 --- a/Classes.h +++ b/Classes.h @@ -40,6 +40,7 @@ class powergridsource_table; class instance_table; class vehicle_table; struct light_array; +struct dictionary_source; namespace scene { struct node_data; diff --git a/Driver.cpp b/Driver.cpp index d4c53a9c..69a4a129 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1373,7 +1373,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( ( mvOccupied->Vel < v ) || ( v == 0.0 ) ) { // if we're going slower than the target velocity and there's enough room for safe stop, speed up - auto const brakingdistance = fBrakeDist * braking_distance_multiplier( v ); + auto const brakingdistance { 1.2 * fBrakeDist * braking_distance_multiplier( v ) }; if( brakingdistance > 0.0 ) { // maintain desired acc while we have enough room to brake safely, when close enough start paying attention // try to make a smooth transition instead of sharp change @@ -1822,8 +1822,7 @@ void TController::Activation() } } // Ra: to przełączanie poniżej jest tu bez sensu - mvOccupied->ActiveCab = - iDirection; // aktywacja kabiny w prowadzonym pojeżdzie (silnikowy może być odwrotnie?) + mvOccupied->ActiveCab = iDirection; // aktywacja kabiny w prowadzonym pojeżdzie (silnikowy może być odwrotnie?) // mvOccupied->CabNo=iDirection; // mvOccupied->ActiveDir=0; //żeby sam ustawił kierunek mvOccupied->CabActivisation(); // uruchomienie kabin w członach @@ -1963,6 +1962,7 @@ void TController::AutoRewident() && ( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) ) { fAccThreshold += 0.10; } + fNominalAccThreshold = fAccThreshold; } if( OrderCurrentGet() & ( Obey_train | Bank ) ) { @@ -2125,6 +2125,11 @@ bool TController::CheckVehicles(TOrders user) && ( ( 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 ); + // enable heating and converter in carriages with can be heated + if( p->MoverParameters->HeatingPower > 0 ) { + p->MoverParameters->HeatingAllow = true; + p->MoverParameters->ConverterSwitch( true, range_t::local ); + } } if (p->asDestination == "none") @@ -2354,8 +2359,11 @@ void TController::SetVelocity(double NewVel, double NewVelNext, TStopReason r) double TController::BrakeAccFactor() const { double Factor = 1.0; - if( ( ActualProximityDist > fMinProximityDist ) - || ( mvOccupied->Vel > VelDesired + fVelPlus ) ) { + + if( ( fAccThreshold != 0.0 ) + && ( AccDesired < 0.0 ) + && ( ( ActualProximityDist > fMinProximityDist ) + || ( mvOccupied->Vel > VelDesired + fVelPlus ) ) ) { Factor += ( fBrakeReaction * ( /*mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition < 0.5 ? 1.5 : 1 ) ) * mvOccupied->Vel / ( std::max( 0.0, ActualProximityDist ) + 1 ) * ( ( AccDesired - AbsAccS_pub ) / fAccThreshold ); } return Factor; @@ -2520,7 +2528,15 @@ bool TController::PrepareEngine() // enable train brake if it's off if( mvOccupied->fBrakeCtrlPos == mvOccupied->Handle->GetPos( bh_NP ) ) { mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_RP ) ); - BrakeLevelSet(gbh_RP); // GBH + } + // sync virtual brake state with the 'real' one + std::unordered_map const brakepositions { + { static_cast( mvOccupied->Handle->GetPos( bh_RP ) ), gbh_RP }, + { static_cast( mvOccupied->Handle->GetPos( bh_NP ) ), gbh_NP }, + { static_cast( mvOccupied->Handle->GetPos( bh_FS ) ), gbh_FS } }; + auto const lookup { brakepositions.find( static_cast( mvOccupied->fBrakeCtrlPos ) ) }; + if( lookup != brakepositions.end() ) { + BrakeLevelSet( lookup->second ); // GBH } } } @@ -2859,7 +2875,7 @@ bool TController::DecBrake() // mvOccupied->BrakeLevelAdd(-1.0); /* if (mvOccupied->BrakeCtrlPosR < 0.74) GBH */ if (BrakeCtrlPosition < 0.74) - /*mvOccupied->*/BrakeLevelSet(0.0); + /*mvOccupied->*/BrakeLevelSet(gbh_RP); } } if( !OK ) { @@ -3065,7 +3081,7 @@ bool TController::IncSpeed() if (!mvControlling->FuseFlag) if (Ready || (iDrivigFlags & movePress) || (mvOccupied->ShuntMode)) //{(BrakePress<=0.01*MaxBrakePress)} { - OK = mvControlling->IncMainCtrl(std::max(1,mvOccupied->MainCtrlPosNo/10)); + OK = IncSpeedEIM(); // cruise control auto const SpeedCntrlVel { ( ( ActualProximityDist > std::max( 50.0, fMaxProximityDist ) ) ? @@ -3167,6 +3183,34 @@ bool TController::DecSpeed(bool force) return OK; }; +bool TController::IncSpeedEIM() { + + bool OK = false; // domyślnie false, aby wyszło z pętli while + switch( mvControlling->EIMCtrlType ) { + case 0: + OK = mvControlling->IncMainCtrl( std::max( 1, mvOccupied->MainCtrlPosNo / 10 ) ); + break; + case 1: + OK = mvControlling->MainCtrlPos < 6; + if( OK ) + mvControlling->MainCtrlPos = 6; +/* + // TBD, TODO: set position based on desired acceleration? + OK = mvControlling->MainCtrlPos < mvControlling->MainCtrlPosNo; + if( OK ) { + mvControlling->MainCtrlPos = clamp( mvControlling->MainCtrlPos + 1, 6, mvControlling->MainCtrlPosNo ); + } +*/ + break; + case 2: + OK = mvControlling->MainCtrlPos < 4; + if( OK ) + mvControlling->MainCtrlPos = 4; + break; + } + 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 @@ -3214,59 +3258,59 @@ void TController::SpeedSet() if (mvControlling->MainCtrlPosNo > 0) { // jeśli ma czym kręcić // TODO: sprawdzanie innego czlonu //if (!FuseFlagCheck()) - if ((AccDesired < fAccGravity - 0.05) || - (mvOccupied->Vel > VelDesired)) // jeśli nie ma przyspieszać - mvControlling->DecMainCtrl(2); // na zero - else if (fActionTime >= 0.0) - { // jak już można coś poruszać, przetok rozłączać od razu - if (iDrivigFlags & moveIncSpeed) - { // jak ma jechać - if (fReady < 0.4) // 0.05*Controlling->MaxBrakePress) - { // jak jest odhamowany - if (mvOccupied->ActiveDir > 0) - mvOccupied->DirectionForward(); //żeby EN57 jechały na drugiej nastawie - { - if (mvControlling->MainCtrlPos && - !mvControlling->StLinFlag) // jak niby jedzie, ale ma rozłączone liniowe - mvControlling->DecMainCtrl(2); // to na zero i czekać na przewalenie kułakowego - else - switch (mvControlling->MainCtrlPos) - { // ruch nastawnika uzależniony jest od aktualnie ustawionej - // pozycji - case 0: - if (mvControlling->MainCtrlActualPos) // jeśli kułakowy nie jest - // wyzerowany - break; // to czekać na wyzerowanie - mvControlling->IncMainCtrl(1); // przetok; bez "break", bo nie - // ma czekania na 1. pozycji - case 1: - if (VelDesired >= 20) - mvControlling->IncMainCtrl(1); // szeregowa - case 2: - if (VelDesired >= 50) - mvControlling->IncMainCtrl(1); // równoległa - case 3: - if (VelDesired >= 80) - mvControlling->IncMainCtrl(1); // bocznik 1 - case 4: - if (VelDesired >= 90) - mvControlling->IncMainCtrl(1); // bocznik 2 - case 5: - if (VelDesired >= 100) - mvControlling->IncMainCtrl(1); // bocznik 3 - } - if (mvControlling->MainCtrlPos) // jak załączył pozycję - { - fActionTime = -5.0; // niech trochę potrzyma - mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania + if( ( iDrivigFlags & moveIncSpeed ) == 0 ) { + // przetok rozłączać od razu (no dependency on fActionTime) + while( ( mvControlling->MainCtrlPos ) + && ( mvControlling->DecMainCtrl( 1 ) ) ) { + ; // na zero + } + if( fActionTime >= 0.0 ) { + fActionTime = -5.0; // niech trochę potrzyma + } + mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania + } + else { + // jak ma jechać + if( fActionTime < 0.0 ) { break; } + if( fReady > 0.4 ) { break; } + + if( mvOccupied->ActiveDir > 0 ) { + mvOccupied->DirectionForward(); //żeby EN57 jechały na drugiej nastawie + } + + if( ( mvControlling->MainCtrlPos > 0 ) + && ( false == mvControlling->StLinFlag ) ) { + // jak niby jedzie, ale ma rozłączone liniowe to na zero i czekać na przewalenie kułakowego + mvControlling->DecMainCtrl( 2 ); + } + else { + // ruch nastawnika uzależniony jest od aktualnie ustawionej pozycji + switch( mvControlling->MainCtrlPos ) { + case 0: + if( mvControlling->MainCtrlActualPos ) { + // jeśli kułakowy nie jest wyzerowany to czekać na wyzerowanie + break; } - } + mvControlling->IncMainCtrl( 1 ); // przetok; bez "break", bo nie ma czekania na 1. pozycji + case 1: + if( VelDesired >= 20 ) + mvControlling->IncMainCtrl( 1 ); // szeregowa + case 2: + if( VelDesired >= 50 ) + mvControlling->IncMainCtrl( 1 ); // równoległa + case 3: + if( VelDesired >= 80 ) + mvControlling->IncMainCtrl( 1 ); // bocznik 1 + case 4: + if( VelDesired >= 90 ) + mvControlling->IncMainCtrl( 1 ); // bocznik 2 + case 5: + if( VelDesired >= 100 ) + mvControlling->IncMainCtrl( 1 ); // bocznik 3 } } - else + if( mvControlling->MainCtrlPos ) // jak załączył pozycję { - while (mvControlling->MainCtrlPos) - mvControlling->DecMainCtrl(1); // na zero fActionTime = -5.0; // niech trochę potrzyma mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania } @@ -3370,7 +3414,7 @@ void TController::SpeedCntrl(double DesiredSpeed) mvControlling->IncScndCtrl(1); mvControlling->RunCommand("SpeedCntrl", DesiredSpeed, mvControlling->CabNo); } - else if (mvControlling->ScndCtrlPosNo > 1) + else if ((mvControlling->ScndCtrlPosNo > 1) && (!mvOccupied->SpeedCtrlTypeTime)) { int DesiredPos = 1 + mvControlling->ScndCtrlPosNo * ((DesiredSpeed - 1.0) / mvControlling->Vmax); while( ( mvControlling->ScndCtrlPos > DesiredPos ) && ( true == mvControlling->DecScndCtrl( 1 ) ) ) { ; } // all work is done in the condition loop @@ -3412,6 +3456,22 @@ void TController::SetTimeControllers() if (mvOccupied->LocalBrakePosA > 0.95) mvOccupied->MainCtrlPos = 1; } } + //4. Check Speed Control System + if (mvOccupied->EngineType == TEngineType::ElectricInductionMotor && mvOccupied->ScndCtrlPosNo > 1 && mvOccupied->SpeedCtrlTypeTime) + { + double SpeedCntrlVel = + (ActualProximityDist > std::max(50.0, fMaxProximityDist)) ? + VelDesired : + min_speed(VelDesired, VelNext); + SpeedCntrlVel = 10 * std::floor(SpeedCntrlVel*0.1); + if (mvOccupied->ScndCtrlPosNo == 4) + { + if (mvOccupied->NewSpeed + 0.1 < SpeedCntrlVel) + mvOccupied->ScndCtrlPos = 3; + if (mvOccupied->NewSpeed - 0.1 > SpeedCntrlVel) + mvOccupied->ScndCtrlPos = 1; + } + } }; void TController::CheckTimeControllers() @@ -3444,6 +3504,14 @@ void TController::CheckTimeControllers() if (mvOccupied->eimic < 0) mvOccupied->MainCtrlPos = 2; } } + //4. Check Speed Control System + if (mvOccupied->EngineType == TEngineType::ElectricInductionMotor && mvOccupied->ScndCtrlPosNo>1 && mvOccupied->SpeedCtrlTypeTime) + { + if (mvOccupied->ScndCtrlPosNo == 4) + { + mvOccupied->ScndCtrlPos = 2; + } + } }; // otwieranie/zamykanie drzwi w składzie albo (tylko AI) EZT @@ -3487,7 +3555,7 @@ void TController::Doors( bool const Open, int const Side ) { } if( AIControllFlag ) { - if( ( true == mvOccupied->Doors.has_autowarning ) + if( ( true == mvOccupied->Doors.has_warning ) && ( false == mvOccupied->DepartureSignal ) && ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) { mvOccupied->signal_departure( true ); // załącenie bzyczka @@ -3846,15 +3914,14 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N 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 + //-3,-y - podłączyć do całego stojącego składu (sprzęgiem y>=1), zmienić kierunek i czekać w trybie pociągowym //-2,-y - podłączyć do całego stojącego składu (sprzęgiem y>=1), zmienić kierunek i czekać //-2, y - podłączyć do całego stojącego składu (sprzęgiem y>=1) i czekać //-1,-y - podłączyć do całego stojącego składu (sprzęgiem y>=1) i jechać w powrotną stronę //-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 + // x,-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) @@ -4141,7 +4208,7 @@ TController::UpdateSituation(double dt) { Need_TryAgain = true; // reset jak przy wywaleniu nadmiarowego } // check door state - auto const switchsides { p->DirectionGet() <= 0 }; + auto const switchsides { p->DirectionGet() != iDirection }; IsAnyDoorOpen[ side::right ] = IsAnyDoorOpen[ side::right ] || ( false == vehicle->Doors.instances[ ( switchsides ? side::left : side::right ) ].is_closed ); @@ -4563,20 +4630,21 @@ TController::UpdateSituation(double dt) { 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 - CheckVehicles(); // sprawdzić światła nowego składu - JumpToNextOrder(); // wykonanie następnej komendy + if( neighbour.vehicle != nullptr ) { + // 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 + CheckVehicles(); // sprawdzić światła nowego składu + JumpToNextOrder(); // wykonanie następnej komendy + } } - } // if (AIControllFlag) //koniec zblokowania, bo była zmienna lokalna } else { @@ -4606,11 +4674,11 @@ TController::UpdateSituation(double dt) { // TODO: test if we can use the distances calculation from obey_train fMinProximityDist = std::min( 5 + iVehicles, 25 ); fMaxProximityDist = std::min( 10 + iVehicles, 50 ); -/* - if( IsHeavyCargoTrain ) { - fMaxProximityDist *= 1.5; + // HACK: modern vehicles might brake slower at low speeds, increase safety margin as crude counter + if( mvControlling->EIMCtrlType > 0 ) { + fMinProximityDist += 5.0; + fMaxProximityDist += 5.0; } -*/ fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania // margines prędkości powodujący załączenie napędu // były problemy z jazdą np. 3km/h podczas ładowania wagonów @@ -5090,7 +5158,11 @@ TController::UpdateSituation(double dt) { 20.0 ) ); // others if( vel > VelDesired + fVelPlus ) { // if going too fast force some prompt braking - AccPreferred = std::min( -0.65, AccPreferred ); + AccPreferred = std::min( + ( ( mvOccupied->CategoryFlag & 2 ) ? + -0.65 : // cars + -0.30 ), // others + AccPreferred ); } } @@ -5602,11 +5674,11 @@ TController::UpdateSituation(double dt) { || ( VelNext > vel - 40.0 ) ) ? fBrake_a0[ 0 ] * 0.8 : -fAccThreshold ) - / braking_distance_multiplier( VelNext ) ) { + / ( 1.2 * braking_distance_multiplier( VelNext ) ) ) { AccDesired = std::max( -0.06, AccDesired ); } } - else { + if( AccDesired < -0.1 ) { // i orientuj się szybciej, jeśli hamujesz ReactionTime = 0.25; } @@ -5705,11 +5777,11 @@ TController::UpdateSituation(double dt) { } } } - // yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej - // zwiekszanie predkosci + // yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej zwiekszanie predkosci // Ra 2F1H: jest konflikt histerezy pomiędzy nastawioną pozycją a uzyskiwanym // przyspieszeniem - utrzymanie pozycji powoduje przekroczenie przyspieszenia - if( ( AccDesired - AbsAccS > 0.01 ) ) { + if( ( AccDesired > -0.06 ) // don't add power if not asked for actual speed-up + && ( AccDesired - AbsAccS > 0.05 ) ) { // jeśli przyspieszenie pojazdu jest mniejsze niż żądane oraz... if( vel < ( VelDesired == 1.0 ? // work around for trains getting stuck on tracks with speed limit = 1 @@ -5762,6 +5834,7 @@ TController::UpdateSituation(double dt) { // 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 + // TBD: check if the condition isn't redundant with the DecBrake() code if( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition >= 0 ) { DecBrake(); // tutaj zmniejszało o 1 przy odczepianiu } @@ -5800,7 +5873,8 @@ TController::UpdateSituation(double dt) { } } } - if ((AccDesired < fAccGravity - 0.05) && (AbsAccS < AccDesired - fBrake_a1[0]*0.51)) { + if ( ( AccDesired < fAccGravity - 0.05 ) + && ( ( AccDesired - fBrake_a1[0]*0.51 ) ) - AbsAccS > 0.05 ) { // jak hamuje, to nie tykaj kranu za często // yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2 if( OrderCurrentGet() != Disconnect ) { @@ -6526,6 +6600,9 @@ void TController::TakeControl(bool yes) void TController::DirectionForward(bool forward) { // ustawienie jazdy do przodu dla true i do tyłu dla false (zależy od kabiny) ZeroSpeed( true ); // TODO: check if force switch is needed anymore here + // HACK: make sure the master controller isn't set in position which prevents direction change + mvControlling->MainCtrlPos = std::min( mvControlling->MainCtrlPos, mvControlling->MaxMainCtrlPosNoDirChange ); + if( forward ) { // do przodu w obecnej kabinie while( ( mvOccupied->ActiveDir <= 0 ) diff --git a/Driver.h b/Driver.h index 1c4034d1..d75de7d1 100644 --- a/Driver.h +++ b/Driver.h @@ -407,9 +407,9 @@ private: // methods public: std::string TrainName() const; + Mtable::TTrainParameters const * TrainTimetable() const; private: std::string Relation() const; - Mtable::TTrainParameters const * TrainTimetable() const; int StationIndex() const; int StationCount() const; bool IsStop() const; diff --git a/DynObj.cpp b/DynObj.cpp index 2511b854..51cfd89c 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -162,6 +162,39 @@ void TAnim::Parovoz(){ // animowanie tłoka i rozrządu parowozu }; */ + + +void TDynamicObject::destination_data::deserialize( cParser &Input ) { + + while( true == deserialize_mapping( Input ) ) { + ; // all work done by while() + } +} + +bool TDynamicObject::destination_data::deserialize_mapping( cParser &Input ) { + // token can be a key or block end + auto const key { Input.getToken( true, "\n\r\t ,;[]" ) }; + + if( ( true == key.empty() ) || ( key == "}" ) ) { return false; } + + if( key == "{" ) { + script = Input.getToken(); + } + else if( key == "update:" ) { + auto const value { Input.getToken() }; + // TODO: implement + } + else if( key == "instance:" ) { + instancing = Input.getToken(); + } + else if( key == "parameters:" ) { + parameters = Input.getToken(); + } + + return true; +} + + //--------------------------------------------------------------------------- TDynamicObject * TDynamicObject::FirstFind(int &coupler_nr, int cf) { // szukanie skrajnego połączonego pojazdu w pociagu @@ -921,6 +954,8 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) btEndSignalsTab2.Turn( true ); btnOn = true; } + // destination signs + update_destinations(); // else btEndSignalsTab2.TurnOff(); // McZapkie-181002: krecenie wahaczem (korzysta z kata obrotu silnika) if (iAnimType[ANIM_LEVERS]) @@ -1947,6 +1982,10 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" init_sections( mdLowPolyInt, nameprefix ); } } + // destination sign + if( mdModel ) { + init_destination( mdModel ); + } // 'external_load' is an optional special section in the main model, pointing to submodel of external load if( mdModel ) { init_sections( mdModel, "external_load" ); @@ -2070,6 +2109,16 @@ TDynamicObject::init_sections( TModel3d const *Model, std::string const &Namepre return sectioncount; } +bool +TDynamicObject::init_destination( TModel3d *Model ) { + + if( Model->GetSMRoot() == nullptr ) { return false; } + + std::tie( DestinationSign.sign, DestinationSign.has_light ) = Model->GetSMRoot()->find_replacable4(); + + return DestinationSign.sign != nullptr; +} + void TDynamicObject::create_controller( std::string const Type, bool const Trainset ) { @@ -2554,6 +2603,27 @@ na sprzęgach, opóźnienie działania hamulca itp. Oczywiście musi mieć to pe histerezę czasową, aby te tryby pracy nie przełączały się zbyt szybko. */ +void TDynamicObject::update_destinations() { + + if( DestinationSign.sign == nullptr ) { return; } + + DestinationSign.sign->fLight = ( + ( ( DestinationSign.has_light ) && ( MoverParameters->Battery ) ) ? + 2.0 : + -1.0 ); + + // jak są 4 tekstury wymienne, to nie zmieniać rozkładem + if( std::abs( m_materialdata.multi_textures ) >= 4 ) { return; } + // TODO: dedicated setting to discern electronic signs, instead of fallback on light presence + m_materialdata.replacable_skins[ 4 ] = ( + ( ( DestinationSign.destination != null_handle ) + && ( ( false == DestinationSign.has_light ) // physical destination signs remain up until manually changed + || ( ( true == MoverParameters->Battery ) // lcd signs are off without power + && ( ctOwner != nullptr ) ) ) ) ? // lcd signs are off for carriages without engine, potentially left on a siding + DestinationSign.destination : + DestinationSign.destination_off ); +} + bool TDynamicObject::Update(double dt, double dt1) { if (dt1 == 0) @@ -2714,6 +2784,7 @@ bool TDynamicObject::Update(double dt, double dt1) MoverParameters->eimic_real = eimic; MoverParameters->SendCtrlToNext("EIMIC", Max0R(0, eimic), MoverParameters->CabNo); auto LBR = Max0R(-eimic, 0); + auto eim_lb = (Mechanik->AIControllFlag || !MoverParameters->LocHandleTimeTraxx ? 0 : MoverParameters->eim_localbrake); // 1. ustal wymagana sile hamowania calego pociagu // - opoznienie moze byc ustalane na podstawie charakterystyki @@ -2944,6 +3015,11 @@ bool TDynamicObject::Update(double dt, double dt1) p->MoverParameters->LocalBrakePosAEIM = p->MoverParameters->LocalBrakePosAEIM; else p->MoverParameters->LocalBrakePosAEIM = 0; + if (p->MoverParameters->LocHandleTimeTraxx) + { + p->MoverParameters->eim_localbrake = eim_lb; + p->MoverParameters->LocalBrakePosAEIM = std::max(p->MoverParameters->LocalBrakePosAEIM, eim_lb); + } ++i; } @@ -4012,6 +4088,10 @@ void TDynamicObject::RenderSounds() { } if( volume > 0.05 ) { rscurve + .pitch( + true == rscurve.is_combined() ? + MoverParameters->Vel * 0.01f : + rscurve.m_frequencyoffset + rscurve.m_frequencyfactor * 1.f ) .gain( 2.5 * volume ) .play( sound_flags::exclusive | sound_flags::looping ); } @@ -4237,7 +4317,8 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } // potentially set blank destination texture - DestinationSet( {}, {} ); + DestinationSign.destination_off = DestinationFind( "nowhere" ); +// DestinationSet( {}, {} ); if( GfxRenderer.Material( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) { // tekstura -1 z kanałem alfa - nie renderować w cyklu nieprzezroczystych @@ -5534,6 +5615,15 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co parser >> JointCabs; } + else if( token == "pydestinationsign:" ) { + DestinationSign.deserialize( parser ); + // supply vehicle folder as script path if none is provided + if( ( false == DestinationSign.script.empty() ) + && ( substr_path( DestinationSign.script ).empty() ) ) { + DestinationSign.script = asBaseDir + DestinationSign.script; + } + } + } while( token != "" ); } // internaldata: @@ -6091,45 +6181,92 @@ int TDynamicObject::RouteWish(TTrack *tr) return Mechanik ? Mechanik->CrossRoute(tr) : 0; // wg AI albo prosto }; -void TDynamicObject::DestinationSet(std::string to, std::string numer) -{ // ustawienie stacji docelowej oraz wymiennej tekstury 4, jeśli istnieje plik +void TDynamicObject::DestinationSet(std::string to, std::string numer) { + // ustawienie stacji docelowej oraz wymiennej tekstury 4, jeśli istnieje plik // w zasadzie, to każdy wagon mógłby mieć inną stację docelową // zwłaszcza w towarowych, pod kątem zautomatyzowania maewrów albo pracy górki // ale to jeszcze potrwa, zanim będzie możliwe, na razie można wpisać stację z // rozkładu - if( std::abs( m_materialdata.multi_textures ) >= 4 ) { - // jak są 4 tekstury wymienne, to nie zmieniać rozkładem - return; - } - numer = Bezogonkow(numer); + asDestination = to; - to = Bezogonkow(to); // do szukania pliku obcinamy ogonki - if( true == to.empty() ) { - to = "nowhere"; + + if( std::abs( m_materialdata.multi_textures ) >= 4 ) { return; } // jak są 4 tekstury wymienne, to nie zmieniać rozkładem + if( DestinationSign.sign == nullptr ) { return; } // no sign submodel, no problem + + // now see if we can find any version of the destination texture + std::vector const destinations = { + numer, // try dedicated timetable sign first... + to }; // ...then generic destination sign + + for( auto const &destination : destinations ) { + + DestinationSign.destination = DestinationFind( destination ); + if( DestinationSign.destination != null_handle ) { + // got what we wanted, we're done here + return; + } } + // if we didn't get static texture we might be able to make one + if( DestinationSign.script.empty() ) { return; } // no script so no way to make the texture + if( numer == "none" ) { return; } // blank or incomplete/malformed timetable, don't bother + + std::string signrequest { + "make:" + + DestinationSign.script + "?" + // timetable include + + "$timetable=" + ( + ctOwner == nullptr ? + MoverParameters->Name : // leading vehicle, can point to it directly + ctOwner->Vehicle()->MoverParameters->Name ) + "&" // owned vehicle, safer to point to owner as carriages can have identical names + // basic instancing string + // NOTE: underscore doesn't have any magic meaning for the time being, it's just less likely to conflict with regular dictionary keys + + "_id1=" + ( + ctOwner != nullptr ? ctOwner->TrainName() : + Mechanik != nullptr ? Mechanik->TrainName() : + "none" ) }; // shouldn't get here but, eh + // TBD, TODO: replace instancing with support for variables in extra parameters string? + if( false == DestinationSign.instancing.empty() ) { + signrequest += + "&_id2=" + ( + DestinationSign.instancing == "name" ? MoverParameters->Name : + DestinationSign.instancing == "type" ? MoverParameters->TypeName : + "none" ); + } + // optionl extra parameters + if( false == DestinationSign.parameters.empty() ) { + signrequest += "&" + DestinationSign.parameters; + } + + DestinationSign.destination = GfxRenderer.Fetch_Material( signrequest ); +} + +material_handle TDynamicObject::DestinationFind( std::string Destination ) { + + if( Destination.empty() ) { return null_handle; } + + Destination = Bezogonkow( Destination ); // do szukania pliku obcinamy ogonki // destination textures are kept in the vehicle's directory so we point the current texture path there auto const currenttexturepath { Global.asCurrentTexturePath }; Global.asCurrentTexturePath = asBaseDir; // now see if we can find any version of the texture - std::vector destinations = { - numer + '@' + MoverParameters->TypeName, - numer, - to + '@' + MoverParameters->TypeName, - to, - "nowhere" + '@' + MoverParameters->TypeName, - "nowhere" }; + std::vector const destinations { + Destination + '@' + MoverParameters->TypeName, + Destination }; + + auto destinationhandle { null_handle }; for( auto const &destination : destinations ) { - auto material = TextureTest( ToLower( destination ) ); if( false == material.empty() ) { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Material( material ); + destinationhandle = GfxRenderer.Fetch_Material( material ); break; } } // whether we got anything, restore previous texture path Global.asCurrentTexturePath = currenttexturepath; -}; + + return destinationhandle; +} void TDynamicObject::OverheadTrack(float o) { // ewentualne wymuszanie jazdy @@ -6814,6 +6951,9 @@ vehicle_table::update_traction( TDynamicObject *Vehicle ) { if( pantograph->hvPowerWire != nullptr ) { // jeżeli znamy drut z poprzedniego przebiegu for( int attempts = 0; attempts < 30; ++attempts ) { + // sanity check. shouldn't happen in theory, but did happen in practice + if( pantograph->hvPowerWire == nullptr ) { break; } + // powtarzane aż do znalezienia odpowiedniego odcinka na liście dwukierunkowej if( pantograph->hvPowerWire->iLast & 0x3 ) { // dla ostatniego i przedostatniego przęsła wymuszamy szukanie innego @@ -6829,6 +6969,7 @@ vehicle_table::update_traction( TDynamicObject *Vehicle ) { } // obliczamy wyraz wolny równania płaszczyzny (to miejsce nie jest odpowienie) // podstawiamy równanie parametryczne drutu do równania płaszczyzny pantografu + // TODO: investigate this routine with reardriver/negative speed, does it picks the right wire? auto const fRaParam = -( glm::dot( pantograph->hvPowerWire->pPoint1, vFront ) - glm::dot( pant0, vFront ) ) / glm::dot( pantograph->hvPowerWire->vParametric, vFront ); diff --git a/DynObj.h b/DynObj.h index 5619cd46..d5e7e6c1 100644 --- a/DynObj.h +++ b/DynObj.h @@ -201,6 +201,20 @@ public: TModel3d *mdLowPolyInt; // ABu 010305: wnetrze lowpoly std::array LowPolyIntCabs {}; // pointers to low fidelity version of individual driver cabs bool JointCabs{ false }; // flag for vehicles with multiple virtual 'cabs' sharing location and 3d model(s) + struct destination_data { + TSubModel *sign { nullptr }; // submodel mapped with replacable texture -4 + bool has_light { false }; // the submodel was originally configured with self-illumination attribute + material_handle destination { null_handle }; // most recently assigned non-blank destination texture + material_handle destination_off { null_handle }; // blank destination sign + std::string script; // potential python script used to generate texture data + int update_rate { 0 }; // -1: per stop, 0: none, >0: fps // TBD, TODO: implement? + std::string instancing; // potential method to generate more than one texture per timetable + std::string parameters; // potential extra parameters supplied by mmd file + // methods + void deserialize( cParser &Input ); + private: + bool deserialize_mapping( cParser &Input ); + } DestinationSign; float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?) float LoadOffset { 0.f }; std::unordered_map LoadModelOverrides; // potential overrides of default load visualization models @@ -518,6 +532,7 @@ private: TTrack *Track, double fDist, std::string DriverType, double fVel, std::string TrainName, float Load, std::string LoadType, bool Reversed, std::string); int init_sections( TModel3d const *Model, std::string const &Nameprefix ); + bool init_destination( TModel3d *Model ); void create_controller( std::string const Type, bool const Trainset ); void AttachPrev(TDynamicObject *Object, int iType = 1); bool UpdateForce(double dt); @@ -532,6 +547,7 @@ private: void update_load_visibility(); void update_load_offset(); void shuffle_load_sections(); + void update_destinations(); bool Update(double dt, double dt1); bool FastUpdate(double dt); void Move(double fDistance); @@ -634,6 +650,7 @@ private: // zapytanie do AI, po którym segmencie skrzyżowania jechać int RouteWish(TTrack *tr); void DestinationSet(std::string to, std::string numer); + material_handle DestinationFind( std::string Destination ); void OverheadTrack(float o); double MED[9][8]; // lista zmiennych do debugowania hamulca ED diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 9e592985..16593aa6 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -892,6 +892,7 @@ public: TBrakeValve BrakeValve = TBrakeValve::NoValve; TBrakeHandle BrakeHandle = TBrakeHandle::NoHandle; TBrakeHandle BrakeLocHandle = TBrakeHandle::NoHandle; + bool LocHandleTimeTraxx = false; /*hamulec dodatkowy typu traxx*/ double MBPM = 1.0; /*masa najwiekszego cisnienia*/ std::shared_ptr Hamulec; @@ -1131,6 +1132,7 @@ public: double AccN = 0.0; // przyspieszenie normalne w [m/s^2] double AccVert = 0.0; // vertical acceleration double nrot = 0.0; + double nrot_eps = 0.0; //przyspieszenie kątowe kół (bez kierunku) double WheelFlat = 0.0; bool TruckHunting { true }; // enable/disable truck hunting calculation /*! rotacja kol [obr/s]*/ @@ -1306,6 +1308,7 @@ public: /*- 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*/ + double eim_localbrake = 0; /*nastawa hamowania dodatkowego pneumatycznego lokomotywy*/ int EIMCtrlType = 0; /*rodzaj wariantu zadajnika jazdy*/ bool SpeedCtrlTypeTime = false; /*czy tempomat sterowany czasowo*/ double eimv_pr = 0; /*realizowany procent dostepnej sily rozruchu/hamowania*/ @@ -1352,6 +1355,7 @@ public: std::string StLinSwitchType; bool Heating = false; //ogrzewanie 'Winger 020304 + bool HeatingAllow { false }; // heating switch // TODO: wrap heating in a basic device int DoubleTr = 1; //trakcja ukrotniona - przedni pojazd 'Winger 160304 bool PhysicActivation = true; @@ -1498,6 +1502,7 @@ public: /*-funkcje typowe dla lokomotywy elektrycznej*/ void ConverterCheck( double const Timestep ); // przetwornica + void HeatingCheck( double const Timestep ); void WaterPumpCheck( double const Timestep ); void WaterHeaterCheck( double const Timestep ); void FuelPumpCheck( double const Timestep ); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 78d6b51a..c2efd701 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -502,7 +502,7 @@ bool TMoverParameters::Dettach(int ConnectNo) bool TMoverParameters::DirectionForward() { - if ((MainCtrlPosNo > 0) && (ActiveDir < 1) && (MainCtrlPos <= MaxMainCtrlPosNoDirChange) && (EIMDirectionChangeAllow())) + if ((MainCtrlPosNo > 0) && (ActiveDir < 1) && (EIMDirectionChangeAllow())) { ++ActiveDir; DirAbsolute = ActiveDir * CabNo; @@ -1075,11 +1075,13 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap for( int side = 0; side < 2; ++side ) { // przekazywanie napiec - auto const oppositeside = ( side == end::front ? end::rear : end::front ); + auto const oppositeside { ( side == end::front ? end::rear : end::front ) }; + auto const liveconnection{ + ( Couplers[ side ].CouplingFlag & ctrain_power ) + || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) + && ( Couplers[ side ].Connected->Heating ) ) }; - if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { + if( liveconnection ) { auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ Couplers[ side ].ConnectedNr ]; Couplers[ oppositeside ].power_high.voltage = std::max( @@ -1103,8 +1105,8 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap Couplers[ side ].power_high.local = false; // power, if any, will be from external source if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { + || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) + && ( Couplers[ side ].Connected->Heating ) ) ) { auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ ( Couplers[ side ].ConnectedNr == end::front ? @@ -1127,8 +1129,8 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap Couplers[ side ].power_high.local = true; // power is coming from local pantographs if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { + || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) + && ( Couplers[ side ].Connected->Heating ) ) ) { auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ ( Couplers[ side ].ConnectedNr == end::front ? @@ -1409,6 +1411,8 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { // w rozrządczym nie (jest błąd w FIZ!) - Ra 2014-07: teraz we wszystkich UpdatePantVolume( Deltatime ); // Ra 2014-07: obsługa zbiornika rozrządu oraz pantografów } + // heating + HeatingCheck( Deltatime ); UpdateBrakePressure(Deltatime); UpdatePipePressure(Deltatime); @@ -1462,7 +1466,8 @@ void TMoverParameters::ConverterCheck( double const Timestep ) { if( ( ConverterAllow ) && ( ConverterAllowLocal ) && ( false == PantPressLockActive ) - && ( Mains ) ) { + && ( ( Mains ) + || ( GetTrainsetVoltage() > 0 ) ) ) { // delay timer can be optionally configured, and is set anew whenever converter goes off if( ConverterStartDelayTimer <= 0.0 ) { ConverterFlag = true; @@ -1477,6 +1482,17 @@ void TMoverParameters::ConverterCheck( double const Timestep ) { } }; +// heating system status check +void TMoverParameters::HeatingCheck( double const Timestep ) { + + Heating = ( + ( true == HeatingAllow ) + // powered vehicles are generally required to activate their power source to provide heating + // passive vehicles get a pass in this regard + && ( ( Power < 0.1 ) + || ( true == Mains ) ) ); +} + // water pump status check void TMoverParameters::WaterPumpCheck( double const Timestep ) { // NOTE: breaker override with start type is sm42 specific hack, replace with ability to define the presence of the breaker @@ -2355,7 +2371,7 @@ bool TMoverParameters::DirectionBackward(void) DB = true; // return DB; // exit; TODO: czy dobrze przetlumaczone? } - if ((MainCtrlPosNo > 0) && (ActiveDir > -1) && (MainCtrlPos <= MaxMainCtrlPosNoDirChange) && (EIMDirectionChangeAllow())) + if ((MainCtrlPosNo > 0) && (ActiveDir > -1) && (EIMDirectionChangeAllow())) { if (EngineType == TEngineType::WheelsDriven) CabNo--; @@ -2376,7 +2392,11 @@ bool TMoverParameters::DirectionBackward(void) bool TMoverParameters::EIMDirectionChangeAllow(void) { bool OK = false; +/* + // NOTE: disabled while eimic variables aren't immediately synced with master controller changes inside ai module OK = (EngineType != TEngineType::ElectricInductionMotor || ((eimic <= 0) && (eimic_real <= 0) && (Vel < 0.1))); +*/ + OK = ( MainCtrlPos <= MaxMainCtrlPosNoDirChange ); return OK; } @@ -3714,7 +3734,7 @@ void TMoverParameters::UpdatePipePressure(double dt) Pipe->Flow( temp * Hamulec->GetPF( temp * PipePress, dt, Vel ) + GetDVc( dt ) ); if (ASBType == 128) - Hamulec->ASB(int(SlippingWheels)); + Hamulec->ASB(int(SlippingWheels && (Vel>1))*(1+2*int(nrot_eps<-0.01))); dpPipe = 0; @@ -3962,6 +3982,7 @@ void TMoverParameters::ComputeTotalForce(double dt) { // juz zoptymalizowane: FStand = FrictionForce(RunningShape.R, RunningTrack.DamageFlag); // siła oporów ruchu + double old_nrot = abs(nrot); nrot = v2n(); // przeliczenie prędkości liniowej na obrotową if( ( true == TestFlag( BrakeMethod, bp_MHS ) ) @@ -4034,8 +4055,9 @@ void TMoverParameters::ComputeTotalForce(double dt) { } else { - Fb = -Fwheels*Sign(V); - FTrain = 0; + double factor = (FTrain - Fb * Sign(V) != 0 ? Fwheels/(FTrain - Fb * Sign(V)) : 1.0); + Fb *= factor; + FTrain *= factor; } if (nrot < 0.1) { @@ -4044,6 +4066,7 @@ void TMoverParameters::ComputeTotalForce(double dt) { nrot = temp_nrot; } + nrot_eps = (abs(nrot) - (old_nrot))/dt; // doliczenie sił z innych pojazdów for( int end = end::front; end <= end::rear; ++end ) { if( Neighbours[ end ].vehicle != nullptr ) { @@ -4336,6 +4359,7 @@ double TMoverParameters::CouplerForce( int const End, double dt ) { // zderzenie coupler.CheckCollision = true; if( ( coupler.CouplerType == TCouplerType::Automatic ) + && ( coupler.CouplerType == othercoupler.CouplerType ) && ( coupler.CouplingFlag == coupling::faux ) ) { // sprzeganie wagonow z samoczynnymi sprzegami // EN57 @@ -6018,6 +6042,26 @@ void TMoverParameters::CheckEIMIC(double dt) } if (MainCtrlPos >= 3 && eimic < 0) eimic = 0; if (MainCtrlPos <= 3 && eimic > 0) eimic = 0; + if (LocHandleTimeTraxx) + { + if (LocalBrakeRatio() < 0.05) //pozycja 0 + { + eim_localbrake -= dt*0.17; //zmniejszanie + } + + if (LocalBrakeRatio() > 0.15) //pozycja 2 + { + eim_localbrake += dt*0.17; //wzrastanie + eim_localbrake = std::max(eim_localbrake, BrakePress / MaxBrakePress[0]); + } + else + { + if (eim_localbrake < Hamulec->GetEDBCP() / MaxBrakePress[0]) + eim_localbrake = 0; + } + eim_localbrake = clamp(eim_localbrake, 0.0, 1.0); + if (eim_localbrake > 0.04 && eimic > 0) eimic = 0; + } break; case 2: switch (MainCtrlPos) @@ -7261,14 +7305,14 @@ double TMoverParameters::GetTrainsetVoltage(void) return std::max( ( ( ( Couplers[end::front].Connected ) && ( ( Couplers[ end::front ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ end::front ].CouplingFlag & ctrain_heating ) ) ) ) ? + || ( ( Couplers[ end::front ].CouplingFlag & ctrain_heating ) + && ( Couplers[ end::front ].Connected->Heating ) ) ) ) ? Couplers[end::front].Connected->Couplers[ Couplers[end::front].ConnectedNr ].power_high.voltage : 0.0 ), ( ( ( Couplers[end::rear].Connected ) && ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ end::rear ].CouplingFlag & ctrain_heating ) ) ) ) ? + || ( ( Couplers[ end::rear ].CouplingFlag & ctrain_heating ) + && ( Couplers[ end::rear ].Connected->Heating ) ) ) ) ? Couplers[ end::rear ].Connected->Couplers[ Couplers[ end::rear ].ConnectedNr ].power_high.voltage : 0.0 ) ); } @@ -8638,6 +8682,7 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { extract_value( CoupledCtrl, "CoupledCtrl", line, "" ); extract_value( EIMCtrlType, "EIMCtrlType", line, "" ); clamp( EIMCtrlType, 0, 3 ); + LocHandleTimeTraxx = (extract_value("LocalBrakeTraxx", line) == "Yes"); extract_value( ScndS, "ScndS", line, "" ); // brak pozycji rownoleglej przy niskiej nastawie PSR diff --git a/McZapkie/hamulce.cpp b/McZapkie/hamulce.cpp index 6d258f8e..5cc3dd02 100644 --- a/McZapkie/hamulce.cpp +++ b/McZapkie/hamulce.cpp @@ -391,7 +391,8 @@ void TBrake::SetEPS( double const nEPS ) void TBrake::ASB( int const state ) { // 255-b_asb(32) - BrakeStatus = (BrakeStatus & ~b_asb) | ( state * b_asb ); + BrakeStatus = (BrakeStatus & ~b_asb) | ( (state / 2) * b_asb ); + BrakeStatus = (BrakeStatus & ~b_asb_unbrake) | ( (state % 2) * b_asb_unbrake); } int TBrake::GetStatus() @@ -1551,7 +1552,7 @@ double TEStED::GetPF( double const PP, double const dt, double const Vel ) // powtarzacz — podwojny zawor zwrotny temp = Max0R(LoadC * BCP / temp * Min0R(Max0R(1 - EDFlag, 0), 1), LBP); double speed = 1; - if ((ASBP < 0.1) && ((BrakeStatus & b_asb) == b_asb)) + if ((ASBP < 0.1) && ((BrakeStatus & b_asb_unbrake) == b_asb_unbrake)) { temp = 0; speed = 3; @@ -1559,7 +1560,7 @@ double TEStED::GetPF( double const PP, double const dt, double const Vel ) if ((BrakeCyl->P() > temp)) dv = -PFVd(BrakeCyl->P(), 0, 0.02 * SizeBC * speed, temp) * dt; - else if ((BrakeCyl->P() < temp)) + else if ((BrakeCyl->P() < temp) && ((BrakeStatus & b_asb) == 0)) dv = PFVa(BVP, BrakeCyl->P(), 0.02 * SizeBC, temp) * dt; else dv = 0; diff --git a/McZapkie/hamulce.h b/McZapkie/hamulce.h index d09427c5..6ae70eca 100644 --- a/McZapkie/hamulce.h +++ b/McZapkie/hamulce.h @@ -53,7 +53,8 @@ static int const b_on = 2; //napelnianie static int const b_rfl = 4; //uzupelnianie static int const b_rls = 8; //odluzniacz static int const b_ep = 16; //elektropneumatyczny -static int const b_asb = 32; //elektropneumatyczny +static int const b_asb = 32; //przeciwposlizg-wstrzymanie +static int const b_asb_unbrake = 64; //przeciwposlizg-luzowanie static int const b_dmg = 128; //wylaczony z dzialania /*uszkodzenia hamulca*/ diff --git a/Model3d.cpp b/Model3d.cpp index ef68cb1b..9f635e47 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -791,6 +791,27 @@ int TSubModel::count_children() { 1 + Child->count_siblings() ); } +// locates submodel mapped with replacable -4 +std::tuple +TSubModel::find_replacable4() { + + if( m_material == -4 ) { + return std::make_tuple( this, ( fLight != -1.0 ) ); + } + + if( Next != nullptr ) { + auto lookup { Next->find_replacable4() }; + if( std::get( lookup ) != nullptr ) { return lookup; } + } + + if( Child != nullptr ) { + auto lookup { Child->find_replacable4() }; + if( std::get( lookup ) != nullptr ) { return lookup; } + } + + return std::make_tuple( nullptr, false ); +} + uint32_t TSubModel::FlagsCheck() { // analiza koniecznych zmian pomiędzy submodelami // samo pomijanie glBindTexture() nie poprawi wydajności diff --git a/Model3d.h b/Model3d.h index 68fca064..751a9958 100644 --- a/Model3d.h +++ b/Model3d.h @@ -169,6 +169,8 @@ public: TSubModel * ChildGet() { return Child; }; int count_siblings(); int count_children(); + // locates submodel mapped with replacable -4 + std::tuple find_replacable4(); int TriangleAdd(TModel3d *m, material_handle tex, int tri); void SetRotate(float3 vNewRotateAxis, float fNewAngle); void SetRotateXYZ( Math3D::vector3 vNewAngles); diff --git a/PyInt.cpp b/PyInt.cpp index 74d4d6e7..4b7ba2fd 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -10,9 +10,8 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "PyInt.h" -#include "Globals.h" +#include "dictionary.h" #include "application.h" -#include "renderer.h" #include "Logs.h" #ifdef __GNUC__ diff --git a/PyInt.h b/PyInt.h index 5f88d1e0..c40f2c48 100644 --- a/PyInt.h +++ b/PyInt.h @@ -33,24 +33,6 @@ http://mozilla.org/MPL/2.0/. #define PyGetBool(param) param ? Py_True : Py_False #define PyGetString(param) PyString_FromString(param) -// collection of keyword-value pairs -// NOTE: since our python dictionary operates on a few types, most of the class was hardcoded for simplicity -struct dictionary_source { -// types - template - using keyvaluepair_sequence = std::vector>; -// members - keyvaluepair_sequence floats; - keyvaluepair_sequence integers; - keyvaluepair_sequence bools; - keyvaluepair_sequence strings; -// methods - inline void insert( std::string const &Key, double const Value ) { floats.emplace_back( Key, Value ); } - inline void insert( std::string const &Key, int const Value ) { integers.emplace_back( Key, Value ); } - inline void insert( std::string const &Key, bool const Value ) { bools.emplace_back( Key, Value ); } - inline void insert( std::string const &Key, std::string const Value ) { strings.emplace_back( Key, Value ); } -}; - // TODO: extract common base and inherit specialization from it class render_task { diff --git a/Texture.cpp b/Texture.cpp index e5516b1c..3506948c 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -19,14 +19,16 @@ http://mozilla.org/MPL/2.0/. #include "GL/glew.h" #include "application.h" -#include "utilities.h" +#include "dictionary.h" #include "Globals.h" #include "Logs.h" +#include "utilities.h" #include "sn_utils.h" #include "utilities.h" #include "flip-s3tc.h" #include + #define EU07_DEFERRED_TEXTURE_UPLOAD texture_manager::texture_manager() { @@ -146,17 +148,9 @@ void opengl_texture::make_request() { auto const components { Split( name, '?' ) }; - auto const query { Split( components.back(), '&' ) }; - auto *dictionary { new dictionary_source }; - if( dictionary != nullptr ) { - for( auto const &querypair : query ) { - auto const valuepos { querypair.find( '=' ) }; - dictionary->insert( - ToLower( querypair.substr( 0, valuepos ) ), - querypair.substr( valuepos + 1 ) ); - } - } + auto *dictionary { new dictionary_source( components.back() ) }; + if( dictionary == nullptr ) { return; } Application.request( { ToLower( components.front() ), dictionary, id } ); } diff --git a/Train.cpp b/Train.cpp index d9e11077..a38696e4 100644 --- a/Train.cpp +++ b/Train.cpp @@ -30,6 +30,7 @@ http://mozilla.org/MPL/2.0/. #include "Console.h" #include "application.h" #include "renderer.h" +#include "dictionary.h" /* namespace input { @@ -547,31 +548,8 @@ dictionary_source *TTrain::GetTrainState() { dict->insert( "velnext", driver->VelNext ); dict->insert( "actualproximitydist", driver->ActualProximityDist ); // train data - auto const *timetable{ driver->TrainTimetable() }; - - dict->insert( "trainnumber", driver->TrainName() ); - dict->insert( "train_brakingmassratio", timetable->BrakeRatio ); - dict->insert( "train_enginetype", timetable->LocSeries ); - dict->insert( "train_engineload", timetable->LocLoad ); - - dict->insert( "train_stationindex", driver->iStationStart ); - auto const stationcount { driver->StationCount() }; - dict->insert( "train_stationcount", stationcount ); - if( stationcount > 0 ) { - // timetable stations data, if there's any - for( auto stationidx = 1; stationidx <= stationcount; ++stationidx ) { - auto const stationlabel { "train_station" + std::to_string( stationidx ) + "_" }; - auto const &timetableline { timetable->TimeTable[ stationidx ] }; - dict->insert( ( stationlabel + "name" ), Bezogonkow( timetableline.StationName ) ); - dict->insert( ( stationlabel + "fclt" ), Bezogonkow( timetableline.StationWare ) ); - dict->insert( ( stationlabel + "lctn" ), timetableline.km ); - dict->insert( ( stationlabel + "vmax" ), timetableline.vmax ); - dict->insert( ( stationlabel + "ah" ), timetableline.Ah ); - dict->insert( ( stationlabel + "am" ), timetableline.Am ); - dict->insert( ( stationlabel + "dh" ), timetableline.Dh ); - dict->insert( ( stationlabel + "dm" ), timetableline.Dm ); - } - } + driver->TrainTimetable()->serialize( dict ); + dict->insert( "train_stationstart", driver->iStationStart ); dict->insert( "train_atpassengerstop", driver->IsAtPassengerStop ); // world state data dict->insert( "scenario", Global.SceneryFile ); @@ -4152,7 +4130,7 @@ void TTrain::OnCommand_heatingtoggle( TTrain *Train, command_data const &Command if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( false == Train->mvControlled->Heating ) { + if( false == Train->mvControlled->HeatingAllow ) { // turn on OnCommand_heatingenable( Train, Command ); } @@ -4166,24 +4144,20 @@ void TTrain::OnCommand_heatingtoggle( TTrain *Train, command_data const &Command void TTrain::OnCommand_heatingenable( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { + + Train->mvControlled->HeatingAllow = true; // visual feedback Train->ggTrainHeatingButton.UpdateValue( 1.0, Train->dsbSwitch ); - - if( true == Train->mvControlled->Heating ) { return; } // already enabled - - Train->mvControlled->Heating = true; } } void TTrain::OnCommand_heatingdisable( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { + + Train->mvControlled->HeatingAllow = false; // visual feedback Train->ggTrainHeatingButton.UpdateValue( 0.0, Train->dsbSwitch ); - - if( false == Train->mvControlled->Heating ) { return; } // already disabled - - Train->mvControlled->Heating = false; } } @@ -5711,6 +5685,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaMalfunction.Turn( mvControlled->dizel_heat.PA ); btLampkaMotorBlowers.Turn( ( mvControlled->MotorBlowers[ end::front ].is_active ) && ( mvControlled->MotorBlowers[ end::rear ].is_active ) ); btLampkaCoolingFans.Turn( mvControlled->RventRot > 1.0 ); + btLampkaTempomat.Turn( mvControlled->ScndCtrlPos > 0 ); // universal devices state indicators for( auto idx = 0; idx < btUniversals.size(); ++idx ) { btUniversals[ idx ].Turn( ggUniversals[ idx ].GetValue() > 0.5 ); @@ -5772,6 +5747,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaMalfunction.Turn( false ); btLampkaMotorBlowers.Turn( false ); btLampkaCoolingFans.Turn( false ); + btLampkaTempomat.Turn( false ); // universal devices state indicators for( auto &universal : btUniversals ) { universal.Turn( false ); @@ -6011,7 +5987,6 @@ bool TTrain::Update( double const Deltatime ) //--------- // hunter-080812: poprawka na ogrzewanie w elektrykach - usuniete uzaleznienie od przetwornicy if( ( mvControlled->Heating == true ) - && ( mvControlled->Mains == true ) && ( mvControlled->ConvOvldFlag == false ) ) btLampkaOgrzewanieSkladu.Turn( true ); else @@ -7417,6 +7392,7 @@ void TTrain::clear_cab_controls() btLampkaMalfunctionB.Clear(); btLampkaMotorBlowers.Clear(); btLampkaCoolingFans.Clear(); + btLampkaTempomat.Clear(); ggLeftLightButton.Clear(); ggRightLightButton.Clear(); @@ -7762,6 +7738,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-vent_trim:", btLampkaWentZaluzje }, { "i-motorblowers:", btLampkaMotorBlowers }, { "i-coolingfans:", btLampkaCoolingFans }, + { "i-tempomat:", btLampkaTempomat }, { "i-trainheating:", btLampkaOgrzewanieSkladu }, { "i-security_aware:", btLampkaCzuwaka }, { "i-security_cabsignal:", btLampkaSHP }, diff --git a/Train.h b/Train.h index 9e5d4858..8c9bcdf6 100644 --- a/Train.h +++ b/Train.h @@ -584,6 +584,7 @@ public: // reszta może by?publiczna TButton btLampkaMalfunctionB; TButton btLampkaMotorBlowers; TButton btLampkaCoolingFans; + TButton btLampkaTempomat; TButton btCabLight; // hunter-171012: lampa oswietlajaca kabine // Ra 2013-12: wirtualne "lampki" do odbijania na haslerze w PoKeys diff --git a/dictionary.cpp b/dictionary.cpp new file mode 100644 index 00000000..f4e987ec --- /dev/null +++ b/dictionary.cpp @@ -0,0 +1,55 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "dictionary.h" + +#include "simulation.h" +#include "utilities.h" +#include "DynObj.h" +#include "Driver.h" +#include "mtable.h" + +dictionary_source::dictionary_source( std::string const &Input ) { + + auto const keyvaluepairs { Split( Input, '&' ) }; + + for( auto const &keyvaluepair : keyvaluepairs ) { + + auto const valuepos { keyvaluepair.find( '=' ) }; + if( keyvaluepair[ 0 ] != '$' ) { + // regular key, value pairs + insert( + ToLower( keyvaluepair.substr( 0, valuepos ) ), + keyvaluepair.substr( valuepos + 1 ) ); + } + else { + // special case, $key indicates request to include certain dataset clarified by value + auto const key { ToLower( keyvaluepair.substr( 0, valuepos ) ) }; + auto const value { keyvaluepair.substr( valuepos + 1 ) }; + + if( key == "$timetable" ) { + // timetable pulled from (preferably) the owner/direct controller of specified vehicle + auto const *vehicle { simulation::Vehicles.find( value ) }; + auto const *controller { ( + vehicle == nullptr ? nullptr : + vehicle->ctOwner == nullptr ? vehicle->Mechanik : + vehicle->ctOwner ) }; + auto const *timetable { ( + controller != nullptr ? + controller->TrainTimetable() : + nullptr ) }; + + if( timetable != nullptr ) { + timetable->serialize( this ); + } + } + } + } +} diff --git a/dictionary.h b/dictionary.h new file mode 100644 index 00000000..98828856 --- /dev/null +++ b/dictionary.h @@ -0,0 +1,31 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +// collection of keyword-value pairs +// NOTE: since our python dictionary operates on a few types, most of the class was hardcoded for simplicity +struct dictionary_source { +// types + template + using keyvaluepair_sequence = std::vector>; +// members + keyvaluepair_sequence floats; + keyvaluepair_sequence integers; + keyvaluepair_sequence bools; + keyvaluepair_sequence strings; +// constructors + dictionary_source() = default; + dictionary_source( std::string const &Input ); +// methods + inline void insert( std::string const &Key, double const Value ) { floats.emplace_back( Key, Value ); } + inline void insert( std::string const &Key, int const Value ) { integers.emplace_back( Key, Value ); } + inline void insert( std::string const &Key, bool const Value ) { bools.emplace_back( Key, Value ); } + inline void insert( std::string const &Key, std::string const Value ) { strings.emplace_back( Key, Value ); } +}; diff --git a/mtable.cpp b/mtable.cpp index e5bf7331..97f5354c 100644 --- a/mtable.cpp +++ b/mtable.cpp @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "mtable.h" #include "Globals.h" #include "simulationtime.h" +#include "dictionary.h" #include "utilities.h" double TTrainParameters::CheckTrainLatency() @@ -523,3 +524,29 @@ bool TTrainParameters::DirectionChange() return true; return false; } + +void TTrainParameters::serialize( dictionary_source *Output ) const { + + Output->insert( "trainnumber", TrainName ); + Output->insert( "train_brakingmassratio", BrakeRatio ); + Output->insert( "train_enginetype", LocSeries ); + Output->insert( "train_engineload", LocLoad ); + + Output->insert( "train_stationindex", StationIndex ); + Output->insert( "train_stationcount", StationCount ); + if( StationCount > 0 ) { + // timetable stations data, if there's any + for( auto stationidx = 1; stationidx <= StationCount; ++stationidx ) { + auto const stationlabel { "train_station" + std::to_string( stationidx ) + "_" }; + auto const &timetableline { TimeTable[ stationidx ] }; + Output->insert( ( stationlabel + "name" ), Bezogonkow( timetableline.StationName ) ); + Output->insert( ( stationlabel + "fclt" ), Bezogonkow( timetableline.StationWare ) ); + Output->insert( ( stationlabel + "lctn" ), timetableline.km ); + Output->insert( ( stationlabel + "vmax" ), timetableline.vmax ); + Output->insert( ( stationlabel + "ah" ), timetableline.Ah ); + Output->insert( ( stationlabel + "am" ), timetableline.Am ); + Output->insert( ( stationlabel + "dh" ), timetableline.Dh ); + Output->insert( ( stationlabel + "dm" ), timetableline.Dm ); + } + } +} diff --git a/mtable.h b/mtable.h index ea905e4c..78816aaa 100644 --- a/mtable.h +++ b/mtable.h @@ -83,6 +83,7 @@ class TTrainParameters bool LoadTTfile(std::string scnpath, int iPlus, double vmax); bool DirectionChange(); void StationIndexInc(); + void serialize( dictionary_source *Output ) const; }; class TMTableTime diff --git a/version.h b/version.h index 3666a8d4..ffeabe3a 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION_INFO "M7 23.03.2018, based on tmj-ae5daac9" +#define VERSION_INFO "M7 5.04.2019, based on tmj-a081077c"