diff --git a/AnimModel.cpp b/AnimModel.cpp index f7dc4752..5b184f96 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -438,7 +438,8 @@ TAnimModel::is_keyword( std::string const &Token ) const { return ( Token == "endmodel" ) || ( Token == "lights" ) || ( Token == "lightcolors" ) - || ( Token == "angles" ); + || ( Token == "angles" ) + || ( Token == "notransition" ); } bool TAnimModel::Load(cParser *parser, bool ter) @@ -529,6 +530,10 @@ bool TAnimModel::Load(cParser *parser, bool ter) >> vAngle[ 2 ]; } + if( token == "notransition" ) { + m_transition = false; + } + } while( ( false == token.empty() ) && ( token != "endmodel" ) ); @@ -589,6 +594,13 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) { auto &timer { m_lighttimers[ idx ] }; if( ( modeintegral < ls_Blink ) && ( modefractional < 0.01f ) ) { // simple flip modes + auto const transitiontime { ( + m_transition ? + std::min( + 1.f, + std::min( fOnTime, fOffTime ) * 0.9f ) : + 0.01f ) }; + switch( mode ) { case ls_Off: { // reduce to zero @@ -597,14 +609,14 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) { } case ls_On: { // increase to max value - timer = std::min( fTransitionTime, timer + timedelta ); + timer = std::min( transitiontime, timer + timedelta ); break; } default: { break; } } - opacity = timer / fTransitionTime; + opacity = timer / transitiontime; } else { // blink modes @@ -618,10 +630,12 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) { ( mode == ls_Off ) ? ontime : ( mode == ls_On ) ? ( fOnTime + fOffTime ) - ontime : fOffTime ) }; // fallback - auto const transitiontime { - std::min( - 1.f, - std::min( ontime, offtime ) * 0.9f ) }; + auto const transitiontime { ( + m_transition ? + std::min( + 1.f, + std::min( ontime, offtime ) * 0.9f ) : + 0.01f ) }; timer = clamp_circular( timer + timedelta * ( lsLights[ idx ] > 0.f ? 1.f : -1.f ), ontime + offtime ); // set opacity depending on blink stage @@ -958,6 +972,10 @@ TAnimModel::export_as_text_( std::ostream &Output ) const { Output << lsLights[ lightidx ] << ' '; } } + // potential light transition switch + if( false == m_transition ) { + Output << "notransition" << ' '; + } // footer Output << "endmodel" diff --git a/AnimModel.h b/AnimModel.h index feffdda4..e221f3e1 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -198,7 +198,8 @@ private: std::array m_lightopacities; // {1} in constructor float fOnTime { 1.f / 2 };// { 60.f / 45.f / 2 }; float fOffTime { 1.f / 2 };// { 60.f / 45.f / 2 }; // były stałymi, teraz mogą być zmienne dla każdego egzemplarza - float fTransitionTime { fOnTime * 0.9f }; // time +// float fTransitionTime { fOnTime * 0.9f }; // time + bool m_transition { true }; // smooth transition between light states unsigned int m_framestamp { 0 }; // id of last rendered gfx frame }; diff --git a/Camera.cpp b/Camera.cpp index bfeb103a..c777dad5 100644 --- a/Camera.cpp +++ b/Camera.cpp @@ -190,7 +190,7 @@ void TCamera::Update() auto movement { Velocity * -2.0 }; movement.y = -movement.y; if( ( m_owner->ctOwner ) - && ( m_owner->ctOwner->Vehicle()->MoverParameters->CabOccupied < 0 ) ) { + && ( m_owner->ctOwner->Vehicle()->DirectionGet() != m_owner->DirectionGet() ) ) { movement *= -1.f; movement.y = -movement.y; } diff --git a/Driver.cpp b/Driver.cpp index 236cf6ac..91f4b991 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1909,24 +1909,24 @@ void TController::Activation() mvOccupied->CabOccupied = mvOccupied->CabActive; // użytkownik moze zmienić CabOccupied wychodząc mvOccupied->CabDeactivisation(); // tak jest w Train.cpp // przejście AI na drugą stronę EN57, ET41 itp. - while (TestFlag(d->MoverParameters->Couplers[iDirection < 0 ? end::rear : end::front].CouplingFlag, coupling::control)) - { // jeśli pojazd z przodu jest ukrotniony, to przechodzimy do niego - d = iDirection * d->DirectionGet() < 0 ? d->Next() : - d->Prev(); // przechodzimy do następnego członu - if (d) - { + { + int movedirection { ( iDirection < 0 ? end::rear : end::front ) }; + d = pVehicle->FirstFind( movedirection, coupling::control ); + if( pVehicle != d ) { drugi = d->Mechanik; // zapamiętanie tego, co ewentualnie tam siedzi, żeby w razie dwóch zamienić miejscami d->Mechanik = this; // na razie bilokacja d->MoverParameters->SetInternalCommand("", 0, 0); // usunięcie ewentualnie zalegającej komendy (Change_direction?) - if (d->DirectionGet() != pVehicle->DirectionGet()) // jeśli są przeciwne do siebie - iDirection = -iDirection; // to będziemy jechać w drugą stronę względem zasiedzianego pojazdu + if( d->DirectionGet() != pVehicle->DirectionGet() ) { + // jeśli są przeciwne do siebie to będziemy jechać w drugą stronę względem zasiedzianego pojazdu + iDirection = -iDirection; + } pVehicle->Mechanik = drugi; // wsadzamy tego, co ewentualnie był (podwójna trakcja) +/* pVehicle->MoverParameters->CabActive = 0; // wyłączanie kabin po drodze pVehicle->MoverParameters->CabOccupied = 0; // i zaznaczenie, że nie ma tam nikogo +*/ pVehicle = d; // a mechu ma nowy pojazd (no, człon) } - else - break; // jak koniec składu, to mechanik dalej nie idzie } if (pVehicle != old) { // jeśli zmieniony został pojazd prowadzony @@ -2371,7 +2371,7 @@ bool TController::CheckVehicles(TOrders user) ( ( mvControlling->EngineType == TEngineType::DieselElectric ) || ( mvControlling->EngineType == TEngineType::DieselEngine ) ) ? ( Global.AirTemperature < 10 ) : true ); - } + } } else { // gdy człowiek i gdy nastąpiło połącznie albo rozłączenie // Ra 2014-02: lepiej tu niż w pętli obsługującej komendy, bo tam się zmieni informacja o składzie @@ -3197,7 +3197,7 @@ bool TController::DecBrakeEIM() { case 0: { if( mvOccupied->MED_amax != 9.81 ) { - auto const brakeposition { clamp( -1.0 * mvOccupied->AIHintLocalBrakeAccFactor * AccDesired / mvOccupied->MED_amax, 0.0, 1.0 ) }; + auto const brakeposition { clamp( -1.0 * mvOccupied->AIHintLocalBrakeAccFactor * std::max( 0.0, AccDesired ) / mvOccupied->MED_amax, 0.0, 1.0 ) }; OK = ( brakeposition != mvOccupied->LocalBrakePosA ); mvOccupied->LocalBrakePosA = brakeposition; } diff --git a/DynObj.cpp b/DynObj.cpp index 98e8b5e9..75ba03ab 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -4298,7 +4298,7 @@ void TDynamicObject::RenderSounds() { // NBMX sygnal odjazdu if( MoverParameters->Doors.has_warning ) { auto const lowvoltagepower { MoverParameters->Power24vIsAvailable || MoverParameters->Power110vIsAvailable }; - for( auto &speaker : m_speakers ) { + for( auto &doorspeaker : m_doorspeakers ) { // TBD, TODO: per-location door state triggers? if( ( MoverParameters->DepartureSignal ) && ( lowvoltagepower ) @@ -4310,30 +4310,31 @@ void TDynamicObject::RenderSounds() { ) { // for the autonomous doors play the warning automatically whenever a door is closing // MC: pod warunkiem ze jest zdefiniowane w chk - speaker.departure_signal.play( sound_flags::exclusive | sound_flags::looping ); + doorspeaker.departure_signal.play( sound_flags::exclusive | sound_flags::looping ); } else { - speaker.departure_signal.stop(); + doorspeaker.departure_signal.stop(); } } } // announcements - for( auto &speaker : m_speakers ) { + { auto const lowvoltagepower { MoverParameters->Power24vIsAvailable || MoverParameters->Power110vIsAvailable }; if( lowvoltagepower ) { - // speaker is powered up, can play queued announcements - if( speaker.announcement.is_playing() ) { continue; } - if( speaker.announcement_queue.empty() ) { continue; } - // pull first sound from the queue - speaker.announcement = speaker.announcement_queue.front(); - speaker.announcement.owner( this ); - speaker.announcement.offset( speaker.offset ); - speaker.announcement.play(); - speaker.announcement_queue.pop_front(); + // system is powered up, can play queued announcements + if( ( false == m_pasystem.announcement_queue.empty() ) + && ( false == m_pasystem.announcement.is_playing() ) ) { + // pull first sound from the queue + m_pasystem.announcement = m_pasystem.announcement_queue.front(); + m_pasystem.announcement.owner( this ); + m_pasystem.announcement.range( 0.5 * MoverParameters->Dim.L * -1 ); + m_pasystem.announcement.play(); + m_pasystem.announcement_queue.pop_front(); + } } else { - speaker.announcement.stop(); - speaker.announcement_queue.clear(); + m_pasystem.announcement.stop(); + m_pasystem.announcement_queue.clear(); } } // NBMX Obsluga drzwi, MC: zuniwersalnione @@ -5708,7 +5709,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co sound_source soundtemplate { sound_placement::general, 25.f }; soundtemplate.deserialize( parser, sound_type::multipart, sound_parameters::range ); soundtemplate.owner( this ); - for( auto &speaker : m_speakers ) { + for( auto &speaker : m_doorspeakers ) { speaker.departure_signal = soundtemplate; speaker.departure_signal.offset( speaker.offset ); } @@ -5787,11 +5788,13 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } else if( token == "unloading:" ) { + m_exchangesounds.unloading.range( MoverParameters->Dim.L * 0.5f * -1 ); m_exchangesounds.unloading.deserialize( parser, sound_type::single ); m_exchangesounds.unloading.owner( this ); } else if( token == "loading:" ) { + m_exchangesounds.loading.range( MoverParameters->Dim.L * 0.5f * -1 ); m_exchangesounds.loading.deserialize( parser, sound_type::single ); m_exchangesounds.loading.owner( this ); } @@ -5878,10 +5881,10 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co if( announcementtype == announcement_t::idle ) { continue; } - sound_source soundtemplate { sound_placement::general, EU07_SOUND_CABANNOUNCEMENTCUTOFFRANGE }; + sound_source soundtemplate { sound_placement::engine }; // NOTE: sound range gets filled by pa system soundtemplate.deserialize( announcementsound, sound_type::single ); soundtemplate.owner( this ); - m_announcements[ static_cast( announcementtype ) ] = soundtemplate; + m_pasystem.announcements[ static_cast( announcementtype ) ] = soundtemplate; } } } @@ -5933,10 +5936,9 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co door.step_open.offset( location ); m_doorsounds.emplace_back( door ); } - m_speakers.emplace_back( - speaker_sounds { + m_doorspeakers.emplace_back( + doorspeaker_sounds { { 0.f, 3.f, offset }, - {}, {} } ); } } @@ -7053,7 +7055,7 @@ material_handle TDynamicObject::DestinationFind( std::string Destination ) { void TDynamicObject::announce( announcement_t const Announcement, bool const Chime ) { - if( m_speakers.empty() ) { return; } + if( m_doorspeakers.empty() ) { return; } auto const *driver { ( ctOwner != nullptr ? @@ -7062,9 +7064,10 @@ void TDynamicObject::announce( announcement_t const Announcement, bool const Chi if( driver == nullptr ) { return; } auto const &timetable { driver->TrainTimetable() }; + auto const &announcements { m_pasystem.announcements }; auto playchime { Chime }; - if( m_announcements[ static_cast( Announcement ) ].empty() ) { + if( announcements[ static_cast( Announcement ) ].empty() ) { goto followup; } // if the announcement sound was defined queue playback @@ -7093,17 +7096,13 @@ void TDynamicObject::announce( announcement_t const Announcement, bool const Chi } // potentially precede the announcement with a chime... if( ( true == playchime ) - && ( false == m_announcements[ static_cast( announcement_t::chime ) ].empty() ) ) { - for( auto &speaker : m_speakers ) { - speaker.announcement_queue.emplace_back( m_announcements[ static_cast( announcement_t::chime ) ] ); - } + && ( false == announcements[ static_cast( announcement_t::chime ) ].empty() ) ) { + m_pasystem.announcement_queue.emplace_back( announcements[ static_cast( announcement_t::chime ) ] ); playchime = false; } // ...then play the announcement itself - for( auto &speaker : m_speakers ) { - speaker.announcement_queue.emplace_back( m_announcements[ static_cast( Announcement ) ] ); - speaker.announcement_queue.emplace_back( stopnamesound ); - } + m_pasystem.announcement_queue.emplace_back( announcements[ static_cast( Announcement ) ] ); + m_pasystem.announcement_queue.emplace_back( stopnamesound ); } followup: // potentially follow up with another announcement diff --git a/DynObj.h b/DynObj.h index e50944c0..6ec6b1da 100644 --- a/DynObj.h +++ b/DynObj.h @@ -406,9 +406,13 @@ private: void render( TMoverParameters const &Vehicle, double const Deltatime ); }; // single source per door (pair) on the centreline - struct speaker_sounds { + struct doorspeaker_sounds { glm::vec3 offset; sound_source departure_signal; + }; + // single source per vehicle + struct pasystem_sounds { + std::array( announcement_t::end )> announcements; sound_source announcement; std::deque announcement_queue; // fifo queue }; @@ -509,8 +513,8 @@ private: exchange_data m_exchange; // state of active load exchange procedure, if any exchange_sounds m_exchangesounds; // sounds associated with the load exchange - std::vector m_speakers; - std::array( announcement_t::end )> m_announcements; + std::vector m_doorspeakers; + pasystem_sounds m_pasystem; coupleradapter_data m_coupleradapter; diff --git a/Event.cpp b/Event.cpp index 98b99963..238d6f53 100644 --- a/Event.cpp +++ b/Event.cpp @@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/. #include "Event.h" #include "simulation.h" +#include "simulationtime.h" #include "messaging.h" #include "Globals.h" #include "MemCell.h" @@ -307,6 +308,10 @@ basic_event::deserialize( cParser &Input, scene::scratch_data &Scratchpad ) { Input.getTokens(); Input >> m_delayrandom; // Ra 2014-03-11 } + if( token == "departuredelay" ) { // timetable-based delay + Input.getTokens(); + Input >> m_delaydeparture; + } Input.getTokens(); } } @@ -363,6 +368,11 @@ basic_event::export_as_text( std::ostream &Output ) const { << "randomdelay " << m_delayrandom << ' '; } + if( false == std::isnan( m_delayrandom ) ) { + Output + << "departuredelay " + << m_delaydeparture << ' '; + } // footer Output << "endevent" @@ -428,9 +438,10 @@ basic_event::input_location() const { bool basic_event::is_keyword( std::string const &Token ) { - + // TODO: convert to array lookup if keyword list gets longer return ( Token == "endevent" ) - || ( Token == "randomdelay" ); + || ( Token == "randomdelay" ) + || ( Token == "departuredelay" ); } @@ -2289,11 +2300,25 @@ event_manager::AddToQuery( basic_event *Event, TDynamicObject const *Owner ) { // standardowe dodanie do kolejki ++(Event->m_inqueue); // zabezpieczenie przed podwójnym dodaniem do kolejki WriteLog( "EVENT ADDED TO QUEUE" + ( Owner ? ( " by " + Owner->asName ) : "" ) + ": " + Event->m_name ); - Event->m_launchtime = std::abs( Event->m_delay ) + Timer::GetTime(); // czas od uruchomienia scenerii + Event->m_launchtime = Timer::GetTime() + std::abs( Event->m_delay ); // czas od uruchomienia scenerii if( Event->m_delayrandom > 0.0 ) { // doliczenie losowego czasu opóźnienia Event->m_launchtime += Event->m_delayrandom * Random(); } + if( ( Owner != nullptr ) + && ( false == std::isnan( Event->m_delaydeparture ) ) ) { + auto const *timetableowner { ( + ( ( Owner->Mechanik != nullptr ) && ( Owner->Mechanik->primary() ) ) ? + Owner->Mechanik : + Owner->ctOwner ) }; + if( timetableowner != nullptr ) { + auto const &timetable { timetableowner->TrainTimetable() }; + auto const &time { simulation::Time.data() }; + Event->m_launchtime += + timetable.seconds_until_departure( time.wHour, time.wMinute + time.wSecond * 0.0167 ) + + Event->m_delaydeparture; + } + } if( QueryRootEvent != nullptr ) { basic_event *target { QueryRootEvent }; basic_event *previous { nullptr }; diff --git a/Event.h b/Event.h index 40066f10..f33dbb9e 100644 --- a/Event.h +++ b/Event.h @@ -91,6 +91,7 @@ public: double m_launchtime { 0.0 }; double m_delay { 0.0 }; double m_delayrandom { 0.0 }; // zakres dodatkowego opóźnienia // standardowo nie będzie dodatkowego losowego opóźnienia + double m_delaydeparture { std::numeric_limits::quiet_NaN() }; // departure-based event delay protected: // types diff --git a/Train.cpp b/Train.cpp index b5b255a2..222576f1 100644 --- a/Train.cpp +++ b/Train.cpp @@ -6025,7 +6025,9 @@ bool TTrain::Update( double const Deltatime ) } } // helper variables - m_doors = ( DynamicObject->Mechanik->IsAnyDoorOpen[ side::right ] || DynamicObject->Mechanik->IsAnyDoorOpen[ side::left ] ); + if( DynamicObject->Mechanik != nullptr ) { + m_doors = ( DynamicObject->Mechanik->IsAnyDoorOpen[ side::right ] || DynamicObject->Mechanik->IsAnyDoorOpen[ side::left ] ); + } m_dirforward = ( mvControlled->DirActive > 0 ); m_dirneutral = ( mvControlled->DirActive == 0 ); m_dirbackward = ( mvControlled->DirActive <0 ); @@ -6991,9 +6993,9 @@ bool TTrain::Update( double const Deltatime ) } ggLocalBrake.Update(); } - ggDirForwardButton.Update(); - ggDirNeutralButton.Update(); - ggDirBackwardButton.Update(); + ggDirForwardButton.Update( lowvoltagepower ); + ggDirNeutralButton.Update( lowvoltagepower ); + ggDirBackwardButton.Update( lowvoltagepower ); ggAlarmChain.Update(); ggBrakeProfileCtrl.Update(); ggBrakeProfileG.Update(); @@ -7001,8 +7003,8 @@ bool TTrain::Update( double const Deltatime ) ggBrakeOperationModeCtrl.Update(); ggMaxCurrentCtrl.Update(); // NBMX wrzesien 2003 - drzwi - ggDoorLeftPermitButton.Update(); - ggDoorRightPermitButton.Update(); + ggDoorLeftPermitButton.Update( lowvoltagepower ); + ggDoorRightPermitButton.Update( lowvoltagepower ); ggDoorPermitPresetButton.Update(); ggDoorLeftButton.Update(); ggDoorRightButton.Update(); @@ -7011,7 +7013,7 @@ bool TTrain::Update( double const Deltatime ) ggDoorLeftOffButton.Update(); ggDoorRightOffButton.Update(); ggDoorAllOnButton.Update(); - ggDoorAllOffButton.Update(); + ggDoorAllOffButton.Update( lowvoltagepower ); ggDoorSignallingButton.Update(); // NBMX dzwignia sprezarki ggCompressorButton.Update(); @@ -7534,6 +7536,7 @@ void TTrain::update_sounds_radio() { for( auto &message : m_radiomessages ) { auto const volume { ( true == radioenabled ) + && ( Dynamic()->Mechanik != nullptr ) && ( message.first == RadioChannel() ) ? Global.RadioVolume : 0.0 }; @@ -8125,24 +8128,13 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) Math3D::vector3 TTrain::MirrorPosition(bool lewe) { // zwraca współrzędne widoku kamery z lusterka - switch (iCabn) - { - case 2: // tylna (-1) - return DynamicObject->mMatrix * - Math3D::vector3( - mvOccupied->Dim.W * ( lewe ? -0.5 : 0.5 ) + 0.2 * ( lewe ? -1 : 1 ), - 1.5 + Cabine[iCabn].CabPos1.y, - Cabine[iCabn].CabPos1.z); - case 1: // przednia (1) - [[fallthrough]]; - default: - return DynamicObject->mMatrix * - Math3D::vector3( - mvOccupied->Dim.W * ( lewe ? 0.5 : -0.5 ) + 0.2 * ( lewe ? 1 : -1 ), - 1.5 + Cabine[iCabn].CabPos1.y, - Cabine[iCabn].CabPos2.z); - } -// return DynamicObject->GetPosition(); // współrzędne środka pojazdu + auto const shiftdirection { ( lewe ? -1 : 1 ) * ( iCabn == 2 ? 1 : -1 ) }; + + return DynamicObject->mMatrix + * Math3D::vector3( + mvOccupied->Dim.W * ( 0.5 * shiftdirection ) + ( 0.2 * shiftdirection ), + 1.5 + Cabine[iCabn].CabPos1.y, + interpolate( Cabine[ iCabn ].CabPos1.z , Cabine[ iCabn ].CabPos2.z, 0.5 ) ); }; void TTrain::DynamicSet(TDynamicObject *d) @@ -8337,6 +8329,7 @@ TTrain::radio_message( sound_source *Message, int const Channel ) { auto const radioenabled { ( true == mvOccupied->Radio ) && ( mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable ) }; auto const volume { ( true == radioenabled ) + && ( Dynamic()->Mechanik != nullptr ) && ( Channel == RadioChannel() ) ? 1.0 : 0.0 }; diff --git a/audiorenderer.cpp b/audiorenderer.cpp index aac97574..e4412b41 100644 --- a/audiorenderer.cpp +++ b/audiorenderer.cpp @@ -135,8 +135,9 @@ openal_source::sync_with( sound_properties const &State ) { // location properties.location = State.location; sound_distance = properties.location - glm::dvec3 { Global.pCamera.Pos }; - if( sound_range > 0 ) { - // range cutoff check + if( sound_range != -1 ) { + // range cutoff check for songs other than 'unlimited' + // NOTE: since we're comparing squared distances we can ignore that sound range can be negative auto const cutoffrange = ( is_multipart ? EU07_SOUND_CUTOFFRANGE : // we keep multi-part sounds around longer, to minimize restarts as the sounds get out and back in range @@ -151,7 +152,7 @@ openal_source::sync_with( sound_properties const &State ) { ::alSourcefv( id, AL_POSITION, glm::value_ptr( sound_distance ) ); } else { - // sounds with 'unlimited' range are positioned on top of the listener + // sounds with 'unlimited' or negative range are positioned on top of the listener ::alSourcefv( id, AL_POSITION, glm::value_ptr( glm::vec3() ) ); } // gain @@ -168,7 +169,7 @@ openal_source::sync_with( sound_properties const &State ) { 5 ) }; // range of -1 means sound of unlimited range, positioned at the listener ::alSourcef( id, AL_REFERENCE_DISTANCE, range * ( 1.f / 16.f ) * properties.soundproofing ); } - if( sound_range > 0 ) { + if( sound_range != -1 ) { auto const rangesquared { sound_range * sound_range }; auto const distancesquared { glm::length2( sound_distance ) }; if( ( distancesquared > rangesquared ) @@ -428,7 +429,7 @@ openal_renderer::fetch_source() { continue; } auto const sourceweight { ( - source->sound_range > 0 ? + source->sound_range != -1 ? ( source->sound_range * source->sound_range ) / ( glm::length2( source->sound_distance ) + 1 ) : std::numeric_limits::max() ) }; if( sourceweight < leastimportantweight ) { diff --git a/mtable.cpp b/mtable.cpp index 3167006e..3e6714f4 100644 --- a/mtable.cpp +++ b/mtable.cpp @@ -45,7 +45,7 @@ std::string TTrainParameters::NextStop() const sound_source TTrainParameters::next_stop_sound() const { if( StationIndex > StationCount ) { - return { sound_placement::general }; + return { sound_placement::engine }; } for( auto stationidx { StationIndex }; stationidx < StationCount + 1; ++stationidx ) { auto &station{ TimeTable[ stationidx ] }; @@ -56,7 +56,7 @@ TTrainParameters::next_stop_sound() const { return station.name_sound; } // shouldn't normally get here, unless the timetable is malformed - return { sound_placement::general }; + return { sound_placement::engine }; } sound_source @@ -98,7 +98,7 @@ sound_source TTrainParameters::current_stop_sound() const { if( ( StationIndex <= StationCount ) ) return TimeTable[ StationIndex ].name_sound; else - return { sound_placement::general }; + return { sound_placement::engine }; } bool TTrainParameters::UpdateMTable( scenario_time const &Time, std::string const &NewName ) { @@ -181,6 +181,15 @@ bool TTrainParameters::IsTimeToGo(double hh, double mm) return false; // dalej nie jechać } +// returns: difference between specified time and scheduled departure from current stop, in seconds +double TTrainParameters::seconds_until_departure( double const Hour, double const Minute ) const { + + if( ( TimeTable[ StationIndex ].Ah < 0 ) ) { // passthrough + return 0; + } + return ( 60.0 * CompareTime( Hour, Minute, TimeTable[ StationIndex ].Dh, TimeTable[ StationIndex ].Dm ) ); +} + std::string TTrainParameters::ShowRelation() const /*zwraca informację o relacji*/ { @@ -595,7 +604,7 @@ TTrainParameters::load_sounds() { } // wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie) station.name_sound = - sound_source{ sound_placement::general, EU07_SOUND_CABANNOUNCEMENTCUTOFFRANGE } + sound_source{ sound_placement::engine, EU07_SOUND_CABANNOUNCEMENTCUTOFFRANGE } .deserialize( lookup.first + lookup.second, sound_type::single ); } } diff --git a/mtable.h b/mtable.h index aeb05643..353af85f 100644 --- a/mtable.h +++ b/mtable.h @@ -39,7 +39,7 @@ struct TMTableLine float tm{ 0.f }; // czas jazdy do tej stacji w min. (z kolumny) bool is_maintenance{ false }; int radio_channel{ -1 }; - sound_source name_sound{ sound_placement::internal }; + sound_source name_sound{ sound_placement::engine }; }; typedef TMTableLine TMTable[MaxTTableSize + 1]; @@ -73,6 +73,8 @@ class TTrainParameters bool IsLastStop() const; bool IsMaintenance() const; bool IsTimeToGo(double hh, double mm); + // returns: difference between specified time and scheduled departure from current stop, in seconds + double seconds_until_departure( double const Hour, double const Minute ) const; bool UpdateMTable(double hh, double mm, std::string const &NewName); bool UpdateMTable( scenario_time const &Time, std::string const &NewName ); bool RewindTimeTable( std::string actualStationName ); diff --git a/sound.cpp b/sound.cpp index 67fa09bf..ed2a1b73 100644 --- a/sound.cpp +++ b/sound.cpp @@ -341,8 +341,8 @@ sound_source::play( int const Flags ) { // NOTE: we cache the flags early, even if the sound is out of range, to mark activated event sounds m_flags = Flags; - if( m_range > 0 ) { - auto const cutoffrange { m_range * 5 }; + if( m_range != -1 ) { + auto const cutoffrange { std::abs( m_range * 5 ) }; if( glm::length2( location() - glm::dvec3 { Global.pCamera.Pos } ) > std::min( 2750.f * 2750.f, cutoffrange * cutoffrange ) ) { // while we drop sounds from beyond sensible and/or audible range // we act as if it was activated normally, meaning no need to include the opening bookend in subsequent calls @@ -887,6 +887,12 @@ sound_source::location() const { + m_owner->VectorFront() * m_offset.z }; } +void +sound_source::range( float const Range ) { + + m_range = Range; +} + // returns defined range of the sound float const sound_source::range() const { @@ -920,24 +926,31 @@ sound_source::update_location() { m_properties.location = location(); } -float const EU07_SOUNDPROOFING_STRONG { 0.5f }; // 0.25 after squaring -float const EU07_SOUNDPROOFING_SOME { 0.8f }; // ~0.65 after squaring +float const EU07_SOUNDPROOFING_GLOBAL_VERYSTRONG { 0.01f }; +float const EU07_SOUNDPROOFING_GLOBAL_STRONG { std::sqrtf( 0.025 ) }; +float const EU07_SOUNDPROOFING_GLOBAL_SOME { std::sqrtf( 0.15 ) }; +float const EU07_SOUNDPROOFING_GLOBAL_NONE { std::sqrtf( 0.40 ) }; +float const EU07_SOUNDPROOFING_STRONG { std::sqrtf( 0.20 ) }; +float const EU07_SOUNDPROOFING_SOME { std::sqrtf( 0.65 ) }; float const EU07_SOUNDPROOFING_NONE { 1.f }; bool sound_source::update_soundproofing() { - // NOTE, HACK: current cab id can vary from -1 to +1, and we use another higher priority value for open cab window + // NOTE, HACK: current cab id can vary from -1 to +1, and we use higher priority values for open cab window and external views // we use this as modifier to force re-calculations when moving between compartments or changing window state int const occupiedcab = ( Global.CabWindowOpen ? 2 : - FreeFlyModeFlag ? 0 : + FreeFlyModeFlag ? ( + Global.pCamera.m_owner ? + 3 : + 0 ) : ( simulation::Train ? simulation::Train->Occupied()->CabOccupied : 0 ) ); // location-based gain factor: std::uintptr_t soundproofingstamp = reinterpret_cast( ( FreeFlyModeFlag ? - nullptr : + Global.pCamera.m_owner : ( simulation::Train ? simulation::Train->Dynamic() : nullptr ) ) ) @@ -948,36 +961,72 @@ sound_source::update_soundproofing() { // listener location has changed, calculate new location-based gain factor switch( m_placement ) { case sound_placement::general: { - m_properties.soundproofing = EU07_SOUNDPROOFING_NONE; + m_properties.soundproofing = ( + m_range >= -1 ? + EU07_SOUNDPROOFING_NONE : + EU07_SOUNDPROOFING_GLOBAL_NONE ); break; } case sound_placement::external: { - m_properties.soundproofing = ( - ( ( soundproofingstamp == 0 ) || ( true == Global.CabWindowOpen ) ) ? - EU07_SOUNDPROOFING_NONE : // listener outside or has a window open - EU07_SOUNDPROOFING_STRONG ); // listener in a vehicle with windows shut + if( m_range >= -1 ) { // limited range sound... + m_properties.soundproofing = ( + ( soundproofingstamp == 0 ? EU07_SOUNDPROOFING_NONE : // ...and listener outside + true == Global.CabWindowOpen ? EU07_SOUNDPROOFING_SOME : // ...and window open + EU07_SOUNDPROOFING_STRONG ) ); // ...and window closed + } + else { // global sound... + auto const externalcamera { ( Global.CabWindowOpen ) || ( Global.pCamera.m_owner && FreeFlyModeFlag ) }; + m_properties.soundproofing = ( + ( soundproofingstamp == 0 ? EU07_SOUNDPROOFING_GLOBAL_NONE : // ...and listener outside + externalcamera ? EU07_SOUNDPROOFING_GLOBAL_STRONG : // ...and window open + EU07_SOUNDPROOFING_GLOBAL_VERYSTRONG ) ); // ...and window closed + } break; } case sound_placement::internal: { - m_properties.soundproofing = ( - soundproofingstamp == 0 ? - EU07_SOUNDPROOFING_STRONG : // listener outside HACK: won't be true if active vehicle has open window - ( simulation::Train->Dynamic() != m_owner ? - EU07_SOUNDPROOFING_STRONG : // in another vehicle - ( occupiedcab == 0 ? - EU07_SOUNDPROOFING_STRONG : // listener in the engine compartment - EU07_SOUNDPROOFING_NONE ) ) ); // listener in the cab of the same vehicle + if( m_range >= -1 ) { + m_properties.soundproofing = ( + soundproofingstamp == 0 ? + EU07_SOUNDPROOFING_STRONG : // listener outside HACK: won't be true if active vehicle has open window + ( simulation::Train->Dynamic() != m_owner ? + EU07_SOUNDPROOFING_STRONG : // in another vehicle + ( occupiedcab == 0 ? + EU07_SOUNDPROOFING_STRONG : // listener in the engine compartment + EU07_SOUNDPROOFING_NONE ) ) ); // listener in the cab of the same vehicle + } + else { + m_properties.soundproofing = ( + soundproofingstamp == 0 ? + EU07_SOUNDPROOFING_GLOBAL_STRONG : // listener outside HACK: won't be true if active vehicle has open window + ( simulation::Train->Dynamic() != m_owner ? + EU07_SOUNDPROOFING_GLOBAL_VERYSTRONG : // in another vehicle + ( occupiedcab == 0 ? + EU07_SOUNDPROOFING_GLOBAL_NONE : // listener in the engine compartment + EU07_SOUNDPROOFING_GLOBAL_STRONG ) ) ); // listener in the cab of the same vehicle + } break; } case sound_placement::engine: { - m_properties.soundproofing = ( - ( ( soundproofingstamp == 0 ) || ( true == Global.CabWindowOpen ) ) ? - EU07_SOUNDPROOFING_SOME : // listener outside or has a window open - ( simulation::Train->Dynamic() != m_owner ? - EU07_SOUNDPROOFING_STRONG : // in another vehicle - ( occupiedcab == 0 ? - EU07_SOUNDPROOFING_NONE : // listener in the engine compartment - EU07_SOUNDPROOFING_STRONG ) ) ); // listener in another compartment of the same vehicle + if( m_range >= -1 ) { + m_properties.soundproofing = ( + ( ( soundproofingstamp == 0 ) || ( true == Global.CabWindowOpen ) ) ? + EU07_SOUNDPROOFING_SOME : // listener outside or has a window open + ( simulation::Train->Dynamic() != m_owner ? + EU07_SOUNDPROOFING_STRONG : // in another vehicle + ( occupiedcab == 0 ? + EU07_SOUNDPROOFING_NONE : // listener in the engine compartment + EU07_SOUNDPROOFING_STRONG ) ) ); // listener in another compartment of the same vehicle + } + else { + m_properties.soundproofing = ( + ( ( soundproofingstamp == 0 ) || ( true == Global.CabWindowOpen ) ) ? + EU07_SOUNDPROOFING_GLOBAL_STRONG : // listener outside or has a window open + ( simulation::Train->Dynamic() != m_owner ? + EU07_SOUNDPROOFING_GLOBAL_VERYSTRONG : // in another vehicle + ( occupiedcab == 0 ? + EU07_SOUNDPROOFING_NONE : // listener in the engine compartment + EU07_SOUNDPROOFING_GLOBAL_STRONG ) ) ); // listener in another compartment of the same vehicle + } break; } default: { diff --git a/sound.h b/sound.h index 69f61f5c..c6b5d0da 100644 --- a/sound.h +++ b/sound.h @@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/. float const EU07_SOUND_GLOBALRANGE { -1.f }; float const EU07_SOUND_CABCONTROLSCUTOFFRANGE { 7.5f }; -float const EU07_SOUND_CABANNOUNCEMENTCUTOFFRANGE{ 30.f }; +float const EU07_SOUND_CABANNOUNCEMENTCUTOFFRANGE{ -10.f }; float const EU07_SOUND_BRAKINGCUTOFFRANGE { 100.f }; float const EU07_SOUND_RUNNINGNOISECUTOFFRANGE { 200.f }; float const EU07_SOUND_HANDHELDRADIORANGE { 3500.f }; @@ -122,6 +122,8 @@ public: glm::dvec3 const location() const; // returns defined range of the sound + void + range( float const Range ); float const range() const;