diff --git a/Driver.cpp b/Driver.cpp index 76df8dd8..e3a97d1d 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -868,53 +868,15 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // jeśli długość peronu ((sSpeedTable[i].evEvent->ValueGet(2)) nie podana, // przyjąć odległość fMinProximityDist { // jeśli się zatrzymał przy W4, albo stał w momencie zobaczenia W4 - if (!AIControllFlag) // AI tylko sobie otwiera drzwi - iDrivigFlags &= ~moveStopCloser; // w razie przełączenia na AI ma - // nie podciągać do W4, gdy - // użytkownik zatrzymał za daleko - if ((iDrivigFlags & moveDoorOpened) == 0) - { // drzwi otwierać jednorazowo + if( !AIControllFlag ) { + // w razie przełączenia na AI ma nie podciągać do W4, gdy użytkownik zatrzymał za daleko + iDrivigFlags &= ~moveStopCloser; + } + + if( ( iDrivigFlags & moveDoorOpened ) == 0 ) { + // drzwi otwierać jednorazowo iDrivigFlags |= moveDoorOpened; // nie wykonywać drugi raz - if (mvOccupied->DoorOpenCtrl == 1) //(mvOccupied->TrainType==dt_EZT) - { // otwieranie drzwi w EZT - if (AIControllFlag) // tylko AI otwiera drzwi EZT, użytkownik - // musi samodzielnie - if (!mvOccupied->DoorLeftOpened && - !mvOccupied->DoorRightOpened) - { // otwieranie drzwi - int p2 = - int(floor(sSpeedTable[i].evEvent->ValueGet(2))) % - 10; // p7=platform side (1:left, 2:right, 3:both) - int lewe = (iDirection > 0) ? 1 : 2; // jeśli jedzie do tyłu, to drzwi otwiera odwrotnie - int prawe = (iDirection > 0) ? 2 : 1; - if (p2 & lewe) - mvOccupied->DoorLeft(true); - if (p2 & prawe) - mvOccupied->DoorRight(true); - } - } - else - { // otwieranie drzwi w składach wagonowych - docelowo wysyłać komendę zezwolenia na otwarcie drzwi - int p7, lewe, prawe; // p7=platform side (1:left, 2:right, 3:both) - // tu będzie jeszcze długość peronu zaokrąglona do 10m - // (20m bezpieczniej, bo nie modyfikuje bitu 1) - p7 = int(std::floor(sSpeedTable[i].evEvent->ValueGet(2))) % 10; - TDynamicObject *p = pVehicles[0]; // pojazd na czole składu - while (p) - { // otwieranie drzwi w pojazdach - flaga zezwolenia była by lepsza - // jeśli jedzie do tyłu, to drzwi otwiera odwrotnie - lewe = (p->DirectionGet() > 0) ? 1 : 2; - prawe = 3 - lewe; - // wagony muszą mieć baterię załączoną do otwarcia drzwi... - p->MoverParameters->BatterySwitch(true); - if (p7 & lewe) - p->MoverParameters->DoorLeft(true); - if (p7 & prawe) - p->MoverParameters->DoorRight(true); - // pojazd podłączony z tyłu (patrząc od czoła) - p = p->Next(); - } - } + Doors( true, static_cast( std::floor( sSpeedTable[ i ].evEvent->ValueGet( 2 ) ) ) % 10 ); } if (TrainParams->UpdateMTable( simulation::Time, asNextStop) ) { // to się wykona tylko raz po zatrzymaniu na W4 @@ -924,23 +886,9 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN iDrivigFlags &= ~( moveStartHorn | moveStartHornNow ); } // perform loading/unloading - auto const exchangetime { simulation::Station.update_load( pVehicles[ 0 ], *TrainParams ) }; - // TBD: adjust time to load exchange size - if( fStopTime > -55 ) { - // na końcu rozkładu się ustawia 60s i tu by było skrócenie -// WaitingSet( 15.0 + Random( 15.0 ) ); // 10 sekund (wziąć z rozkładu????) - czekanie - // with door open on both sides calculated loading time is halved - // p7=platform side (1:left, 2:right, 3:both) - auto const platformside = static_cast( std::floor( sSpeedTable[ i ].evEvent->ValueGet( 2 ) ) ) % 10; - WaitingSet( - platformside == 3 ? - exchangetime * 0.5 : - exchangetime ); - } - // update brake settings and ai braking tables - // NOTE: this calculation is run after completing loading/unloading - // remember to move it to a more fitting spot, when loading/unloading taks some actual time - AutoRewident(); // nastawianie hamulca do jazdy pociągowej + auto const platformside = static_cast( std::floor( sSpeedTable[ i ].evEvent->ValueGet( 2 ) ) ) % 10; + auto const exchangetime = simulation::Station.update_load( pVehicles[ 0 ], *TrainParams, platformside ); + WaitingSet( std::max( -fStopTime, exchangetime ) ); // na końcu rozkładu się ustawia 60s i tu by było skrócenie if( TrainParams->CheckTrainLatency() < 0.0 ) { // odnotowano spóźnienie @@ -1011,6 +959,10 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN + ": at " + std::to_string(simulation::Time.data().wHour) + ":" + std::to_string(simulation::Time.data().wMinute) + " next " + asNextStop); // informacja #endif + // update brake settings and ai braking tables + // NOTE: this calculation is expected to run after completing loading/unloading + AutoRewident(); // nastawianie hamulca do jazdy pociągowej + if( int( floor( sSpeedTable[ i ].evEvent->ValueGet( 1 ) ) ) & 1 ) { // nie podjeżdżać do semafora, jeśli droga nie jest wolna iDrivigFlags |= moveStopHere; @@ -2400,7 +2352,7 @@ bool TController::ReleaseEngine() ReactionTime = PrepareTime; if (AIControllFlag) { // jeśli steruje komputer - if (mvOccupied->DoorOpenCtrl == 1) + if (mvOccupied->DoorCloseCtrl == control::driver) { // zamykanie drzwi if (mvOccupied->DoorLeftOpened) mvOccupied->DoorLeft(false); @@ -3050,50 +3002,66 @@ void TController::SpeedSet() } }; -void TController::Doors(bool what) -{ // otwieranie/zamykanie drzwi w składzie albo (tylko AI) EZT - if (what) - { // otwarcie - } - else - { // zamykanie - if (mvOccupied->DoorOpenCtrl == 1) - { // jeśli drzwi sterowane z kabiny - if( AIControllFlag ) { - if( mvOccupied->DoorLeftOpened || mvOccupied->DoorRightOpened ) { - // AI zamyka drzwi przed odjazdem - if( ( true == mvOccupied->DoorClosureWarning ) - && ( false == mvOccupied->DepartureSignal ) - && ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) { - mvOccupied->signal_departure( true ); // załącenie bzyczka - fActionTime = -3.0 - 0.1 * Random( 10 ); // 3-4 second wait - } - if( fActionTime > -0.5 ) { - // Ra: trzeba by ustawić jakiś czas oczekiwania na zamknięcie się drzwi - mvOccupied->DoorLeft( false ); // zamykanie drzwi - mvOccupied->DoorRight( false ); - iDrivigFlags &= ~moveDoorOpened; - // 1.5-2.5 sec wait, +potentially 0.5 remaining - fActionTime = -1.5 - 0.1 * Random( 10 ); - } +// otwieranie/zamykanie drzwi w składzie albo (tylko AI) EZT +void TController::Doors( bool const Open, int const Side ) { + + if( true == Open ) { + // otwieranie drzwi + // otwieranie drzwi w składach wagonowych - docelowo wysyłać komendę zezwolenia na otwarcie drzwi + // tu będzie jeszcze długość peronu zaokrąglona do 10m (20m bezpieczniej, bo nie modyfikuje bitu 1) + // p7=platform side (1:left, 2:right, 3:both) + auto *vehicle = pVehicles[0]; // pojazd na czole składu + while( vehicle != nullptr ) { + // wagony muszą mieć baterię załączoną do otwarcia drzwi... + vehicle->MoverParameters->BatterySwitch( true ); + // otwieranie drzwi w pojazdach - flaga zezwolenia była by lepsza + if( vehicle->MoverParameters->DoorOpenCtrl != control::passenger ) { + // if the door are controlled by the driver, we let the user operate them... + if( true == AIControllFlag ) { + // ...unless this user is an ai + // p7=platform side (1:left, 2:right, 3:both) + // jeśli jedzie do tyłu, to drzwi otwiera odwrotnie + auto const lewe = ( vehicle->DirectionGet() > 0 ) ? 1 : 2; + auto const prawe = 3 - lewe; + if( Side & lewe ) + vehicle->MoverParameters->DoorLeft( true, range::local ); + if( Side & prawe ) + vehicle->MoverParameters->DoorRight( true, range::local ); } } + // pojazd podłączony z tyłu (patrząc od czoła) + vehicle = vehicle->Next(); } - else - { // jeśli nie, to zamykanie w składzie wagonowym - TDynamicObject *p = pVehicles[0]; // pojazd na czole składu - while (p) - { // zamykanie drzwi w pojazdach - flaga zezwolenia była by lepsza - p->MoverParameters->DoorLeft(false); // w lokomotywie można by nie zamykać... - p->MoverParameters->DoorRight(false); - p = p->Next(); // pojazd podłączony z tyłu (patrząc od czoła) + } + else { + // zamykanie + if( AIControllFlag ) { + if( ( true == mvOccupied->DoorClosureWarning ) + && ( false == mvOccupied->DepartureSignal ) + && ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) { + mvOccupied->signal_departure( true ); // załącenie bzyczka + fActionTime = -3.0 - 0.1 * Random( 10 ); // 3-4 second wait } - // WaitingSet(5); //10 sekund tu to za długo, opóźnia odjazd o pół minuty - fActionTime = -3.0 - 0.1 * Random(10); // czekanie sekundę, może trochę dłużej + } + + if( ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) + && ( ( 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 + auto *vehicle = pVehicles[ 0 ]; // pojazd na czole składu + while( vehicle != nullptr ) { + // zamykanie drzwi w pojazdach - flaga zezwolenia była by lepsza + if( vehicle->MoverParameters->DoorCloseCtrl != control::passenger ) { + vehicle->MoverParameters->DoorLeft( false, range::local ); // w lokomotywie można by nie zamykać... + vehicle->MoverParameters->DoorRight( false, range::local ); + } + vehicle = vehicle->Next(); // pojazd podłączony z tyłu (patrząc od czoła) + } + fActionTime = -2.5 - 0.1 * Random( 10 ); // 2.5-3.5 sec wait, +potentially 0.5 remaining iDrivigFlags &= ~moveDoorOpened; // zostały zamknięte - nie wykonywać drugi raz } } -}; +} void TController::RecognizeCommand() { // odczytuje i wykonuje komendę przekazaną lokomotywie @@ -3631,7 +3599,7 @@ TController::UpdateSituation(double dt) { // for human-controlled vehicles with no door control and dynamic brake auto-activating with door open if( ( false == AIControllFlag ) && ( iDrivigFlags & moveDoorOpened ) - && ( mvOccupied->DoorOpenCtrl != 1 ) + && ( mvOccupied->DoorCloseCtrl != control::driver ) && ( mvControlling->MainCtrlPos > 0 ) ) { Doors( false ); } @@ -4097,7 +4065,10 @@ TController::UpdateSituation(double dt) { 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 +*/ } } else { @@ -4246,8 +4217,8 @@ TController::UpdateSituation(double dt) { && ( false == TestFlag( iDrivigFlags, movePress ) ) && ( iCoupler == 0 ) // && ( mvOccupied->Vel > 0.0 ) - && ( pVehicle->PrevConnected == nullptr ) - && ( pVehicle->NextConnected == nullptr ) ) { + && ( pVehicle->MoverParameters->Couplers[ side::front ].CouplingFlag == coupling::faux ) + && ( pVehicle->MoverParameters->Couplers[ side::rear ].CouplingFlag == coupling::faux ) ) { SetVelocity(0, 0, stopJoin); // 1. faza odczepiania: zatrzymanie // WriteLog("Zatrzymanie w celu odczepienia"); AccPreferred = std::min( 0.0, AccPreferred ); @@ -5039,7 +5010,7 @@ TController::UpdateSituation(double dt) { fMinProximityDist : // cars are allowed to move within min proximity distance fMaxProximityDist ) ? // other vehicle types keep wider margin true : - vel < VelNext ) ) { + ( vel + 1.0 ) < VelNext ) ) { // to można przyspieszyć IncSpeed(); } diff --git a/Driver.h b/Driver.h index f28c7a3a..6ddf0c22 100644 --- a/Driver.h +++ b/Driver.h @@ -303,7 +303,7 @@ private: bool IncSpeed(); bool DecSpeed(bool force = false); void SpeedSet(); - void Doors(bool what); + void Doors(bool const Open, int const Side = 0); void RecognizeCommand(); // odczytuje komende przekazana lokomotywie void Activation(); // umieszczenie obsady w odpowiednim członie void ControllingSet(); // znajduje człon do sterowania diff --git a/DynObj.cpp b/DynObj.cpp index 087e5a06..953fc8ba 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -1926,37 +1926,49 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" } } // koniec hamulce else if( ( ActPar.size() >= 3 ) - && ( ActPar.substr( 0, 2 ) == "WF" ) ) { - // wheel flat - // TODO: convert this whole mess to something more elegant one day - ActPar.erase( 0, 2 ); + && ( ActPar[ 0 ] == 'W' ) ) { + // wheel + ActPar.erase( 0, 1 ); + auto fixedflatsize { 0 }; - { - // fixed size flat - auto const indexend { ActPar.find_first_not_of( "1234567890", 0 ) }; - fixedflatsize = std::atoi( ActPar.substr( 0, indexend ).c_str() ); - ActPar.erase( 0, indexend ); - } - // optional parameters auto randomflatsize { 0 }; - auto randomflatchance { 100 }; + auto flatchance { 100 }; + while( false == ActPar.empty() ) { - if( ActPar[ 0 ] == 'R' ) { - // random flat size - auto const indexstart { 1 }; - auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) }; - randomflatsize = std::atoi( ActPar.substr( indexstart, indexend ).c_str() ); - ActPar.erase( 0, indexend ); - } - else if( ActPar[ 0 ] == 'P' ) { - // random flat probability - auto const indexstart { 1 }; - auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) }; - randomflatchance = std::atoi( ActPar.substr( indexstart, indexend ).c_str() ); - ActPar.erase( 0, indexend ); + // TODO: convert this whole copy and paste mess to something more elegant one day + switch( ActPar[ 0 ] ) { + case 'F': { + // fixed flat size + auto const indexstart { 1 }; + auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) }; + fixedflatsize = std::atoi( ActPar.substr( indexstart, indexend ).c_str() ); + ActPar.erase( 0, indexend ); + break; + } + case 'R': { + // random flat size + auto const indexstart { 1 }; + auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) }; + randomflatsize = std::atoi( ActPar.substr( indexstart, indexend ).c_str() ); + ActPar.erase( 0, indexend ); + break; + } + case 'P': { + // random flat probability + auto const indexstart { 1 }; + auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) }; + flatchance = std::atoi( ActPar.substr( indexstart, indexend ).c_str() ); + ActPar.erase( 0, indexend ); + break; + } + default: { + // unrecognized key + ActPar.erase( 0, 1 ); + break; + } } } - if( Random(0, 100) <= randomflatchance ) { + if( Random( 0, 100 ) <= flatchance ) { MoverParameters->WheelFlat += fixedflatsize + Random( 0, randomflatsize ); } } @@ -2478,6 +2490,87 @@ bool TDynamicObject::UpdateForce(double dt, double dt1, bool FullVer) return true; } +// initiates load change by specified amounts, with a platform on specified side +void TDynamicObject::LoadExchange( int const Disembark, int const Embark, int const Platform ) { + + if( ( MoverParameters->DoorOpenCtrl == control::passenger ) + || ( MoverParameters->DoorOpenCtrl == control::mixed ) ) { + // jeśli jedzie do tyłu, to drzwi otwiera odwrotnie + auto const lewe = ( DirectionGet() > 0 ) ? 1 : 2; + auto const prawe = 3 - lewe; + if( Platform & lewe ) { + MoverParameters->DoorLeft( true, range::local ); + } + if( Platform & prawe ) { + MoverParameters->DoorRight( true, range::local ); + } + } + m_exchange.unload_count += Disembark; + m_exchange.load_count += Embark; + m_exchange.speed_factor = ( + Platform == 3 ? + 2.0 : + 1.0 ); + m_exchange.time = 0.0; +} + +// update state of load exchange operation +void TDynamicObject::update_exchange( double const Deltatime ) { + + if( ( m_exchange.unload_count < 0.01 ) && ( m_exchange.load_count < 0.01 ) ) { return; } + + if( ( MoverParameters->Vel < 2.0 ) + && ( ( true == MoverParameters->DoorLeftOpened ) + || ( true == MoverParameters->DoorRightOpened ) ) ) { + + m_exchange.time += Deltatime; + while( ( m_exchange.unload_count > 0.01 ) + && ( m_exchange.time >= 1.0 ) ) { + + m_exchange.time -= 1.0; + auto const exchangesize = std::min( m_exchange.unload_count, MoverParameters->UnLoadSpeed * m_exchange.speed_factor ); + m_exchange.unload_count -= exchangesize; + MoverParameters->LoadStatus = 1; + MoverParameters->Load = std::max( 0.f, MoverParameters->Load - exchangesize ); + update_load_visibility(); + } + if( m_exchange.unload_count < 0.01 ) { + // finish any potential unloading operation before adding new load + // don't load more than can fit + m_exchange.load_count = std::min( m_exchange.load_count, MoverParameters->MaxLoad - MoverParameters->Load ); + while( ( m_exchange.load_count > 0.01 ) + && ( m_exchange.time >= 1.0 ) ) { + + m_exchange.time -= 1.0; + auto const exchangesize = std::min( m_exchange.load_count, MoverParameters->LoadSpeed * m_exchange.speed_factor ); + m_exchange.load_count -= exchangesize; + MoverParameters->LoadStatus = 2; + MoverParameters->Load = std::min( MoverParameters->MaxLoad, MoverParameters->Load + exchangesize ); // std::max not strictly needed but, eh + update_load_visibility(); + } + } + } + + if( MoverParameters->Vel > 2.0 ) { + // if the vehicle moves too fast cancel the exchange + m_exchange.unload_count = 0; + m_exchange.load_count = 0; + } + + if( ( m_exchange.unload_count < 0.01 ) + && ( m_exchange.load_count < 0.01 ) ) { + + MoverParameters->LoadStatus = 0; + // if the exchange is completed (or canceled) close the door, if applicable + if( ( MoverParameters->DoorCloseCtrl == control::passenger ) + || ( MoverParameters->DoorCloseCtrl == control::mixed ) ) { + + MoverParameters->DoorLeft( false, range::local ); + MoverParameters->DoorRight( false, range::local ); + } + } +} + void TDynamicObject::LoadUpdate() { // przeładowanie modelu ładunku // Ra: nie próbujemy wczytywać modeli miliony razy podczas renderowania!!! @@ -2496,7 +2589,11 @@ void TDynamicObject::LoadUpdate() { } if( mdLoad == nullptr ) { // if this fails, try generic load model - mdLoad = TModelsManager::GetModel( asBaseDir + MoverParameters->LoadType, true ); + auto const genericloadfilename { asBaseDir + MoverParameters->LoadType }; + if( ( true == FileExists( genericloadfilename + ".e3d" ) ) + || ( true == FileExists( genericloadfilename + ".t3d" ) ) ) { + mdLoad = TModelsManager::GetModel( genericloadfilename, true ); + } } if( mdLoad != nullptr ) { // TODO: discern from vehicle component which merely uses vehicle directory and has no animations, so it can be initialized outright @@ -2541,11 +2638,11 @@ TDynamicObject::update_load_sections() { void TDynamicObject::update_load_visibility() { - +/* if( Random() < 0.25 ) { shuffle_load_sections(); } - +*/ auto loadpercentage { ( MoverParameters->MaxLoad == 0.0 ? 0.0 : @@ -3523,6 +3620,8 @@ bool TDynamicObject::Update(double dt, double dt1) } MoverParameters->DerailReason = 0; //żeby tylko raz } + + update_exchange( dt ); if (MoverParameters->LoadStatus) LoadUpdate(); // zmiana modelu ładunku @@ -3563,6 +3662,7 @@ bool TDynamicObject::FastUpdate(double dt) // ResetdMoveLen(); FastMove(dDOMoveLen); + update_exchange( dt ); if (MoverParameters->LoadStatus) LoadUpdate(); // zmiana modelu ładunku return true; // Ra: chyba tak? @@ -3759,6 +3859,19 @@ void TDynamicObject::RenderSounds() { } // other sounds + // load exchange + if( MoverParameters->LoadStatus & 1 ) { + m_exchangesounds.unloading.play( sound_flags::exclusive ); + } + else { + m_exchangesounds.unloading.stop(); + } + if( MoverParameters->LoadStatus & 2 ) { + m_exchangesounds.loading.play( sound_flags::exclusive ); + } + else { + m_exchangesounds.loading.stop(); + } // NBMX sygnal odjazdu if( MoverParameters->DoorClosureWarning ) { if( MoverParameters->DepartureSignal ) { @@ -4880,6 +4993,16 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, } } + else if( token == "unloading:" ) { + m_exchangesounds.unloading.deserialize( parser, sound_type::single ); + m_exchangesounds.unloading.owner( this ); + } + + else if( token == "loading:" ) { + m_exchangesounds.loading.deserialize( parser, sound_type::single ); + m_exchangesounds.loading.owner( this ); + } + else if( token == "sand:" ) { sSand.deserialize( parser, sound_type::multipart, sound_parameters::range ); sSand.owner( this ); diff --git a/DynObj.h b/DynObj.h index 14610ca2..cac32e30 100644 --- a/DynObj.h +++ b/DynObj.h @@ -265,6 +265,13 @@ private: private: // types + struct exchange_data { + float unload_count { 0.f }; // amount to unload + float load_count { 0.f }; // amount to load + float speed_factor { 1.f }; // operation speed modifier + float time { 0.f }; // time spent on the operation + }; + struct coupler_sounds { sound_source dsbCouplerAttach { sound_placement::external }; // moved from cab sound_source dsbCouplerDetach { sound_placement::external }; // moved from cab @@ -283,6 +290,11 @@ private: sound_source rsDoorClose { sound_placement::general, 25.f }; }; + struct exchange_sounds { + sound_source loading { sound_placement::general }; + sound_source unloading { sound_placement::general }; + }; + struct axle_sounds { double distance; // distance to rail joint double offset; // axle offset from centre of the vehicle @@ -319,6 +331,8 @@ private: void ABuBogies(); void ABuModelRoll(); void TurnOff(); + // update state of load exchange operation + void update_exchange( double const Deltatime ); // members TButton btCoupler1; // sprzegi @@ -386,6 +400,9 @@ private: sound_source rscurve { sound_placement::external, EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; // youBy sound_source rsDerailment { sound_placement::external, 250.f }; // McZapkie-051202 + exchange_data m_exchange; // state of active load exchange procedure, if any + exchange_sounds m_exchangesounds; // sounds associated with the load exchange + Math3D::vector3 modelShake; bool renderme; // yB - czy renderowac @@ -467,6 +484,8 @@ private: 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); + // initiates load change by specified amounts, with a platform on specified side + void LoadExchange( int const Disembark, int const Embark, int const Platform ); void LoadUpdate(); void update_load_sections(); void update_load_visibility(); @@ -554,6 +573,7 @@ private: void DestinationSet(std::string to, std::string numer); std::string TextureTest(std::string const &name); void OverheadTrack(float o); + double MED[9][8]; // lista zmiennych do debugowania hamulca ED static std::string const MED_labels[ 8 ]; }; diff --git a/EU07.cpp b/EU07.cpp index 3ae98062..44079c3a 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -438,7 +438,6 @@ int main(int argc, char *argv[]) return -1; } - TPythonInterpreter::killInstance(); #ifdef _WIN32 Console::Off(); // wyłączenie konsoli (komunikacji zwrotnej) SafeDelete( pConsole ); @@ -448,7 +447,8 @@ int main(int argc, char *argv[]) glfwDestroyWindow(window); glfwTerminate(); - _exit(0); // skip destructors, there are ordering errors which causes segfaults + TPythonInterpreter::killInstance(); + _exit(0); // skip destructors, there are ordering errors which causes segfaults return 0; } diff --git a/Globals.h b/Globals.h index 539e44c4..ad6471a6 100644 --- a/Globals.h +++ b/Globals.h @@ -118,6 +118,7 @@ struct global_settings { float SplineFidelity{ 1.f }; // determines segment size during conversion of splines to geometry bool ResourceSweep{ true }; // gfx resource garbage collection bool ResourceMove{ false }; // gfx resources are moved between cpu and gpu side instead of sending a copy + bool compress_tex{ true }; // all textures are compressed on gpu side std::string asSky{ "1" }; bool bGlutFont{ false }; // czy tekst generowany przez GLUT32.DLL double fFpsAverage{ 20.0 }; // oczekiwana wartosć FPS @@ -168,7 +169,6 @@ struct global_settings { std::string screenshot_dir; bool loading_log = true; bool dds_upper_origin = false; - bool compress_tex = true; // methods void LoadIniFile( std::string asFileName ); diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index c72285f9..709a9008 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -153,7 +153,8 @@ enum coupling { gangway = 0x10, mainhose = 0x20, heating = 0x40, - permanent = 0x80 + permanent = 0x80, + uic = 0x100 }; // possible effect ranges for control commands; exclusive enum range { @@ -176,6 +177,15 @@ enum light { redmarker_right = 0x20, rearendsignals = 0x40 }; + +// door operation methods; exclusive +enum control { + passenger, // local, opened/closed for duration of loading + driver, // remote, operated by the driver + autonomous, // local, closed when vehicle moves and/or after timeout + conductor, // remote, operated by the conductor + mixed // primary manual but answers also to remote control +}; /*typ hamulca elektrodynamicznego*/ static int const dbrake_none = 0; static int const dbrake_passive = 1; @@ -307,7 +317,7 @@ struct TCommand std::string Command; /*komenda*/ double Value1 = 0.0; /*argumenty komendy*/ double Value2 = 0.0; - int Coupling{ ctrain_controll }; // coupler flag used to determine command propagation + int Coupling { coupling::control }; // coupler flag used to determine command propagation TLocation Location; }; @@ -821,10 +831,10 @@ public: static std::vector const eimc_labels; double InverterFrequency { 0.0 }; // current frequency of power inverters /*-dla wagonow*/ - double MaxLoad = 0.0; /*masa w T lub ilosc w sztukach - ladownosc*/ + float MaxLoad = 0.f; /*masa w T lub ilosc w sztukach - ladownosc*/ std::string LoadAccepted; std::string LoadQuantity; /*co moze byc zaladowane, jednostki miary*/ double OverLoadFactor = 0.0; /*ile razy moze byc przekroczona ladownosc*/ - double LoadSpeed = 0.0; double UnLoadSpeed = 0.0;/*szybkosc na- i rozladunku jednostki/s*/ + float LoadSpeed = 0.f; float UnLoadSpeed = 0.f;/*szybkosc na- i rozladunku jednostki/s*/ int DoorOpenCtrl = 0; int DoorCloseCtrl = 0; /*0: przez pasazera, 1: przez maszyniste, 2: samoczynne (zamykanie)*/ double DoorStayOpen = 0.0; /*jak dlugo otwarte w przypadku DoorCloseCtrl=2*/ bool DoorClosureWarning = false; /*czy jest ostrzeganie przed zamknieciem*/ @@ -887,7 +897,7 @@ public: bool CompressorAllowLocal{ true }; // local device state override (most units don't have this fitted so it's set to true not to intefere) bool CompressorGovernorLock{ false }; // indicates whether compressor pressure switch was activated due to reaching cut-out pressure // TODO converter parameters, for when we start cleaning up mover parameters - start ConverterStart{ manual }; // whether converter is started manually, or by other means + start ConverterStart{ start::manual }; // whether converter is started manually, or by other means float ConverterStartDelay{ 0.0f }; // delay (in seconds) before the converter is started, once its activation conditions are met double ConverterStartDelayTimer{ 0.0 }; // helper, for tracking whether converter start delay passed bool ConverterAllow = false; /*zezwolenie na prace przetwornicy NBMX*/ @@ -1024,7 +1034,7 @@ public: double eAngle = M_PI * 0.5; /*-dla wagonow*/ - double Load = 0.0; /*masa w T lub ilosc w sztukach - zaladowane*/ + float Load = 0.f; /*masa w T lub ilosc w sztukach - zaladowane*/ std::string LoadType; /*co jest zaladowane*/ int LoadStatus = 0; //+1=trwa rozladunek,+2=trwa zaladunek,+4=zakończono,0=zaktualizowany model double LastLoadChangeTime = 0.0; //raz (roz)ładowania @@ -1220,8 +1230,8 @@ public: /* funckje dla wagonow*/ bool LoadingDone(double LSpeed, std::string LoadInit); - bool DoorLeft(bool State); //obsluga drzwi lewych - bool DoorRight(bool State); //obsluga drzwi prawych + bool DoorLeft(bool State, int const Notify = range::consist ); //obsluga drzwi lewych + bool DoorRight(bool State, int const Notify = range::consist ); //obsluga drzwi prawych bool DoorBlockedFlag(void); //sprawdzenie blokady drzwi bool signal_departure( bool const State, int const Notify = range::consist ); // toggles departure warning diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 7c5ef7d7..8f6e4a4f 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -6004,65 +6004,80 @@ bool TMoverParameters::DoorBlockedFlag(void) // Q: 20160713 // Otwiera / zamyka lewe drzwi // ************************************************************************************************* -bool TMoverParameters::DoorLeft(bool State) -{ - bool DL = false; - if ((DoorLeftOpened != State) && (DoorBlockedFlag() == false) && (Battery == true)) - { - DL = true; - DoorLeftOpened = State; - if (State == true) - { - if (CabNo > 0) - SendCtrlToNext("DoorOpen", 1, CabNo); // 1=lewe, 2=prawe - else - SendCtrlToNext("DoorOpen", 2, CabNo); // zamiana - CompressedVolume -= 0.003; - } - else - { - if (CabNo > 0) - SendCtrlToNext("DoorClose", 1, CabNo); - else - SendCtrlToNext("DoorClose", 2, CabNo); - } +// NOTE: door methods work regardless of vehicle door control type, +// but commands issued through the command system work only for vehicles which accept remote door control +bool TMoverParameters::DoorLeft(bool State, int const Notify ) { + + if( DoorLeftOpened == State ) { + // TBD: should the command be passed to other vehicles regardless of whether it affected the primary target? + // (for the time being no, methods are often invoked blindly which would lead to commands spam) + return false; } - else - DL = false; - return DL; + + bool result { false }; + + if( ( Battery == true ) + && ( DoorBlockedFlag() == false ) ) { + + DoorLeftOpened = State; + result = true; + } + if( Notify != range::local ) { + + SendCtrlToNext( + ( State == true ? + "DoorOpen" : + "DoorClose" ), + ( CabNo > 0 ? // 1=lewe, 2=prawe (swap if reversed) + 1 : + 2 ), + CabNo, + ( Notify == range::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return result; } // ************************************************************************************************* // Q: 20160713 // Otwiera / zamyka prawe drzwi // ************************************************************************************************* -bool TMoverParameters::DoorRight(bool State) -{ - bool DR = false; - if ((DoorRightOpened != State) && (DoorBlockedFlag() == false) && (Battery == true)) - { - DR = true; - DoorRightOpened = State; - if (State == true) - { - if (CabNo > 0) - SendCtrlToNext("DoorOpen", 2, CabNo); // 1=lewe, 2=prawe - else - SendCtrlToNext("DoorOpen", 1, CabNo); // zamiana - CompressedVolume -= 0.003; - } - else - { - if (CabNo > 0) - SendCtrlToNext("DoorClose", 2, CabNo); - else - SendCtrlToNext("DoorClose", 1, CabNo); - } - } - else - DR = false; +// NOTE: door methods work regardless of vehicle door control type, +// but commands issued through the command system work only for vehicles which accept remote door control +bool TMoverParameters::DoorRight(bool State, int const Notify ) { - return DR; + if( DoorRightOpened == State ) { + // TBD: should the command be passed to other vehicles regardless of whether it affected the primary target? + // (for the time being no, methods are often invoked blindly which would lead to commands spam) + return false; + } + + bool result { false }; + + if( ( Battery == true ) + && ( DoorBlockedFlag() == false ) ) { + + DoorRightOpened = State; + result = true; + } + if( Notify != range::local ) { + + SendCtrlToNext( + ( State == true ? + "DoorOpen" : + "DoorClose" ), + ( CabNo > 0 ? // 1=lewe, 2=prawe (swap if reversed) + 2 : + 1 ), + CabNo, + ( Notify == range::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return result; } // toggles departure warning @@ -6084,8 +6099,8 @@ TMoverParameters::signal_departure( bool const State, int const Notify ) { 0 ), CabNo, ( Notify == range::unit ? - ctrain_controll | ctrain_depot : - ctrain_controll ) ); + coupling::control | coupling::permanent : + coupling::control ) ); } return true; @@ -7183,8 +7198,8 @@ void TMoverParameters::LoadFIZ_Brake( std::string const &line ) { auto lookup = compressorpowers.find( extract_value( "CompressorPower", line ) ); CompressorPower = lookup != compressorpowers.end() ? - lookup->second : - 1; + lookup->second : + 1; } if( true == extract_value( AirLeakRate, "AirLeakRate", line, "" ) ) { @@ -7195,16 +7210,31 @@ void TMoverParameters::LoadFIZ_Brake( std::string const &line ) { void TMoverParameters::LoadFIZ_Doors( std::string const &line ) { - DoorOpenCtrl = 0; - std::string openctrl; extract_value( openctrl, "OpenCtrl", line, "" ); - if( openctrl == "DriverCtrl" ) { DoorOpenCtrl = 1; } - - DoorCloseCtrl = 0; - std::string closectrl; extract_value( closectrl, "CloseCtrl", line, "" ); - if( closectrl == "DriverCtrl" ) { DoorCloseCtrl = 1; } - else if( closectrl == "AutomaticCtrl" ) { DoorCloseCtrl = 2; } - - if( DoorCloseCtrl == 2 ) { extract_value( DoorStayOpen, "DoorStayOpen", line, "" ); } + std::map doorcontrols { + { "Passenger", control::passenger }, + { "AutomaticCtrl", control::autonomous }, + { "DriverCtrl", control::driver }, + { "Conductor", control::conductor }, + { "Mixed", control::mixed } + }; + // opening method + { + auto lookup = doorcontrols.find( extract_value( "OpenCtrl", line ) ); + DoorOpenCtrl = + lookup != doorcontrols.end() ? + lookup->second : + control::passenger; + } + // closing method + { + auto lookup = doorcontrols.find( extract_value( "CloseCtrl", line ) ); + DoorCloseCtrl = + lookup != doorcontrols.end() ? + lookup->second : + control::passenger; + } + // automatic closing timer + if( DoorCloseCtrl == control::autonomous ) { extract_value( DoorStayOpen, "DoorStayOpen", line, "" ); } extract_value( DoorOpenSpeed, "OpenSpeed", line, "" ); extract_value( DoorCloseSpeed, "CloseSpeed", line, "" ); @@ -8314,6 +8344,7 @@ bool TMoverParameters::SendCtrlToNext( std::string const CtrlCommand, double con // musi być wybrana niezerowa kabina if( ( Couplers[ d ].Connected != nullptr ) && ( TestFlag( Couplers[ d ].CouplingFlag, Couplertype ) ) ) { + if( Couplers[ d ].ConnectedNr != d ) { // jeśli ten nastpęny jest zgodny z aktualnym if( Couplers[ d ].Connected->SetInternalCommand( CtrlCommand, ctrlvalue, dir, Couplertype ) ) @@ -8472,38 +8503,48 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C } else if (Command == "DoorOpen") /*NBMX*/ { // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów - if ((CValue2 > 0)) - { // normalne ustawienie pojazdu - if ((CValue1 == 1) || (CValue1 == 3)) - DoorLeftOpened = true; - if ((CValue1 == 2) || (CValue1 == 3)) - DoorRightOpened = true; - } - else - { // odwrotne ustawienie pojazdu - if ((CValue1 == 2) || (CValue1 == 3)) - DoorLeftOpened = true; - if ((CValue1 == 1) || (CValue1 == 3)) - DoorRightOpened = true; - } + if( ( DoorCloseCtrl == control::conductor ) + || ( DoorCloseCtrl == control::driver ) + || ( DoorCloseCtrl == control::mixed ) ) { + // ignore remote command if the door is only operated locally + if( CValue2 > 0 ) { + // normalne ustawienie pojazdu + if( ( CValue1 == 1 ) || ( CValue1 == 3 ) ) + DoorLeftOpened = true; + if( ( CValue1 == 2 ) || ( CValue1 == 3 ) ) + DoorRightOpened = true; + } + else { + // odwrotne ustawienie pojazdu + if( ( CValue1 == 2 ) || ( CValue1 == 3 ) ) + DoorLeftOpened = true; + if( ( CValue1 == 1 ) || ( CValue1 == 3 ) ) + DoorRightOpened = true; + } + } OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } else if (Command == "DoorClose") /*NBMX*/ { // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów - if ((CValue2 > 0)) - { // normalne ustawienie pojazdu - if ((CValue1 == 1) || (CValue1 == 3)) - DoorLeftOpened = false; - if ((CValue1 == 2) || (CValue1 == 3)) - DoorRightOpened = false; - } - else - { // odwrotne ustawienie pojazdu - if ((CValue1 == 2) || (CValue1 == 3)) - DoorLeftOpened = false; - if ((CValue1 == 1) || (CValue1 == 3)) - DoorRightOpened = false; - } + if( ( DoorCloseCtrl == control::conductor ) + || ( DoorCloseCtrl == control::driver ) + || ( DoorCloseCtrl == control::mixed ) ) { + // ignore remote command if the door is only operated locally + if( CValue2 > 0 ) { + // normalne ustawienie pojazdu + if( ( CValue1 == 1 ) || ( CValue1 == 3 ) ) + DoorLeftOpened = false; + if( ( CValue1 == 2 ) || ( CValue1 == 3 ) ) + DoorRightOpened = false; + } + else { + // odwrotne ustawienie pojazdu + if( ( CValue1 == 2 ) || ( CValue1 == 3 ) ) + DoorLeftOpened = false; + if( ( CValue1 == 1 ) || ( CValue1 == 3 ) ) + DoorRightOpened = false; + } + } OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } else if( Command == "DepartureSignal" ) { diff --git a/MdlMngr.cpp b/MdlMngr.cpp index 2013afb6..13233c19 100644 --- a/MdlMngr.cpp +++ b/MdlMngr.cpp @@ -18,6 +18,7 @@ http://mozilla.org/MPL/2.0/. #include "Model3d.h" #include "Globals.h" +#include "Logs.h" #include "utilities.h" // wczytanie modelu do kontenerka @@ -78,42 +79,76 @@ TModelsManager::GetModel(std::string const &Name, bool const Dynamic) // - wczytanie uproszczonego wnętrza, ścieżka dokładna, tekstury z katalogu modelu // - niebo animowane, ścieżka brana ze wpisu, tekstury nieokreślone // - wczytanie modelu animowanego - Init() - sprawdzić - std::string buf; - std::string const buftp = Global.asCurrentTexturePath; // zapamiętanie aktualnej ścieżki do tekstur, - if( Name.find('\\') == std::string::npos ) - { - buf = "models\\" + Name; // Ra: było by lepiej katalog dodać w parserze - if( Name.find( '/') != std::string::npos) - { - Global.asCurrentTexturePath = Global.asCurrentTexturePath + Name; - Global.asCurrentTexturePath.erase(Global.asCurrentTexturePath.find("/") + 1, - Global.asCurrentTexturePath.length()); - } + std::string const buftp { Global.asCurrentTexturePath }; // zapamiętanie aktualnej ścieżki do tekstur, + std::string filename { Name }; + if( Name.find( '/' ) != std::string::npos ) { + // pobieranie tekstur z katalogu, w którym jest model + Global.asCurrentTexturePath += Name; + Global.asCurrentTexturePath.erase( Global.asCurrentTexturePath.rfind( "/" ) + 1 ); } - else - { - buf = Name; - if( Dynamic ) { - // na razie tak, bo nie wiadomo, jaki może mieć wpływ na pozostałe modele - if( Name.find( '/' ) != std::string::npos ) { // pobieranie tekstur z katalogu, w którym jest model - Global.asCurrentTexturePath = Global.asCurrentTexturePath + Name; - Global.asCurrentTexturePath.erase( - Global.asCurrentTexturePath.find( "/" ) + 1, - Global.asCurrentTexturePath.length() - 1 ); - } - } - } - buf = ToLower( buf ); - auto const lookup = m_modelsmap.find( buf ); - if( lookup != m_modelsmap.end() ) { + filename = ToLower( filename ); + if( ( filename.rfind( '.' ) != std::string::npos ) + && ( filename.rfind( '.' ) != filename.rfind( ".." ) + 1 ) ) { + // trim extension if there's one, but don't mistake folder traverse for extension + filename.erase( filename.rfind( '.' ) ); + } + + // see if we have it in the databank + auto *model { find_in_databank( filename ) }; + if( model != nullptr ) { Global.asCurrentTexturePath = buftp; - return ( m_models[ lookup->second ].Model.get() ); + return model; } - auto model = LoadModel(buf, Dynamic); // model nie znaleziony, to wczytać + // not yet loaded, check if it's on disk + std::string lookup { find_on_disk( filename ) }; + + if( false == lookup.empty() ) { + model = LoadModel( lookup, Dynamic ); // model nie znaleziony, to wczytać + } + else { + // there's nothing matching in the databank nor on the disk, report failure + ErrorLog( "Bad file: failed do locate 3d model file \"" + filename + "\"", logtype::file ); + } Global.asCurrentTexturePath = buftp; // odtworzenie ścieżki do tekstur return model; // NULL jeśli błąd }; +TModel3d * +TModelsManager::find_in_databank( std::string const &Name ) { + + std::vector filenames { + Name, + szModelPath + Name }; + + for( auto const &filename : filenames ) { + auto const lookup { m_modelsmap.find( filename ) }; + if( lookup != m_modelsmap.end() ) { + return ( m_models[ lookup->second ].Model.get() ); + } + } + + return nullptr; +} + +// checks whether specified file exists. returns name of the located file, or empty string. +std::string +TModelsManager::find_on_disk( std::string const &Name ) { + + std::vector extensions { { ".e3d" }, { ".t3d" } }; + for( auto const &extension : extensions ) { + + auto lookup = ( + FileExists( Name + extension ) ? Name : + FileExists( szModelPath + Name + extension ) ? szModelPath + Name : + "" ); + if( false == lookup.empty() ) { + return lookup; + } + } + + return {}; +} + //--------------------------------------------------------------------------- diff --git a/MdlMngr.h b/MdlMngr.h index 540a9820..c4d56287 100644 --- a/MdlMngr.h +++ b/MdlMngr.h @@ -20,6 +20,11 @@ private: // klasa statyczna, nie ma obiektu class TModelsManager { +public: + // McZapkie: dodalem sciezke, notabene Path!=Patch :) + static TModel3d *GetModel( std::string const &Name, bool dynamic = false ); + +private: // types: typedef std::deque modelcontainer_sequence; typedef std::unordered_map stringmodelcontainerindex_map; @@ -28,9 +33,10 @@ class TModelsManager { static stringmodelcontainerindex_map m_modelsmap; // methods: static TModel3d *LoadModel( std::string const &Name, bool const Dynamic ); -public: - // McZapkie: dodalem sciezke, notabene Path!=Patch :) - static TModel3d *GetModel( std::string const &Name, bool dynamic = false ); + static TModel3d *find_in_databank( std::string const &Name ); + // checks whether specified file exists. returns name of the located file, or empty string. + static std::string find_on_disk( std::string const &Name ); + }; //--------------------------------------------------------------------------- diff --git a/PyInt.cpp b/PyInt.cpp index 53c3184b..e426af9f 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -591,4 +591,4 @@ void TPythonScreens::_freeTrainState() Py_CLEAR(_trainState); _trainState = NULL; } -} +} \ No newline at end of file diff --git a/PyInt.h b/PyInt.h index 89b86c13..aad4411c 100644 --- a/PyInt.h +++ b/PyInt.h @@ -101,4 +101,4 @@ class TPythonScreens void finish(); }; -#endif // PyIntH +#endif // PyIntH \ No newline at end of file diff --git a/Texture.cpp b/Texture.cpp index 2385d606..fba8f6ca 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -647,7 +647,8 @@ opengl_texture::create() { else { // uncompressed texture data. have the gfx card do the compression as it sees fit ::glTexImage2D( - GL_TEXTURE_2D, 0, Global.compress_tex ? GL_COMPRESSED_RGBA : GL_RGBA, + GL_TEXTURE_2D, 0, + Global.compress_tex ? GL_COMPRESSED_RGBA : GL_RGBA, data_width, data_height, 0, data_format, GL_UNSIGNED_BYTE, (GLubyte *)&data[ 0 ] ); } @@ -821,7 +822,10 @@ texture_manager::create( std::string Filename, bool const Loadnow ) { Filename.erase( Filename.rfind( '.' ) ); } - std::replace(Filename.begin(), Filename.end(), '\\', '/'); // fix slashes + // change slashes to cross-platform + std::replace( + std::begin( Filename ), std::end( Filename ), + '\\', '/' ); std::vector extensions{ { ".dds" }, { ".tga" }, { ".png" }, { ".bmp" }, { ".ext" } }; @@ -998,17 +1002,19 @@ texture_manager::info() const { texture_handle texture_manager::find_in_databank( std::string const &Texturename ) const { - auto lookup = m_texturemappings.find( Texturename ); - if( lookup != m_texturemappings.end() ) { - return (int)lookup->second; - } - // jeszcze próba z dodatkową ścieżką - lookup = m_texturemappings.find( global_texture_path + Texturename ); + std::vector filenames { + Global.asCurrentTexturePath + Texturename, + Texturename, + szTexturePath + Texturename }; - return ( - lookup != m_texturemappings.end() ? - (int)lookup->second : - npos ); + for( auto const &filename : filenames ) { + auto const lookup { m_texturemappings.find( filename ) }; + if( lookup != m_texturemappings.end() ) { + return lookup->second; + } + } + + return npos; } // checks whether specified file exists. @@ -1016,8 +1022,9 @@ std::string texture_manager::find_on_disk( std::string const &Texturename ) const { return( + FileExists( Global.asCurrentTexturePath + Texturename ) ? Global.asCurrentTexturePath + Texturename : FileExists( Texturename ) ? Texturename : - FileExists( global_texture_path + Texturename ) ? global_texture_path + Texturename : + FileExists( szTexturePath + Texturename ) ? szTexturePath + Texturename : "" ); } diff --git a/Train.cpp b/Train.cpp index a38bebc8..92ba5c36 100644 --- a/Train.cpp +++ b/Train.cpp @@ -380,12 +380,12 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d) } } */ - MechSpring.Init(250); + MechSpring.Init(125.0); vMechVelocity = Math3D::vector3(0, 0, 0); pMechOffset = Math3D::vector3( 0, 0, 0 ); - fMechSpringX = 1; - fMechSpringY = 0.5; - fMechSpringZ = 0.5; + fMechSpringX = 0.2; + fMechSpringY = 0.2; + fMechSpringZ = 0.1; fMechMaxSpring = 0.15; fMechRoll = 0.05; fMechPitch = 0.1; @@ -3296,9 +3296,6 @@ void TTrain::OnCommand_doorlocktoggle( TTrain *Train, command_data const &Comman void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Command ) { - if( Train->mvOccupied->DoorOpenCtrl != 1 ) { - return; - } if( Command.action == GLFW_PRESS ) { // NOTE: test how the door state check works with consists where the occupied vehicle doesn't have opening doors if( false == ( @@ -3306,76 +3303,74 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman Train->mvOccupied->DoorLeftOpened : Train->mvOccupied->DoorRightOpened ) ) { // open + if( Train->mvOccupied->DoorOpenCtrl != control::driver ) { + return; + } if( Train->mvOccupied->ActiveCab == 1 ) { - if( Train->mvOccupied->DoorLeft( true ) ) { - Train->ggDoorLeftButton.UpdateValue( 1.0, Train->dsbSwitch ); - } + Train->mvOccupied->DoorLeft( true ); } else { // in the rear cab sides are reversed... - if( Train->mvOccupied->DoorRight( true ) ) { - // ...but so are the switches - Train->ggDoorLeftButton.UpdateValue( 1.0, Train->dsbSwitch ); - } + Train->mvOccupied->DoorRight( true ); } + // visual feedback + Train->ggDoorLeftButton.UpdateValue( 1.0, Train->dsbSwitch ); } else { // close + if( Train->mvOccupied->DoorCloseCtrl != control::driver ) { + return; + } + // TODO: move door opening/closing to the update, so the switch animation doesn't hinge on door working if( Train->mvOccupied->ActiveCab == 1 ) { - if( Train->mvOccupied->DoorLeft( false ) ) { - Train->ggDoorLeftButton.UpdateValue( 0.0, Train->dsbSwitch ); - } + Train->mvOccupied->DoorLeft( false ); } else { // in the rear cab sides are reversed... - if( Train->mvOccupied->DoorRight( false ) ) { - // ...but so are the switches - Train->ggDoorLeftButton.UpdateValue( 0.0, Train->dsbSwitch ); - } + Train->mvOccupied->DoorRight( false ); } + // visual feedback + Train->ggDoorLeftButton.UpdateValue( 0.0, Train->dsbSwitch ); } } } void TTrain::OnCommand_doortoggleright( TTrain *Train, command_data const &Command ) { - if( Train->mvOccupied->DoorOpenCtrl != 1 ) { - return; - } if( Command.action == GLFW_PRESS ) { // NOTE: test how the door state check works with consists where the occupied vehicle doesn't have opening doors if( false == ( Train->mvOccupied->ActiveCab == 1 ? - Train->mvOccupied->DoorRightOpened : - Train->mvOccupied->DoorLeftOpened ) ) { + Train->mvOccupied->DoorRightOpened : + Train->mvOccupied->DoorLeftOpened ) ) { // open + if( Train->mvOccupied->DoorOpenCtrl != control::driver ) { + return; + } if( Train->mvOccupied->ActiveCab == 1 ) { - if( Train->mvOccupied->DoorRight( true ) ) { - Train->ggDoorRightButton.UpdateValue( 1.0, Train->dsbSwitch ); - } + Train->mvOccupied->DoorRight( true ); } else { // in the rear cab sides are reversed... - if( Train->mvOccupied->DoorLeft( true ) ) { - // ...but so are the switches - Train->ggDoorRightButton.UpdateValue( 1.0, Train->dsbSwitch ); - } + Train->mvOccupied->DoorLeft( true ); } + // visual feedback + Train->ggDoorRightButton.UpdateValue( 1.0, Train->dsbSwitch ); } else { // close + if( Train->mvOccupied->DoorCloseCtrl != control::driver ) { + return; + } if( Train->mvOccupied->ActiveCab == 1 ) { - if( Train->mvOccupied->DoorRight( false ) ) { - Train->ggDoorRightButton.UpdateValue( 0.0, Train->dsbSwitch ); - } + Train->mvOccupied->DoorRight( false ); } else { // in the rear cab sides are reversed... - if( Train->mvOccupied->DoorLeft( false ) ) { - // ...but so are the switches - Train->ggDoorRightButton.UpdateValue( 0.0, Train->dsbSwitch ); - } + Train->mvOccupied->DoorLeft( false ); } + // visual feedback + Train->ggDoorRightButton.UpdateValue( 0.0, Train->dsbSwitch ); } } } diff --git a/material.cpp b/material.cpp index 2dcd80a3..7e504433 100644 --- a/material.cpp +++ b/material.cpp @@ -69,14 +69,14 @@ opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool c else if( key == "texture1:" ) { if( ( texture1 == null_handle ) || ( Priority > priority1 ) ) { - texture1 = GfxRenderer.Fetch_Texture( path( name ) + value, Loadnow ); + texture1 = GfxRenderer.Fetch_Texture( value, Loadnow ); priority1 = Priority; } } else if( key == "texture2:" ) { if( ( texture2 == null_handle ) || ( Priority > priority2 ) ) { - texture2 = GfxRenderer.Fetch_Texture( path( name ) + value, Loadnow ); + texture2 = GfxRenderer.Fetch_Texture( value, Loadnow ); priority2 = Priority; } } @@ -120,12 +120,10 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) { } filename += ".mat"; - std::replace(filename.begin(), filename.end(), '\\', '/'); // fix slashes - - if( filename.find( '/' ) == std::string::npos ) { - // jeśli bieżaca ścieżka do tekstur nie została dodana to dodajemy domyślną - filename = global_texture_path + filename; - } + // change slashes to llinux-compatible + std::replace( + std::begin( filename ), std::end( filename ), + '\\', '/' ); // try to locate requested material in the databank auto const databanklookup = find_in_databank( filename ); @@ -168,7 +166,7 @@ material_manager::find_in_databank( std::string const &Materialname ) const { return lookup->second; } // jeszcze próba z dodatkową ścieżką - lookup = m_materialmappings.find( global_texture_path + Materialname ); + lookup = m_materialmappings.find( szTexturePath + Materialname ); return ( lookup != m_materialmappings.end() ? @@ -183,7 +181,7 @@ material_manager::find_on_disk( std::string const &Materialname ) const { return( FileExists( Materialname ) ? Materialname : - FileExists( global_texture_path + Materialname ) ? global_texture_path + Materialname : + FileExists( szTexturePath + Materialname ) ? szTexturePath + Materialname : "" ); } diff --git a/renderer.cpp b/renderer.cpp index af035fc6..413be26b 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -182,11 +182,11 @@ opengl_renderer::Init( GLFWwindow *Window ) { } // preload some common textures WriteLog( "Loading common gfx data..." ); - m_glaretexture = Fetch_Texture( "fx\\lightglare" ); - m_suntexture = Fetch_Texture( "fx\\sun" ); - m_moontexture = Fetch_Texture( "fx\\moon" ); + m_glaretexture = Fetch_Texture( "fx/lightglare" ); + m_suntexture = Fetch_Texture( "fx/sun" ); + m_moontexture = Fetch_Texture( "fx/moon" ); if( m_helpertextureunit >= 0 ) { - m_reflectiontexture = Fetch_Texture( "fx\\reflections" ); + m_reflectiontexture = Fetch_Texture( "fx/reflections" ); } WriteLog( "...gfx data pre-loading done" ); diff --git a/station.cpp b/station.cpp index ab3d7e62..b9b4b441 100644 --- a/station.cpp +++ b/station.cpp @@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/. // exchanges load with consist attached to specified vehicle, operating on specified schedule double -basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule ) { +basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule, int const Platform ) { auto const firststop { Schedule.StationIndex == 1 }; auto const laststop { Schedule.StationIndex == Schedule.StationCount }; @@ -30,7 +30,13 @@ basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Sch auto const stationsizemodifier { ( trainstop ? 1.0 : 2.0 ) }; // go through all vehicles and update their load // NOTE: for the time being we limit ourselves to passenger-carrying cars only - auto exchangetime { 0.0 }; + auto exchangetime { 0.f }; + // platform (1:left, 2:right, 3:both) + // with exchange performed on both sides waiting times are halved + auto const exchangetimemodifier { ( + Platform == 3 ? + 0.5f : + 1.0f ) }; auto *vehicle { First }; while( vehicle != nullptr ) { @@ -47,24 +53,29 @@ basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Sch // NOTE: for the time being we're doing simple, random load change calculation // TODO: exchange driven by station parameters and time of the day auto unloadcount = static_cast( - firststop ? 0 : laststop ? parameters.Load : - std::min( + firststop ? 0 : + std::min( parameters.Load, Random( parameters.MaxLoad * 0.10 * stationsizemodifier ) ) ); auto loadcount = static_cast( laststop ? 0 : - Random( parameters.MaxLoad * 0.15 * stationsizemodifier ) ); + Random( parameters.MaxLoad * 0.15f * stationsizemodifier ) ); if( true == firststop ) { // slightly larger group at the initial station loadcount *= 2; } - parameters.Load = std::min( parameters.MaxLoad, parameters.Load - unloadcount + loadcount ); - vehicle->LoadUpdate(); - vehicle->update_load_visibility(); - exchangetime = std::max( exchangetime, unloadcount / parameters.UnLoadSpeed + loadcount / parameters.LoadSpeed); + if( ( unloadcount > 0 ) || ( loadcount > 0 ) ) { + + vehicle->LoadExchange( unloadcount, loadcount, Platform ); +/* + vehicle->LoadUpdate(); + vehicle->update_load_visibility(); +*/ + exchangetime = std::max( exchangetime, exchangetimemodifier * ( unloadcount / parameters.UnLoadSpeed + loadcount / parameters.LoadSpeed ) ); + } } vehicle = vehicle->Next(); } diff --git a/station.h b/station.h index 84f28f5e..1b6ef061 100644 --- a/station.h +++ b/station.h @@ -18,5 +18,5 @@ public: // methods // exchanges load with consist attached to specified vehicle, operating on specified schedule; returns: time needed for exchange, in seconds double - update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule ); + update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule, int const Platform ); }; diff --git a/utilities.h b/utilities.h index 4b8e4135..5abdbae3 100644 --- a/utilities.h +++ b/utilities.h @@ -25,12 +25,10 @@ http://mozilla.org/MPL/2.0/. #define DegToRad(a) ((M_PI / 180.0) * (a)) //(a) w nawiasie, bo może być dodawaniem #define RadToDeg(r) ((180.0 / M_PI) * (r)) -#define asModelsPath std::string("models\\") -#define asSceneryPath std::string("scenery\\") -#define szSceneryPath "scenery\\" -#define szTexturePath "textures\\" -#define szSoundPath "sounds\\" -#define global_texture_path "textures/" +#define szSceneryPath "scenery/" +#define szTexturePath "textures/" +#define szModelPath "models/" +#define szSoundPath "sounds/" #define MAKE_ID4(a,b,c,d) (((std::uint32_t)(d)<<24)|((std::uint32_t)(c)<<16)|((std::uint32_t)(b)<<8)|(std::uint32_t)(a))