16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-22 22:09:19 +02:00

build 200728. basic train passenger information system, event condition diagnostics enhancement, ai door control logic fix

This commit is contained in:
tmj-fstate
2020-07-29 03:29:02 +02:00
parent 138b793f5a
commit e74a614385
15 changed files with 341 additions and 68 deletions

View File

@@ -986,6 +986,12 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
if( mvOccupied->Vel > 0.3 ) {
// jeśli jedzie (nie trzeba czekać, aż się drgania wytłumią - drzwi zamykane od 1.0) to będzie zatrzymanie
sSpeedTable[ i ].fVelNext = 0;
// potentially announce pending stop
if( ( m_lastannouncement != announcement_t::approaching )
&& ( sSpeedTable[ i ].fDist < 750 )
&& ( sSpeedTable[ i ].fDist > 250 ) ) {
announce( announcement_t::approaching );
}
} else if( true == IsAtPassengerStop ) {
// jeśli się zatrzymał przy W4, albo stał w momencie zobaczenia W4
if( !AIControllFlag ) {
@@ -1009,18 +1015,12 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
auto const platformside = static_cast<int>( std::floor( std::abs( sSpeedTable[ i ].evEvent->input_value( 2 ) ) ) ) % 10;
auto const exchangetime = simulation::Station.update_load( pVehicles[ 0 ], TrainParams, platformside );
WaitingSet( exchangetime );
// announce the stop name while at it
announce( announcement_t::current );
m_lastexchangestop = asNextStop;
m_makenextstopannouncement = true;
}
if( false == TrainParams.IsMaintenance() )
// && ( ( iDrivigFlags & moveDoorOpened ) == 0 ) )
{
// drzwi otwierać jednorazowo
iDrivigFlags |= moveDoorOpened; // nie wykonywać drugi raz
Doors( true, static_cast<int>( std::floor( std::abs( sSpeedTable[ i ].evEvent->input_value( 2 ) ) ) ) % 10 );
}
if (TrainParams.DirectionChange()) {
// jeśli "@" w rozkładzie, to wykonanie dalszych komend
// wykonanie kolejnej komendy, nie dotyczy ostatniej stacji
@@ -1053,6 +1053,13 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
}
}
if( ( false == TrainParams.IsMaintenance() )
&& ( ( false == TestFlag( iDrivigFlags, moveDoorOpened ) )
|| ( true == DoesAnyDoorNeedOpening ) ) ) {
iDrivigFlags |= moveDoorOpened; // nie wykonywać drugi raz
Doors( true, static_cast<int>( std::floor( std::abs( sSpeedTable[ i ].evEvent->input_value( 2 ) ) ) ) % 10 );
}
if (OrderCurrentGet() & ( Shunt | Loose_shunt )) {
OrderNext(Obey_train); // uruchomić jazdę pociągową
CheckVehicles(); // zmienić światła
@@ -4184,6 +4191,23 @@ TController::doors_permit_active() const {
|| IsAnyDoorPermitActive[ side::left ] );
}
void
TController::announce( announcement_t const Announcement ) {
m_lastannouncement = Announcement;
if( IsCargoTrain ) {
// cargo trains aren't likely to have announcement systems, so we can skip the procedure altogether
return;
}
auto *vehicle { pVehicles[ end::front ] };
while( vehicle ) {
vehicle->announce( Announcement );
vehicle = vehicle->Next();
}
}
void TController::RecognizeCommand()
{ // odczytuje i wykonuje komendę przekazaną lokomotywie
TCommand *c = &mvOccupied->CommandIn;
@@ -4268,6 +4292,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
TrainParams.StationIndexInc(); // przejście do następnej
iStationStart = TrainParams.StationIndex;
asNextStop = TrainParams.NextStop();
m_lastannouncement = announcement_t::idle;
iDrivigFlags |= movePrimary; // skoro dostał rozkład, to jest teraz głównym
// NewCommand = Global.asCurrentSceneryPath + NewCommand;
auto lookup =
@@ -4294,7 +4319,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
NewCommand = TrainParams.Relation2; // relacja docelowa z rozkładu
}
// jeszcze poustawiać tekstury na wyświetlaczach
TDynamicObject *p = pVehicles[0];
TDynamicObject *p = pVehicles[end::front];
while (p)
{
p->DestinationSet(NewCommand, TrainParams.TrainName); // relacja docelowa
@@ -4726,6 +4751,10 @@ TController::UpdateSituation(double dt) {
// zaktualizować wyświetlanie rozkładu
iStationStart = TrainParams.StationIndex;
fLastStopExpDist = -1.0; // usunąć licznik
if( true == m_makenextstopannouncement ) {
announce( announcement_t::next );
m_makenextstopannouncement = false; // keep next stop announcements suppressed until another scheduled stop
}
}
// ABu-160305 testowanie gotowości do jazdy
@@ -5960,6 +5989,7 @@ TController::UpdateSituation(double dt) {
VelforDriver );
}
// recalculate potential load exchange duration
DoesAnyDoorNeedOpening = false;
ExchangeTime = 0.f;
if( fStopTime < 0 ) {
// czas postoju przed dalszą jazdą (np. na przystanku)
@@ -5967,7 +5997,9 @@ TController::UpdateSituation(double dt) {
// verify progress of load exchange
auto *vehicle { pVehicles[ 0 ] };
while( vehicle != nullptr ) {
ExchangeTime = std::max( ExchangeTime, vehicle->LoadExchangeTime() );
auto const vehicleexchangetime { vehicle->LoadExchangeTime() };
DoesAnyDoorNeedOpening |= ( ( vehicleexchangetime > 0 ) && ( vehicle->LoadExchangeSpeed() == 0 ) );
ExchangeTime = std::max( ExchangeTime, vehicleexchangetime );
vehicle = vehicle->Next();
}
if( ( ExchangeTime > 0 )

View File

@@ -454,6 +454,8 @@ private:
int iRadioChannel = 1; // numer aktualnego kanału radiowego
int iGuardRadio = 0; // numer kanału radiowego kierownika (0, gdy nie używa radia)
sound_source tsGuardSignal{ sound_placement::internal };
announcement_t m_lastannouncement{ announcement_t::idle };
bool m_makenextstopannouncement{ false };
// consist
// methods
@@ -468,6 +470,7 @@ private:
bool doors_permit_active() const;
void AutoRewident(); // ustawia hamulce w składzie
void UpdatePantographs();
void announce( announcement_t const Announcement );
// members
double fLength = 0.0; // długość składu (do wyciągania z ograniczeń)
double fMass = 0.0; // całkowita masa do liczenia stycznej składowej grawitacji
@@ -482,6 +485,7 @@ private:
bool Ready = false; // ABu: stan gotowosci do odjazdu - sprawdzenie odhamowania wagonow
double ConsistShade{ 1.0 }; // averaged amount of sunlight received by the consist
TDynamicObject *pVehicles[ 2 ]; // skrajne pojazdy w składzie (niekoniecznie bezpośrednio sterowane)
bool DoesAnyDoorNeedOpening{ false };
bool IsAnyDoorOpen[ 2 ]; // state of door in the consist
bool IsAnyDoorPermitActive[ 2 ]; // state of door permit in the consist
bool IsAnyLineBreakerOpen{ false }; // state of line breaker in all powered vehicles under control

View File

@@ -29,6 +29,7 @@ http://mozilla.org/MPL/2.0/.
#include "renderer.h"
#include "uitranscripts.h"
#include "messaging.h"
#include "Driver.h"
// Ra: taki zapis funkcjonuje lepiej, ale może nie jest optymalny
#define vWorldFront Math3D::vector3(0, 0, 1)
@@ -2022,7 +2023,7 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
}
} // koniec hamulce
else if( ( ActPar.size() >= 3 )
&& ( ActPar[ 0 ] == 'W' ) ) {
&& ( ActPar.front() == 'W' ) ) {
// wheel
ActPar.erase( 0, 1 );
@@ -2032,7 +2033,7 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
while( false == ActPar.empty() ) {
// TODO: convert this whole copy and paste mess to something more elegant one day
switch( ActPar[ 0 ] ) {
switch( ActPar.front() ) {
case 'F': {
// fixed flat size
auto const indexstart { 1 };
@@ -2078,14 +2079,14 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
}
} // wheel
else if( ( ActPar.size() >= 2 )
&& ( ActPar[ 0 ] == 'T' ) ) {
&& ( ActPar.front() == 'T' ) ) {
// temperature
ActPar.erase( 0, 1 );
auto setambient { false };
while( false == ActPar.empty() ) {
switch( ActPar[ 0 ] ) {
switch( ActPar.front() ) {
case 'A': {
// cold start, set all temperatures to ambient level
setambient = true;
@@ -4304,7 +4305,7 @@ void TDynamicObject::RenderSounds() {
// NBMX sygnal odjazdu
if( MoverParameters->Doors.has_warning ) {
auto const lowvoltagepower { MoverParameters->Power24vIsAvailable || MoverParameters->Power110vIsAvailable };
for( auto &departuresignalsound : m_departuresignalsounds ) {
for( auto &speaker : m_speakers ) {
// TBD, TODO: per-location door state triggers?
if( ( MoverParameters->DepartureSignal )
&& ( lowvoltagepower )
@@ -4316,13 +4317,32 @@ void TDynamicObject::RenderSounds() {
) {
// for the autonomous doors play the warning automatically whenever a door is closing
// MC: pod warunkiem ze jest zdefiniowane w chk
departuresignalsound.play( sound_flags::exclusive | sound_flags::looping );
speaker.departure_signal.play( sound_flags::exclusive | sound_flags::looping );
}
else {
departuresignalsound.stop();
speaker.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();
}
else {
speaker.announcement.stop();
speaker.announcement_queue.clear();
}
}
// NBMX Obsluga drzwi, MC: zuniwersalnione
std::array<side, 2> const sides { side::right, side::left };
for( auto const side : sides ) {
@@ -4747,15 +4767,13 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
parser.getTokens();
parser >> asModel;
replace_slashes( asModel );
if( asModel[asModel.size() - 1] == '#' ) // Ra 2015-01: nie podoba mi siê to
if( asModel.back() == '#' ) // Ra 2015-01: nie podoba mi siê to
{ // model wymaga wielu tekstur wymiennych
m_materialdata.multi_textures = 1;
asModel.erase( asModel.length() - 1 );
}
// name can contain leading slash, erase it to avoid creation of double slashes when the name is combined with current directory
if( asModel[ 0 ] == '/' ) {
asModel.erase( 0, 1 );
}
erase_leading_slashes( asModel );
/*
// never really used, may as well get rid of it
std::size_t i = asModel.find( ',' );
@@ -4871,7 +4889,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
// value can be optionally set of values enclosed in "[]" in which case one value will be picked randomly
while( ( ( token = parser.getToken<std::string>() ) != "" )
&& ( token != "}" ) ) {
if( token[ token.size() - 1 ] == ':' ) {
if( token.back() == ':' ) {
auto loadmodel { deserialize_random_set( parser ) };
replace_slashes( loadmodel );
LoadModelOverrides.emplace( token.erase( token.size() - 1 ), loadmodel );
@@ -5694,14 +5712,12 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
else if( token == "departuresignal:" ) {
// pliki z sygnalem odjazdu
sound_source soundtemplate { sound_placement::general };
sound_source soundtemplate { sound_placement::general, 25.f };
soundtemplate.deserialize( parser, sound_type::multipart, sound_parameters::range );
soundtemplate.owner( this );
for( auto &departuresignalsound : m_departuresignalsounds ) {
// apply configuration to all defined doors, but preserve their individual offsets
auto const soundoffset { departuresignalsound.offset() };
departuresignalsound = soundtemplate;
departuresignalsound.offset( soundoffset );
for( auto &speaker : m_speakers ) {
speaker.departure_signal = soundtemplate;
speaker.departure_signal.offset( speaker.offset );
}
}
@@ -5844,6 +5860,38 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
m_wheelflat.owner( this );
}
else if(token == "announcements:") {
// announcement sounds
// content provided as "key: value" pairs together enclosed in "{}"
// value can be optionally set of values enclosed in "[]" in which case one value will be picked randomly
std::array<sound_source, static_cast<int>( announcement_t::end )> announcementsounds;
std::unordered_map<std::string, announcement_t> const announcements = {
{ "near_stop:", announcement_t::approaching },
{ "stop:", announcement_t::current },
{ "next_stop:", announcement_t::next },
{ "destination:", announcement_t::destination } };
while( ( ( token = parser.getToken<std::string>() ) != "" )
&& ( token != "}" ) ) {
if( token.back() == ':' ) {
auto const lookup { announcements.find( token ) };
auto const announcementtype { (
lookup != announcements.end() ?
lookup->second :
announcement_t::idle ) };
// NOTE: we retrieve key value for all keys, not just recognized ones
auto announcementsound{ deserialize_random_set( parser ) };
replace_slashes( announcementsound );
if( announcementtype == announcement_t::idle ) {
continue;
}
sound_source soundtemplate { sound_placement::general, EU07_SOUND_CABANNOUNCEMENTCUTOFFRANGE };
soundtemplate.deserialize( announcementsound, sound_type::single );
soundtemplate.owner( this );
m_announcements[ static_cast<int>( announcementtype ) ] = soundtemplate;
}
}
}
} while( ( token != "" )
&& ( token != "endsounds" ) );
@@ -5891,10 +5939,11 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
door.step_open.offset( location );
m_doorsounds.emplace_back( door );
}
// potential departure sound, one per door (pair) on vehicle centreline
sound_source departuresignalsound { sound_placement::general, 25.f };
departuresignalsound.offset( glm::vec3{ 0.f, 3.f, offset } );
m_departuresignalsounds.emplace_back( departuresignalsound );
m_speakers.emplace_back(
speaker_sounds {
{ 0.f, 3.f, offset },
{},
{} } );
}
}
@@ -7009,6 +7058,57 @@ material_handle TDynamicObject::DestinationFind( std::string Destination ) {
return destinationhandle;
}
void TDynamicObject::announce( announcement_t const Announcement ) {
if( m_speakers.empty() ) { return; }
auto const *driver { (
ctOwner != nullptr ?
ctOwner :
Mechanik ) };
if( driver == nullptr ) { return; }
auto const &timetable { driver->TrainTimetable() };
if( m_announcements[ static_cast<int>( Announcement ) ].empty() ) {
goto followup;
}
// if the announcement sound was defined queue playback
{
sound_source stopnamesound;
switch( Announcement ) {
case announcement_t::approaching:
case announcement_t::next: {
stopnamesound = timetable.next_stop_sound();
break;
}
case announcement_t::current: {
stopnamesound = timetable.current_stop_sound();
break;
}
case announcement_t::destination: {
stopnamesound = timetable.last_stop_sound();
break;
}
default: {
break;
}
}
if( stopnamesound.empty() ) {
goto followup;
}
for( auto &speaker : m_speakers ) {
speaker.announcement_queue.emplace_back( m_announcements[ static_cast<int>( Announcement ) ] );
speaker.announcement_queue.emplace_back( stopnamesound );
}
}
followup:
// potentially follow up with another announcement
if( Announcement == announcement_t::next ) {
announce( announcement_t::destination );
}
}
void TDynamicObject::OverheadTrack(float o)
{ // ewentualne wymuszanie jazdy
// bezprądowej z powodu informacji

View File

@@ -150,6 +150,15 @@ public:
//---------------------------------------------------------------------------
enum class announcement_t : int {
idle = 0,
approaching,
current,
next,
destination,
end
};
// parameters for the material object, as currently used by various simulator models
struct material_data {
@@ -395,6 +404,13 @@ private:
void position( glm::vec3 const Location );
void render( TMoverParameters const &Vehicle, double const Deltatime );
};
// single source per door (pair) on the centreline
struct speaker_sounds {
glm::vec3 offset;
sound_source departure_signal;
sound_source announcement;
std::deque<sound_source> announcement_queue; // fifo queue
};
// methods
void ABuLittleUpdate(double ObjSqrDist);
@@ -476,7 +492,6 @@ private:
std::array<coupler_sounds, 2> m_couplersounds; // always front and rear
std::vector<pantograph_sounds> m_pantographsounds; // typically 2 but can be less (or more?)
std::vector<door_sounds> m_doorsounds; // can expect symmetrical arrangement, but don't count on it
std::vector<sound_source> m_departuresignalsounds; // single source per door (pair) on the centreline
bool m_doorlocks { false }; // sound helper, current state of door locks
sound_source sHorn1 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE };
sound_source sHorn2 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE };
@@ -493,6 +508,9 @@ 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<speaker_sounds> m_speakers;
std::array<sound_source, static_cast<int>( announcement_t::end )> m_announcements;
coupleradapter_data m_coupleradapter;
bool renderme; // yB - czy renderowac
@@ -702,6 +720,7 @@ private:
void OverheadTrack(float o);
glm::dvec3 get_future_movement() const;
void move_set(double distance);
void announce( announcement_t const Announcement );
double MED[9][8]; // lista zmiennych do debugowania hamulca ED
static std::string const MED_labels[ 8 ];

View File

@@ -74,18 +74,51 @@ basic_event::event_conditions::test() const {
}
}
if( flags & flags::track_busy ) {
auto trackbusyresult { true };
std::string trackbusylog { "Test: Track busy - " };
for( auto *track : tracks ) {
if( true == track->IsEmpty() ) {
return false;
trackbusylog += "[" + track->name() + "]";
trackbusyresult = false;
break;
}
else {
auto const &vehicles { track->Dynamics };
if( trackbusylog.back() == ']' ) {
trackbusylog += ", ";
}
trackbusylog += "[" + vehicles.front()->asName + "] @ [" + track->name() + "]";
}
}
WriteLog(
trackbusylog
+ " - "
+ ( trackbusyresult ? "Pass" : "Fail" ) );
if( false == trackbusyresult ) {
return false;
}
}
if( flags & flags::track_free ) {
auto trackfreeresult { true };
std::string trackfreelog{ "Test: Track free - " };
for( auto *track : tracks ) {
if( false == track->IsEmpty() ) {
return false;
auto const &vehicles { track->Dynamics };
trackfreelog += "[" + vehicles.front()->asName + "] @ [" + track->name() + "] - ";
trackfreeresult = false;
break;
}
}
WriteLog(
trackfreelog
+ ( trackfreeresult ? "Pass" : "Fail" ) );
if( false == trackfreeresult ) {
return false;
}
}
if( flags & ( flags::text | flags::value1 | flags::value2 ) ) {
// porównanie wartości

View File

@@ -9146,7 +9146,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath)
continue;
}
if( inputline[ 0 ] == ' ' ) {
if( !inputline.empty() && inputline.front() == ' ' ) {
// guard against malformed config files with leading spaces
inputline.erase( 0, inputline.find_first_not_of( ' ' ) );
}

View File

@@ -511,7 +511,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
// zapewni to jakąś zgodność wstecz, bo zamiast liczby będzie ciąg, którego
// wartość powinna być uznana jako zerowa
// parser.getToken(iNumVerts);
if (token[0] == '*')
if (token.front() == '*')
{ // jeśli pierwszy znak jest gwiazdką, poszukać
// submodelu o nazwie bez tej gwiazdki i wziąć z
// niego wierzchołki

View File

@@ -1181,7 +1181,7 @@ texture_manager::create( std::string Filename, bool const Loadnow, GLint Formath
replace_slashes( Filename );
erase_leading_slashes( Filename );
// temporary code for legacy assets -- textures with names beginning with # are to be sharpened
if( ( Filename[ 0 ] == '#' )
if( ( Filename.front() == '#' )
|| ( Filename.find( "/#" ) != std::string::npos ) ) {
traits += '#';
}

View File

@@ -7926,9 +7926,7 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
// szukaj kabinę jako oddzielny model
// name can contain leading slash, erase it to avoid creation of double slashes when the name is combined with current directory
replace_slashes( token );
if( token[ 0 ] == '/' ) {
token.erase( 0, 1 );
}
erase_leading_slashes( token );
TModel3d *kabina = TModelsManager::GetModel(DynamicObject->asBaseDir + token, true);
// z powrotem defaultowa sciezka do tekstur
Global.asCurrentTexturePath = szTexturePath;

View File

@@ -42,28 +42,65 @@ std::string TTrainParameters::NextStop() const
return "[End of route]"; //że niby koniec
}
sound_source
TTrainParameters::next_stop_sound() const {
if( StationIndex > StationCount ) {
return { sound_placement::general };
}
for( auto stationidx { StationIndex }; stationidx < StationCount + 1; ++stationidx ) {
auto &station{ TimeTable[ stationidx ] };
if( station.Ah == -1 ) {
continue;
}
// specified arrival time means it's a scheduled stop
return station.name_sound;
}
// shouldn't normally get here, unless the timetable is malformed
return { sound_placement::general };
}
sound_source
TTrainParameters::last_stop_sound() const {
return TimeTable[ StationCount ].name_sound;
}
bool TTrainParameters::IsStop() const
{ // zapytanie, czy zatrzymywać na następnym punkcie rozkładu
if ((StationIndex < StationCount))
if ((StationIndex <= StationCount))
return TimeTable[StationIndex].Ah >= 0; //-1 to brak postoju
else
return true; // na ostatnim się zatrzymać zawsze
}
bool TTrainParameters::IsLastStop() const {
return ( StationIndex >= StationCount );
}
bool TTrainParameters::IsMaintenance() const {
if( ( StationIndex < StationCount ) )
if( ( StationIndex <= StationCount ) )
return TimeTable[ StationIndex ].is_maintenance;
else
return false;
}
int TTrainParameters::radio_channel() const {
if( ( StationIndex < StationCount ) )
if( ( StationIndex <= StationCount ) )
return TimeTable[ StationIndex ].radio_channel;
else
return -1;
}
// returns: sound file associated with current station, or -1
sound_source TTrainParameters::current_stop_sound() const {
if( ( StationIndex <= StationCount ) )
return TimeTable[ StationIndex ].name_sound;
else
return { sound_placement::general };
}
bool TTrainParameters::UpdateMTable( scenario_time const &Time, std::string const &NewName ) {
return UpdateMTable( Time.data().wHour, Time.data().wMinute + Time.data().wSecond * 0.0167, NewName );
@@ -421,7 +458,7 @@ bool TTrainParameters::LoadTTfile(std::string scnpath, int iPlus, double vmax)
{
auto const stationware { Split( record->StationWare, ',' ) };
for( auto const &entry : stationware ) {
if( entry[ 0 ] == 'R' ) {
if( entry.front() == 'R' ) {
auto const entrysplit { split_string_and_number( entry ) };
if( ( entrysplit.first == "R" )
&& ( entrysplit.second <= 10 ) ) {
@@ -507,9 +544,10 @@ bool TTrainParameters::LoadTTfile(std::string scnpath, int iPlus, double vmax)
TimeTable[1].Ah = TimeTable[1].Dh;
TimeTable[1].Am = TimeTable[1].Dm;
}
// NextStationName:=TimeTable[1].StationName;
/* TTVmax:=TimeTable[1].vmax; */
}
//
load_sounds();
// potentially offset table times
auto const timeoffset { static_cast<int>( Global.ScenarioTimeOffset * 60 ) + iPlus };
if( timeoffset != 0 ) // jeżeli jest przesunięcie rozkładu
{
@@ -534,24 +572,31 @@ bool TTrainParameters::LoadTTfile(std::string scnpath, int iPlus, double vmax)
return ConversionError == 0;
}
void TMTableTime::UpdateMTableTime(double deltaT)
// dodanie czasu (deltaT) w sekundach, z przeliczeniem godziny
{
mr += deltaT; // dodawanie sekund
while (mr >= 60.0) // przeliczenie sekund do właściwego przedziału
{
mr -= 60.0;
++mm;
void
TTrainParameters::load_sounds() {
for( auto stationidx = 1; stationidx < StationCount + 1; ++stationidx ) {
auto &station { TimeTable[ stationidx ] };
if( station.Ah == -1 ) {
continue;
}
while (mm > 59) // przeliczenie minut do właściwego przedziału
{
mm -= 60;
++hh;
// specified arrival time means it's a scheduled stop
auto const stationname { (
ends_with( station.StationName, "_po" ) ?
station.StationName.substr( 0, station.StationName.size() - 3 ) :
station.StationName ) };
auto const lookup {
FileExists(
{ Global.asCurrentSceneryPath + stationname, std::string{ szSoundPath } + "sip/" + stationname },
{ ".ogg", ".flac", ".wav" } ) };
if( lookup.first.empty() ) {
continue;
}
while (hh > 23) // przeliczenie godzin do właściwego przedziału
{
hh -= 24;
++dd; // zwiększenie numeru dnia
// wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie)
station.name_sound =
sound_source{ sound_placement::general, EU07_SOUND_CABANNOUNCEMENTCUTOFFRANGE }
.deserialize( lookup.first + lookup.second, sound_type::single );
}
}
@@ -592,3 +637,24 @@ void TTrainParameters::serialize( dictionary_source *Output ) const {
}
}
}
void TMTableTime::UpdateMTableTime(double deltaT)
// dodanie czasu (deltaT) w sekundach, z przeliczeniem godziny
{
mr += deltaT; // dodawanie sekund
while (mr >= 60.0) // przeliczenie sekund do właściwego przedziału
{
mr -= 60.0;
++mm;
}
while (mm > 59) // przeliczenie minut do właściwego przedziału
{
mm -= 60;
++hh;
}
while (hh > 23) // przeliczenie godzin do właściwego przedziału
{
hh -= 24;
++dd; // zwiększenie numeru dnia
}
}

View File

@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
#include <string>
#include "Classes.h"
#include "sound.h"
namespace Mtable
{
@@ -38,6 +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 };
};
typedef TMTableLine TMTable[MaxTTableSize + 1];
@@ -65,7 +67,10 @@ class TTrainParameters
std::string ShowRelation() const;
double WatchMTable(double DistCounter);
std::string NextStop() const;
sound_source next_stop_sound() const;
sound_source last_stop_sound() const;
bool IsStop() const;
bool IsLastStop() const;
bool IsMaintenance() const;
bool IsTimeToGo(double hh, double mm);
bool UpdateMTable(double hh, double mm, std::string const &NewName);
@@ -80,6 +85,10 @@ class TTrainParameters
void serialize( dictionary_source *Output ) const;
// returns: radio channel associated with current station, or -1
int radio_channel() const;
// returns: sound file associated with current station, or -1
sound_source current_stop_sound() const;
private:
void load_sounds();
};
class TMTableTime

View File

@@ -15,6 +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_BRAKINGCUTOFFRANGE { 100.f };
float const EU07_SOUND_RUNNINGNOISECUTOFFRANGE { 200.f };
float const EU07_SOUND_HANDHELDRADIORANGE { 3500.f };
@@ -50,7 +51,7 @@ class sound_source {
public:
// constructors
sound_source( sound_placement const Placement, float const Range = 50.f );
sound_source( sound_placement const Placement = sound_placement::general, float const Range = 50.f );
// destructor
~sound_source();

View File

@@ -459,6 +459,14 @@ len_common_prefix( std::string const &Left, std::string const &Right ) {
std::distance( left, std::mismatch( left, left + Left.size(), right ).first ) );
}
// returns true if provided string ends with another provided string
bool
ends_with( std::string const &String, std::string const &Suffix ) {
return ( String.size() >= Suffix.size() )
&& ( 0 == String.compare( String.size() - Suffix.size(), Suffix.size(), Suffix ) );
}
// helper, restores content of a 3d vector from provided input stream
// TODO: review and clean up the helper routines, there's likely some redundant ones
glm::dvec3 LoadPoint( cParser &Input ) {

View File

@@ -216,6 +216,9 @@ std::string substr_path( std::string const &Filename );
// returns common prefix of two provided strings
std::ptrdiff_t len_common_prefix( std::string const &Left, std::string const &Right );
// returns true if provided string ends with another provided string
bool ends_with( std::string const &String, std::string const &Suffix );
template <typename Type_>
void SafeDelete( Type_ &Pointer ) {
delete Pointer;

View File

@@ -1,5 +1,5 @@
#pragma once
#define VERSION_MAJOR 20
#define VERSION_MINOR 711
#define VERSION_MINOR 728
#define VERSION_REVISION 0