16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-22 16:19: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 ) { 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 // jeśli jedzie (nie trzeba czekać, aż się drgania wytłumią - drzwi zamykane od 1.0) to będzie zatrzymanie
sSpeedTable[ i ].fVelNext = 0; 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 ) { } else if( true == IsAtPassengerStop ) {
// jeśli się zatrzymał przy W4, albo stał w momencie zobaczenia W4 // jeśli się zatrzymał przy W4, albo stał w momencie zobaczenia W4
if( !AIControllFlag ) { 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 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 ); auto const exchangetime = simulation::Station.update_load( pVehicles[ 0 ], TrainParams, platformside );
WaitingSet( exchangetime ); WaitingSet( exchangetime );
// announce the stop name while at it
announce( announcement_t::current );
m_lastexchangestop = asNextStop; 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()) { if (TrainParams.DirectionChange()) {
// jeśli "@" w rozkładzie, to wykonanie dalszych komend // jeśli "@" w rozkładzie, to wykonanie dalszych komend
// wykonanie kolejnej komendy, nie dotyczy ostatniej stacji // 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 )) { if (OrderCurrentGet() & ( Shunt | Loose_shunt )) {
OrderNext(Obey_train); // uruchomić jazdę pociągową OrderNext(Obey_train); // uruchomić jazdę pociągową
CheckVehicles(); // zmienić światła CheckVehicles(); // zmienić światła
@@ -4184,6 +4191,23 @@ TController::doors_permit_active() const {
|| IsAnyDoorPermitActive[ side::left ] ); || 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() void TController::RecognizeCommand()
{ // odczytuje i wykonuje komendę przekazaną lokomotywie { // odczytuje i wykonuje komendę przekazaną lokomotywie
TCommand *c = &mvOccupied->CommandIn; 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 TrainParams.StationIndexInc(); // przejście do następnej
iStationStart = TrainParams.StationIndex; iStationStart = TrainParams.StationIndex;
asNextStop = TrainParams.NextStop(); asNextStop = TrainParams.NextStop();
m_lastannouncement = announcement_t::idle;
iDrivigFlags |= movePrimary; // skoro dostał rozkład, to jest teraz głównym iDrivigFlags |= movePrimary; // skoro dostał rozkład, to jest teraz głównym
// NewCommand = Global.asCurrentSceneryPath + NewCommand; // NewCommand = Global.asCurrentSceneryPath + NewCommand;
auto lookup = auto lookup =
@@ -4294,7 +4319,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
NewCommand = TrainParams.Relation2; // relacja docelowa z rozkładu NewCommand = TrainParams.Relation2; // relacja docelowa z rozkładu
} }
// jeszcze poustawiać tekstury na wyświetlaczach // jeszcze poustawiać tekstury na wyświetlaczach
TDynamicObject *p = pVehicles[0]; TDynamicObject *p = pVehicles[end::front];
while (p) while (p)
{ {
p->DestinationSet(NewCommand, TrainParams.TrainName); // relacja docelowa p->DestinationSet(NewCommand, TrainParams.TrainName); // relacja docelowa
@@ -4726,6 +4751,10 @@ TController::UpdateSituation(double dt) {
// zaktualizować wyświetlanie rozkładu // zaktualizować wyświetlanie rozkładu
iStationStart = TrainParams.StationIndex; iStationStart = TrainParams.StationIndex;
fLastStopExpDist = -1.0; // usunąć licznik 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 // ABu-160305 testowanie gotowości do jazdy
@@ -5960,6 +5989,7 @@ TController::UpdateSituation(double dt) {
VelforDriver ); VelforDriver );
} }
// recalculate potential load exchange duration // recalculate potential load exchange duration
DoesAnyDoorNeedOpening = false;
ExchangeTime = 0.f; ExchangeTime = 0.f;
if( fStopTime < 0 ) { if( fStopTime < 0 ) {
// czas postoju przed dalszą jazdą (np. na przystanku) // czas postoju przed dalszą jazdą (np. na przystanku)
@@ -5967,7 +5997,9 @@ TController::UpdateSituation(double dt) {
// verify progress of load exchange // verify progress of load exchange
auto *vehicle { pVehicles[ 0 ] }; auto *vehicle { pVehicles[ 0 ] };
while( vehicle != nullptr ) { 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(); vehicle = vehicle->Next();
} }
if( ( ExchangeTime > 0 ) if( ( ExchangeTime > 0 )

View File

@@ -454,6 +454,8 @@ private:
int iRadioChannel = 1; // numer aktualnego kanału radiowego int iRadioChannel = 1; // numer aktualnego kanału radiowego
int iGuardRadio = 0; // numer kanału radiowego kierownika (0, gdy nie używa radia) int iGuardRadio = 0; // numer kanału radiowego kierownika (0, gdy nie używa radia)
sound_source tsGuardSignal{ sound_placement::internal }; sound_source tsGuardSignal{ sound_placement::internal };
announcement_t m_lastannouncement{ announcement_t::idle };
bool m_makenextstopannouncement{ false };
// consist // consist
// methods // methods
@@ -468,6 +470,7 @@ private:
bool doors_permit_active() const; bool doors_permit_active() const;
void AutoRewident(); // ustawia hamulce w składzie void AutoRewident(); // ustawia hamulce w składzie
void UpdatePantographs(); void UpdatePantographs();
void announce( announcement_t const Announcement );
// members // members
double fLength = 0.0; // długość składu (do wyciągania z ograniczeń) 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 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 bool Ready = false; // ABu: stan gotowosci do odjazdu - sprawdzenie odhamowania wagonow
double ConsistShade{ 1.0 }; // averaged amount of sunlight received by the consist double ConsistShade{ 1.0 }; // averaged amount of sunlight received by the consist
TDynamicObject *pVehicles[ 2 ]; // skrajne pojazdy w składzie (niekoniecznie bezpośrednio sterowane) 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 IsAnyDoorOpen[ 2 ]; // state of door in the consist
bool IsAnyDoorPermitActive[ 2 ]; // state of door permit 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 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 "renderer.h"
#include "uitranscripts.h" #include "uitranscripts.h"
#include "messaging.h" #include "messaging.h"
#include "Driver.h"
// Ra: taki zapis funkcjonuje lepiej, ale może nie jest optymalny // Ra: taki zapis funkcjonuje lepiej, ale może nie jest optymalny
#define vWorldFront Math3D::vector3(0, 0, 1) #define vWorldFront Math3D::vector3(0, 0, 1)
@@ -2022,7 +2023,7 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
} }
} // koniec hamulce } // koniec hamulce
else if( ( ActPar.size() >= 3 ) else if( ( ActPar.size() >= 3 )
&& ( ActPar[ 0 ] == 'W' ) ) { && ( ActPar.front() == 'W' ) ) {
// wheel // wheel
ActPar.erase( 0, 1 ); ActPar.erase( 0, 1 );
@@ -2032,7 +2033,7 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
while( false == ActPar.empty() ) { while( false == ActPar.empty() ) {
// TODO: convert this whole copy and paste mess to something more elegant one day // TODO: convert this whole copy and paste mess to something more elegant one day
switch( ActPar[ 0 ] ) { switch( ActPar.front() ) {
case 'F': { case 'F': {
// fixed flat size // fixed flat size
auto const indexstart { 1 }; auto const indexstart { 1 };
@@ -2078,14 +2079,14 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
} }
} // wheel } // wheel
else if( ( ActPar.size() >= 2 ) else if( ( ActPar.size() >= 2 )
&& ( ActPar[ 0 ] == 'T' ) ) { && ( ActPar.front() == 'T' ) ) {
// temperature // temperature
ActPar.erase( 0, 1 ); ActPar.erase( 0, 1 );
auto setambient { false }; auto setambient { false };
while( false == ActPar.empty() ) { while( false == ActPar.empty() ) {
switch( ActPar[ 0 ] ) { switch( ActPar.front() ) {
case 'A': { case 'A': {
// cold start, set all temperatures to ambient level // cold start, set all temperatures to ambient level
setambient = true; setambient = true;
@@ -4304,7 +4305,7 @@ void TDynamicObject::RenderSounds() {
// NBMX sygnal odjazdu // NBMX sygnal odjazdu
if( MoverParameters->Doors.has_warning ) { if( MoverParameters->Doors.has_warning ) {
auto const lowvoltagepower { MoverParameters->Power24vIsAvailable || MoverParameters->Power110vIsAvailable }; auto const lowvoltagepower { MoverParameters->Power24vIsAvailable || MoverParameters->Power110vIsAvailable };
for( auto &departuresignalsound : m_departuresignalsounds ) { for( auto &speaker : m_speakers ) {
// TBD, TODO: per-location door state triggers? // TBD, TODO: per-location door state triggers?
if( ( MoverParameters->DepartureSignal ) if( ( MoverParameters->DepartureSignal )
&& ( lowvoltagepower ) && ( lowvoltagepower )
@@ -4316,13 +4317,32 @@ void TDynamicObject::RenderSounds() {
) { ) {
// for the autonomous doors play the warning automatically whenever a door is closing // for the autonomous doors play the warning automatically whenever a door is closing
// MC: pod warunkiem ze jest zdefiniowane w chk // 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 { 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 // NBMX Obsluga drzwi, MC: zuniwersalnione
std::array<side, 2> const sides { side::right, side::left }; std::array<side, 2> const sides { side::right, side::left };
for( auto const side : sides ) { for( auto const side : sides ) {
@@ -4747,15 +4767,13 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
parser.getTokens(); parser.getTokens();
parser >> asModel; parser >> asModel;
replace_slashes( 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 { // model wymaga wielu tekstur wymiennych
m_materialdata.multi_textures = 1; m_materialdata.multi_textures = 1;
asModel.erase( asModel.length() - 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 // name can contain leading slash, erase it to avoid creation of double slashes when the name is combined with current directory
if( asModel[ 0 ] == '/' ) { erase_leading_slashes( asModel );
asModel.erase( 0, 1 );
}
/* /*
// never really used, may as well get rid of it // never really used, may as well get rid of it
std::size_t i = asModel.find( ',' ); 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 // value can be optionally set of values enclosed in "[]" in which case one value will be picked randomly
while( ( ( token = parser.getToken<std::string>() ) != "" ) while( ( ( token = parser.getToken<std::string>() ) != "" )
&& ( token != "}" ) ) { && ( token != "}" ) ) {
if( token[ token.size() - 1 ] == ':' ) { if( token.back() == ':' ) {
auto loadmodel { deserialize_random_set( parser ) }; auto loadmodel { deserialize_random_set( parser ) };
replace_slashes( loadmodel ); replace_slashes( loadmodel );
LoadModelOverrides.emplace( token.erase( token.size() - 1 ), 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:" ) { else if( token == "departuresignal:" ) {
// pliki z sygnalem odjazdu // 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.deserialize( parser, sound_type::multipart, sound_parameters::range );
soundtemplate.owner( this ); soundtemplate.owner( this );
for( auto &departuresignalsound : m_departuresignalsounds ) { for( auto &speaker : m_speakers ) {
// apply configuration to all defined doors, but preserve their individual offsets speaker.departure_signal = soundtemplate;
auto const soundoffset { departuresignalsound.offset() }; speaker.departure_signal.offset( speaker.offset );
departuresignalsound = soundtemplate;
departuresignalsound.offset( soundoffset );
} }
} }
@@ -5844,6 +5860,38 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
m_wheelflat.owner( this ); 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 != "" ) } while( ( token != "" )
&& ( token != "endsounds" ) ); && ( token != "endsounds" ) );
@@ -5891,10 +5939,11 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
door.step_open.offset( location ); door.step_open.offset( location );
m_doorsounds.emplace_back( door ); m_doorsounds.emplace_back( door );
} }
// potential departure sound, one per door (pair) on vehicle centreline m_speakers.emplace_back(
sound_source departuresignalsound { sound_placement::general, 25.f }; speaker_sounds {
departuresignalsound.offset( glm::vec3{ 0.f, 3.f, offset } ); { 0.f, 3.f, offset },
m_departuresignalsounds.emplace_back( departuresignalsound ); {},
{} } );
} }
} }
@@ -7009,6 +7058,57 @@ material_handle TDynamicObject::DestinationFind( std::string Destination ) {
return destinationhandle; 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) void TDynamicObject::OverheadTrack(float o)
{ // ewentualne wymuszanie jazdy { // ewentualne wymuszanie jazdy
// bezprądowej z powodu informacji // 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 // parameters for the material object, as currently used by various simulator models
struct material_data { struct material_data {
@@ -395,6 +404,13 @@ private:
void position( glm::vec3 const Location ); void position( glm::vec3 const Location );
void render( TMoverParameters const &Vehicle, double const Deltatime ); 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 // methods
void ABuLittleUpdate(double ObjSqrDist); void ABuLittleUpdate(double ObjSqrDist);
@@ -476,7 +492,6 @@ private:
std::array<coupler_sounds, 2> m_couplersounds; // always front and rear 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<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<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 bool m_doorlocks { false }; // sound helper, current state of door locks
sound_source sHorn1 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; sound_source sHorn1 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE };
sound_source sHorn2 { 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_data m_exchange; // state of active load exchange procedure, if any
exchange_sounds m_exchangesounds; // sounds associated with the load exchange 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; coupleradapter_data m_coupleradapter;
bool renderme; // yB - czy renderowac bool renderme; // yB - czy renderowac
@@ -702,6 +720,7 @@ private:
void OverheadTrack(float o); void OverheadTrack(float o);
glm::dvec3 get_future_movement() const; glm::dvec3 get_future_movement() const;
void move_set(double distance); void move_set(double distance);
void announce( announcement_t const Announcement );
double MED[9][8]; // lista zmiennych do debugowania hamulca ED double MED[9][8]; // lista zmiennych do debugowania hamulca ED
static std::string const MED_labels[ 8 ]; static std::string const MED_labels[ 8 ];

View File

@@ -74,18 +74,51 @@ basic_event::event_conditions::test() const {
} }
} }
if( flags & flags::track_busy ) { if( flags & flags::track_busy ) {
auto trackbusyresult { true };
std::string trackbusylog { "Test: Track busy - " };
for( auto *track : tracks ) { for( auto *track : tracks ) {
if( true == track->IsEmpty() ) { 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 ) { if( flags & flags::track_free ) {
auto trackfreeresult { true };
std::string trackfreelog{ "Test: Track free - " };
for( auto *track : tracks ) { for( auto *track : tracks ) {
if( false == track->IsEmpty() ) { 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 ) ) { if( flags & ( flags::text | flags::value1 | flags::value2 ) ) {
// porównanie wartości // porównanie wartości

View File

@@ -9146,7 +9146,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath)
continue; continue;
} }
if( inputline[ 0 ] == ' ' ) { if( !inputline.empty() && inputline.front() == ' ' ) {
// guard against malformed config files with leading spaces // guard against malformed config files with leading spaces
inputline.erase( 0, inputline.find_first_not_of( ' ' ) ); 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 // zapewni to jakąś zgodność wstecz, bo zamiast liczby będzie ciąg, którego
// wartość powinna być uznana jako zerowa // wartość powinna być uznana jako zerowa
// parser.getToken(iNumVerts); // parser.getToken(iNumVerts);
if (token[0] == '*') if (token.front() == '*')
{ // jeśli pierwszy znak jest gwiazdką, poszukać { // jeśli pierwszy znak jest gwiazdką, poszukać
// submodelu o nazwie bez tej gwiazdki i wziąć z // submodelu o nazwie bez tej gwiazdki i wziąć z
// niego wierzchołki // niego wierzchołki

View File

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

View File

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

View File

@@ -42,28 +42,65 @@ std::string TTrainParameters::NextStop() const
return "[End of route]"; //że niby koniec 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 bool TTrainParameters::IsStop() const
{ // zapytanie, czy zatrzymywać na następnym punkcie rozkładu { // zapytanie, czy zatrzymywać na następnym punkcie rozkładu
if ((StationIndex < StationCount)) if ((StationIndex <= StationCount))
return TimeTable[StationIndex].Ah >= 0; //-1 to brak postoju return TimeTable[StationIndex].Ah >= 0; //-1 to brak postoju
else else
return true; // na ostatnim się zatrzymać zawsze return true; // na ostatnim się zatrzymać zawsze
} }
bool TTrainParameters::IsLastStop() const {
return ( StationIndex >= StationCount );
}
bool TTrainParameters::IsMaintenance() const { bool TTrainParameters::IsMaintenance() const {
if( ( StationIndex < StationCount ) ) if( ( StationIndex <= StationCount ) )
return TimeTable[ StationIndex ].is_maintenance; return TimeTable[ StationIndex ].is_maintenance;
else else
return false; return false;
} }
int TTrainParameters::radio_channel() const { int TTrainParameters::radio_channel() const {
if( ( StationIndex < StationCount ) ) if( ( StationIndex <= StationCount ) )
return TimeTable[ StationIndex ].radio_channel; return TimeTable[ StationIndex ].radio_channel;
else else
return -1; 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 ) { 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 ); 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, ',' ) }; auto const stationware { Split( record->StationWare, ',' ) };
for( auto const &entry : stationware ) { for( auto const &entry : stationware ) {
if( entry[ 0 ] == 'R' ) { if( entry.front() == 'R' ) {
auto const entrysplit { split_string_and_number( entry ) }; auto const entrysplit { split_string_and_number( entry ) };
if( ( entrysplit.first == "R" ) if( ( entrysplit.first == "R" )
&& ( entrysplit.second <= 10 ) ) { && ( 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].Ah = TimeTable[1].Dh;
TimeTable[1].Am = TimeTable[1].Dm; 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 }; auto const timeoffset { static_cast<int>( Global.ScenarioTimeOffset * 60 ) + iPlus };
if( timeoffset != 0 ) // jeżeli jest przesunięcie rozkładu 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; return ConversionError == 0;
} }
void TMTableTime::UpdateMTableTime(double deltaT) void
// dodanie czasu (deltaT) w sekundach, z przeliczeniem godziny TTrainParameters::load_sounds() {
{
mr += deltaT; // dodawanie sekund for( auto stationidx = 1; stationidx < StationCount + 1; ++stationidx ) {
while (mr >= 60.0) // przeliczenie sekund do właściwego przedziału auto &station { TimeTable[ stationidx ] };
{ if( station.Ah == -1 ) {
mr -= 60.0; continue;
++mm;
} }
while (mm > 59) // przeliczenie minut do właściwego przedziału // specified arrival time means it's a scheduled stop
{ auto const stationname { (
mm -= 60; ends_with( station.StationName, "_po" ) ?
++hh; 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 // wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie)
{ station.name_sound =
hh -= 24; sound_source{ sound_placement::general, EU07_SOUND_CABANNOUNCEMENTCUTOFFRANGE }
++dd; // zwiększenie numeru dnia .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 <string>
#include "Classes.h" #include "Classes.h"
#include "sound.h"
namespace Mtable namespace Mtable
{ {
@@ -38,6 +39,7 @@ struct TMTableLine
float tm{ 0.f }; // czas jazdy do tej stacji w min. (z kolumny) float tm{ 0.f }; // czas jazdy do tej stacji w min. (z kolumny)
bool is_maintenance{ false }; bool is_maintenance{ false };
int radio_channel{ -1 }; int radio_channel{ -1 };
sound_source name_sound{ sound_placement::internal };
}; };
typedef TMTableLine TMTable[MaxTTableSize + 1]; typedef TMTableLine TMTable[MaxTTableSize + 1];
@@ -65,7 +67,10 @@ class TTrainParameters
std::string ShowRelation() const; std::string ShowRelation() const;
double WatchMTable(double DistCounter); double WatchMTable(double DistCounter);
std::string NextStop() const; std::string NextStop() const;
sound_source next_stop_sound() const;
sound_source last_stop_sound() const;
bool IsStop() const; bool IsStop() const;
bool IsLastStop() const;
bool IsMaintenance() const; bool IsMaintenance() const;
bool IsTimeToGo(double hh, double mm); bool IsTimeToGo(double hh, double mm);
bool UpdateMTable(double hh, double mm, std::string const &NewName); bool UpdateMTable(double hh, double mm, std::string const &NewName);
@@ -80,6 +85,10 @@ class TTrainParameters
void serialize( dictionary_source *Output ) const; void serialize( dictionary_source *Output ) const;
// returns: radio channel associated with current station, or -1 // returns: radio channel associated with current station, or -1
int radio_channel() const; 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 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_GLOBALRANGE { -1.f };
float const EU07_SOUND_CABCONTROLSCUTOFFRANGE { 7.5f }; 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_BRAKINGCUTOFFRANGE { 100.f };
float const EU07_SOUND_RUNNINGNOISECUTOFFRANGE { 200.f }; float const EU07_SOUND_RUNNINGNOISECUTOFFRANGE { 200.f };
float const EU07_SOUND_HANDHELDRADIORANGE { 3500.f }; float const EU07_SOUND_HANDHELDRADIORANGE { 3500.f };
@@ -50,7 +51,7 @@ class sound_source {
public: public:
// constructors // 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 // destructor
~sound_source(); ~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 ) ); 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 // 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 // TODO: review and clean up the helper routines, there's likely some redundant ones
glm::dvec3 LoadPoint( cParser &Input ) { 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 // returns common prefix of two provided strings
std::ptrdiff_t len_common_prefix( std::string const &Left, std::string const &Right ); 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_> template <typename Type_>
void SafeDelete( Type_ &Pointer ) { void SafeDelete( Type_ &Pointer ) {
delete Pointer; delete Pointer;

View File

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