additional door control types, door operation tweaks, load exchange procedure enhancements, load exchange sounds

This commit is contained in:
tmj-fstate
2018-03-01 13:40:57 +01:00
parent 7440b1d902
commit b6206bc12c
9 changed files with 457 additions and 286 deletions

View File

@@ -869,53 +869,15 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
// jeśli długość peronu ((sSpeedTable[i].evEvent->ValueGet(2)) nie podana,
// przyjąć odległość fMinProximityDist
{ // jeśli się zatrzymał przy W4, albo stał w momencie zobaczenia W4
if (!AIControllFlag) // AI tylko sobie otwiera drzwi
iDrivigFlags &= ~moveStopCloser; // w razie przełączenia na AI ma
// nie podciągać do W4, gdy
// użytkownik zatrzymał za daleko
if ((iDrivigFlags & moveDoorOpened) == 0)
{ // drzwi otwierać jednorazowo
if( !AIControllFlag ) {
// w razie przełączenia na AI ma nie podciągać do W4, gdy użytkownik zatrzymał za daleko
iDrivigFlags &= ~moveStopCloser;
}
if( ( iDrivigFlags & moveDoorOpened ) == 0 ) {
// drzwi otwierać jednorazowo
iDrivigFlags |= moveDoorOpened; // nie wykonywać drugi raz
if (mvOccupied->DoorOpenCtrl == 1) //(mvOccupied->TrainType==dt_EZT)
{ // otwieranie drzwi w EZT
if (AIControllFlag) // tylko AI otwiera drzwi EZT, użytkownik
// musi samodzielnie
if (!mvOccupied->DoorLeftOpened &&
!mvOccupied->DoorRightOpened)
{ // otwieranie drzwi
int p2 =
int(floor(sSpeedTable[i].evEvent->ValueGet(2))) %
10; // p7=platform side (1:left, 2:right, 3:both)
int lewe = (iDirection > 0) ? 1 : 2; // jeśli jedzie do tyłu, to drzwi otwiera odwrotnie
int prawe = (iDirection > 0) ? 2 : 1;
if (p2 & lewe)
mvOccupied->DoorLeft(true);
if (p2 & prawe)
mvOccupied->DoorRight(true);
}
}
else
{ // otwieranie drzwi w składach wagonowych - docelowo wysyłać komendę zezwolenia na otwarcie drzwi
int p7, lewe, prawe; // p7=platform side (1:left, 2:right, 3:both)
// tu będzie jeszcze długość peronu zaokrąglona do 10m
// (20m bezpieczniej, bo nie modyfikuje bitu 1)
p7 = int(std::floor(sSpeedTable[i].evEvent->ValueGet(2))) % 10;
TDynamicObject *p = pVehicles[0]; // pojazd na czole składu
while (p)
{ // otwieranie drzwi w pojazdach - flaga zezwolenia była by lepsza
// jeśli jedzie do tyłu, to drzwi otwiera odwrotnie
lewe = (p->DirectionGet() > 0) ? 1 : 2;
prawe = 3 - lewe;
// wagony muszą mieć baterię załączoną do otwarcia drzwi...
p->MoverParameters->BatterySwitch(true);
if (p7 & lewe)
p->MoverParameters->DoorLeft(true);
if (p7 & prawe)
p->MoverParameters->DoorRight(true);
// pojazd podłączony z tyłu (patrząc od czoła)
p = p->Next();
}
}
Doors( true, static_cast<int>( std::floor( sSpeedTable[ i ].evEvent->ValueGet( 2 ) ) ) % 10 );
}
if (TrainParams->UpdateMTable( simulation::Time, asNextStop) ) {
// to się wykona tylko raz po zatrzymaniu na W4
@@ -925,23 +887,9 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
iDrivigFlags &= ~( moveStartHorn | moveStartHornNow );
}
// perform loading/unloading
auto const exchangetime { simulation::Station.update_load( pVehicles[ 0 ], *TrainParams ) };
// TBD: adjust time to load exchange size
if( fStopTime > -55 ) {
// na końcu rozkładu się ustawia 60s i tu by było skrócenie
// WaitingSet( 15.0 + Random( 15.0 ) ); // 10 sekund (wziąć z rozkładu????) - czekanie
// with door open on both sides calculated loading time is halved
// p7=platform side (1:left, 2:right, 3:both)
auto const platformside = static_cast<int>( std::floor( sSpeedTable[ i ].evEvent->ValueGet( 2 ) ) ) % 10;
WaitingSet(
platformside == 3 ?
exchangetime * 0.5 :
exchangetime );
}
// update brake settings and ai braking tables
// NOTE: this calculation is run after completing loading/unloading
// remember to move it to a more fitting spot, when loading/unloading taks some actual time
AutoRewident(); // nastawianie hamulca do jazdy pociągowej
auto const platformside = static_cast<int>( std::floor( sSpeedTable[ i ].evEvent->ValueGet( 2 ) ) ) % 10;
auto const exchangetime = simulation::Station.update_load( pVehicles[ 0 ], *TrainParams, platformside );
WaitingSet( std::max( -fStopTime, exchangetime ) ); // na końcu rozkładu się ustawia 60s i tu by było skrócenie
if( TrainParams->CheckTrainLatency() < 0.0 ) {
// odnotowano spóźnienie
@@ -1012,6 +960,10 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
+ ": at " + std::to_string(simulation::Time.data().wHour) + ":" + std::to_string(simulation::Time.data().wMinute)
+ " next " + asNextStop); // informacja
#endif
// update brake settings and ai braking tables
// NOTE: this calculation is expected to run after completing loading/unloading
AutoRewident(); // nastawianie hamulca do jazdy pociągowej
if( int( floor( sSpeedTable[ i ].evEvent->ValueGet( 1 ) ) ) & 1 ) {
// nie podjeżdżać do semafora, jeśli droga nie jest wolna
iDrivigFlags |= moveStopHere;
@@ -2401,7 +2353,7 @@ bool TController::ReleaseEngine()
ReactionTime = PrepareTime;
if (AIControllFlag)
{ // jeśli steruje komputer
if (mvOccupied->DoorOpenCtrl == 1)
if (mvOccupied->DoorCloseCtrl == control::driver)
{ // zamykanie drzwi
if (mvOccupied->DoorLeftOpened)
mvOccupied->DoorLeft(false);
@@ -3051,50 +3003,66 @@ void TController::SpeedSet()
}
};
void TController::Doors(bool what)
{ // otwieranie/zamykanie drzwi w składzie albo (tylko AI) EZT
if (what)
{ // otwarcie
}
else
{ // zamykanie
if (mvOccupied->DoorOpenCtrl == 1)
{ // jeśli drzwi sterowane z kabiny
if( AIControllFlag ) {
if( mvOccupied->DoorLeftOpened || mvOccupied->DoorRightOpened ) {
// AI zamyka drzwi przed odjazdem
if( ( true == mvOccupied->DoorClosureWarning )
&& ( false == mvOccupied->DepartureSignal )
&& ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) {
mvOccupied->signal_departure( true ); // załącenie bzyczka
fActionTime = -3.0 - 0.1 * Random( 10 ); // 3-4 second wait
}
if( fActionTime > -0.5 ) {
// Ra: trzeba by ustawić jakiś czas oczekiwania na zamknięcie się drzwi
mvOccupied->DoorLeft( false ); // zamykanie drzwi
mvOccupied->DoorRight( false );
iDrivigFlags &= ~moveDoorOpened;
// 1.5-2.5 sec wait, +potentially 0.5 remaining
fActionTime = -1.5 - 0.1 * Random( 10 );
}
// otwieranie/zamykanie drzwi w składzie albo (tylko AI) EZT
void TController::Doors( bool const Open, int const Side ) {
if( true == Open ) {
// otwieranie drzwi
// otwieranie drzwi w składach wagonowych - docelowo wysyłać komendę zezwolenia na otwarcie drzwi
// tu będzie jeszcze długość peronu zaokrąglona do 10m (20m bezpieczniej, bo nie modyfikuje bitu 1)
// p7=platform side (1:left, 2:right, 3:both)
auto *vehicle = pVehicles[0]; // pojazd na czole składu
while( vehicle != nullptr ) {
// wagony muszą mieć baterię załączoną do otwarcia drzwi...
vehicle->MoverParameters->BatterySwitch( true );
// otwieranie drzwi w pojazdach - flaga zezwolenia była by lepsza
if( vehicle->MoverParameters->DoorOpenCtrl != control::passenger ) {
// if the door are controlled by the driver, we let the user operate them...
if( true == AIControllFlag ) {
// ...unless this user is an ai
// p7=platform side (1:left, 2:right, 3:both)
// jeśli jedzie do tyłu, to drzwi otwiera odwrotnie
auto const lewe = ( vehicle->DirectionGet() > 0 ) ? 1 : 2;
auto const prawe = 3 - lewe;
if( Side & lewe )
vehicle->MoverParameters->DoorLeft( true, range::local );
if( Side & prawe )
vehicle->MoverParameters->DoorRight( true, range::local );
}
}
// pojazd podłączony z tyłu (patrząc od czoła)
vehicle = vehicle->Next();
}
else
{ // jeśli nie, to zamykanie w składzie wagonowym
TDynamicObject *p = pVehicles[0]; // pojazd na czole składu
while (p)
{ // zamykanie drzwi w pojazdach - flaga zezwolenia była by lepsza
p->MoverParameters->DoorLeft(false); // w lokomotywie można by nie zamykać...
p->MoverParameters->DoorRight(false);
p = p->Next(); // pojazd podłączony z tyłu (patrząc od czoła)
}
else {
// zamykanie
if( AIControllFlag ) {
if( ( true == mvOccupied->DoorClosureWarning )
&& ( false == mvOccupied->DepartureSignal )
&& ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) {
mvOccupied->signal_departure( true ); // załącenie bzyczka
fActionTime = -3.0 - 0.1 * Random( 10 ); // 3-4 second wait
}
// WaitingSet(5); //10 sekund tu to za długo, opóźnia odjazd o pół minuty
fActionTime = -3.0 - 0.1 * Random(10); // czekanie sekundę, może trochę dłużej
}
if( ( true == TestFlag( iDrivigFlags, moveDoorOpened ) )
&& ( ( fActionTime > -0.5 )
|| ( false == AIControllFlag ) ) ) {
// ai doesn't close the door until it's free to depart, but human driver has free reign to do stupid things
auto *vehicle = pVehicles[ 0 ]; // pojazd na czole składu
while( vehicle != nullptr ) {
// zamykanie drzwi w pojazdach - flaga zezwolenia była by lepsza
if( vehicle->MoverParameters->DoorCloseCtrl != control::passenger ) {
vehicle->MoverParameters->DoorLeft( false, range::local ); // w lokomotywie można by nie zamykać...
vehicle->MoverParameters->DoorRight( false, range::local );
}
vehicle = vehicle->Next(); // pojazd podłączony z tyłu (patrząc od czoła)
}
fActionTime = -2.5 - 0.1 * Random( 10 ); // 2.5-3.5 sec wait, +potentially 0.5 remaining
iDrivigFlags &= ~moveDoorOpened; // zostały zamknięte - nie wykonywać drugi raz
}
}
};
}
void TController::RecognizeCommand()
{ // odczytuje i wykonuje komendę przekazaną lokomotywie
@@ -3632,7 +3600,7 @@ TController::UpdateSituation(double dt) {
// for human-controlled vehicles with no door control and dynamic brake auto-activating with door open
if( ( false == AIControllFlag )
&& ( iDrivigFlags & moveDoorOpened )
&& ( mvOccupied->DoorOpenCtrl != 1 )
&& ( mvOccupied->DoorCloseCtrl != control::driver )
&& ( mvControlling->MainCtrlPos > 0 ) ) {
Doors( false );
}
@@ -4098,7 +4066,10 @@ TController::UpdateSituation(double dt) {
iDrivigFlags |= movePress; // następnie będzie dociskanie
DirectionForward(mvOccupied->ActiveDir < 0); // zmiana kierunku jazdy na przeciwny (dociskanie)
CheckVehicles(); // od razu zmienić światła (zgasić) - bez tego się nie odczepi
/*
// NOTE: disabled to prevent closing the door before passengers can disembark
fStopTime = 0.0; // nie ma na co czekać z odczepianiem
*/
}
}
else {
@@ -4247,8 +4218,8 @@ TController::UpdateSituation(double dt) {
&& ( false == TestFlag( iDrivigFlags, movePress ) )
&& ( iCoupler == 0 )
// && ( mvOccupied->Vel > 0.0 )
&& ( pVehicle->PrevConnected == nullptr )
&& ( pVehicle->NextConnected == nullptr ) ) {
&& ( pVehicle->MoverParameters->Couplers[ side::front ].CouplingFlag == coupling::faux )
&& ( pVehicle->MoverParameters->Couplers[ side::rear ].CouplingFlag == coupling::faux ) ) {
SetVelocity(0, 0, stopJoin); // 1. faza odczepiania: zatrzymanie
// WriteLog("Zatrzymanie w celu odczepienia");
AccPreferred = std::min( 0.0, AccPreferred );
@@ -5040,7 +5011,7 @@ TController::UpdateSituation(double dt) {
fMinProximityDist : // cars are allowed to move within min proximity distance
fMaxProximityDist ) ? // other vehicle types keep wider margin
true :
vel < VelNext ) ) {
( vel + 1.0 ) < VelNext ) ) {
// to można przyspieszyć
IncSpeed();
}

View File

@@ -303,7 +303,7 @@ private:
bool IncSpeed();
bool DecSpeed(bool force = false);
void SpeedSet();
void Doors(bool what);
void Doors(bool const Open, int const Side = 0);
void RecognizeCommand(); // odczytuje komende przekazana lokomotywie
void Activation(); // umieszczenie obsady w odpowiednim członie
void ControllingSet(); // znajduje człon do sterowania

View File

@@ -1924,37 +1924,49 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424"
}
} // koniec hamulce
else if( ( ActPar.size() >= 3 )
&& ( ActPar.substr( 0, 2 ) == "WF" ) ) {
// wheel flat
// TODO: convert this whole mess to something more elegant one day
ActPar.erase( 0, 2 );
&& ( ActPar[ 0 ] == 'W' ) ) {
// wheel
ActPar.erase( 0, 1 );
auto fixedflatsize { 0 };
{
// fixed size flat
auto const indexend { ActPar.find_first_not_of( "1234567890", 0 ) };
fixedflatsize = std::atoi( ActPar.substr( 0, indexend ).c_str() );
ActPar.erase( 0, indexend );
}
// optional parameters
auto randomflatsize { 0 };
auto randomflatchance { 100 };
auto flatchance { 100 };
while( false == ActPar.empty() ) {
if( ActPar[ 0 ] == 'R' ) {
// random flat size
auto const indexstart { 1 };
auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) };
randomflatsize = std::atoi( ActPar.substr( indexstart, indexend ).c_str() );
ActPar.erase( 0, indexend );
}
else if( ActPar[ 0 ] == 'P' ) {
// random flat probability
auto const indexstart { 1 };
auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) };
randomflatchance = std::atoi( ActPar.substr( indexstart, indexend ).c_str() );
ActPar.erase( 0, indexend );
// TODO: convert this whole copy and paste mess to something more elegant one day
switch( ActPar[ 0 ] ) {
case 'F': {
// fixed flat size
auto const indexstart { 1 };
auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) };
fixedflatsize = std::atoi( ActPar.substr( indexstart, indexend ).c_str() );
ActPar.erase( 0, indexend );
break;
}
case 'R': {
// random flat size
auto const indexstart { 1 };
auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) };
randomflatsize = std::atoi( ActPar.substr( indexstart, indexend ).c_str() );
ActPar.erase( 0, indexend );
break;
}
case 'P': {
// random flat probability
auto const indexstart { 1 };
auto const indexend { ActPar.find_first_not_of( "1234567890", indexstart ) };
flatchance = std::atoi( ActPar.substr( indexstart, indexend ).c_str() );
ActPar.erase( 0, indexend );
break;
}
default: {
// unrecognized key
ActPar.erase( 0, 1 );
break;
}
}
}
if( Random(0, 100) <= randomflatchance ) {
if( Random( 0, 100 ) <= flatchance ) {
MoverParameters->WheelFlat += fixedflatsize + Random( 0, randomflatsize );
}
}
@@ -2476,6 +2488,87 @@ bool TDynamicObject::UpdateForce(double dt, double dt1, bool FullVer)
return true;
}
// initiates load change by specified amounts, with a platform on specified side
void TDynamicObject::LoadExchange( int const Disembark, int const Embark, int const Platform ) {
if( ( MoverParameters->DoorOpenCtrl == control::passenger )
|| ( MoverParameters->DoorOpenCtrl == control::mixed ) ) {
// jeśli jedzie do tyłu, to drzwi otwiera odwrotnie
auto const lewe = ( DirectionGet() > 0 ) ? 1 : 2;
auto const prawe = 3 - lewe;
if( Platform & lewe ) {
MoverParameters->DoorLeft( true, range::local );
}
if( Platform & prawe ) {
MoverParameters->DoorRight( true, range::local );
}
}
m_exchange.unload_count += Disembark;
m_exchange.load_count += Embark;
m_exchange.speed_factor = (
Platform == 3 ?
2.0 :
1.0 );
m_exchange.time = 0.0;
}
// update state of load exchange operation
void TDynamicObject::update_exchange( double const Deltatime ) {
if( ( m_exchange.unload_count < 0.01 ) && ( m_exchange.load_count < 0.01 ) ) { return; }
if( ( MoverParameters->Vel < 2.0 )
&& ( ( true == MoverParameters->DoorLeftOpened )
|| ( true == MoverParameters->DoorRightOpened ) ) ) {
m_exchange.time += Deltatime;
while( ( m_exchange.unload_count > 0.01 )
&& ( m_exchange.time >= 1.0 ) ) {
m_exchange.time -= 1.0;
auto const exchangesize = std::min( m_exchange.unload_count, MoverParameters->UnLoadSpeed * m_exchange.speed_factor );
m_exchange.unload_count -= exchangesize;
MoverParameters->LoadStatus = 1;
MoverParameters->Load = std::max( 0.f, MoverParameters->Load - exchangesize );
update_load_visibility();
}
if( m_exchange.unload_count < 0.01 ) {
// finish any potential unloading operation before adding new load
// don't load more than can fit
m_exchange.load_count = std::min( m_exchange.load_count, MoverParameters->MaxLoad - MoverParameters->Load );
while( ( m_exchange.load_count > 0.01 )
&& ( m_exchange.time >= 1.0 ) ) {
m_exchange.time -= 1.0;
auto const exchangesize = std::min( m_exchange.load_count, MoverParameters->LoadSpeed * m_exchange.speed_factor );
m_exchange.load_count -= exchangesize;
MoverParameters->LoadStatus = 2;
MoverParameters->Load = std::min( MoverParameters->MaxLoad, MoverParameters->Load + exchangesize ); // std::max not strictly needed but, eh
update_load_visibility();
}
}
}
if( MoverParameters->Vel > 2.0 ) {
// if the vehicle moves too fast cancel the exchange
m_exchange.unload_count = 0;
m_exchange.load_count = 0;
}
if( ( m_exchange.unload_count < 0.01 )
&& ( m_exchange.load_count < 0.01 ) ) {
MoverParameters->LoadStatus = 0;
// if the exchange is completed (or canceled) close the door, if applicable
if( ( MoverParameters->DoorCloseCtrl == control::passenger )
|| ( MoverParameters->DoorCloseCtrl == control::mixed ) ) {
MoverParameters->DoorLeft( false, range::local );
MoverParameters->DoorRight( false, range::local );
}
}
}
void TDynamicObject::LoadUpdate() {
// przeładowanie modelu ładunku
// Ra: nie próbujemy wczytywać modeli miliony razy podczas renderowania!!!
@@ -2494,7 +2587,11 @@ void TDynamicObject::LoadUpdate() {
}
if( mdLoad == nullptr ) {
// if this fails, try generic load model
mdLoad = TModelsManager::GetModel( asBaseDir + MoverParameters->LoadType, true );
auto const genericloadfilename { asBaseDir + MoverParameters->LoadType };
if( ( true == FileExists( genericloadfilename + ".e3d" ) )
|| ( true == FileExists( genericloadfilename + ".t3d" ) ) ) {
mdLoad = TModelsManager::GetModel( genericloadfilename, true );
}
}
if( mdLoad != nullptr ) {
// TODO: discern from vehicle component which merely uses vehicle directory and has no animations, so it can be initialized outright
@@ -2539,11 +2636,11 @@ TDynamicObject::update_load_sections() {
void
TDynamicObject::update_load_visibility() {
/*
if( Random() < 0.25 ) {
shuffle_load_sections();
}
*/
auto loadpercentage { (
MoverParameters->MaxLoad == 0.0 ?
0.0 :
@@ -3521,6 +3618,8 @@ bool TDynamicObject::Update(double dt, double dt1)
}
MoverParameters->DerailReason = 0; //żeby tylko raz
}
update_exchange( dt );
if (MoverParameters->LoadStatus)
LoadUpdate(); // zmiana modelu ładunku
@@ -3561,6 +3660,7 @@ bool TDynamicObject::FastUpdate(double dt)
// ResetdMoveLen();
FastMove(dDOMoveLen);
update_exchange( dt );
if (MoverParameters->LoadStatus)
LoadUpdate(); // zmiana modelu ładunku
return true; // Ra: chyba tak?
@@ -3757,6 +3857,19 @@ void TDynamicObject::RenderSounds() {
}
// other sounds
// load exchange
if( MoverParameters->LoadStatus & 1 ) {
m_exchangesounds.unloading.play( sound_flags::exclusive );
}
else {
m_exchangesounds.unloading.stop();
}
if( MoverParameters->LoadStatus & 2 ) {
m_exchangesounds.loading.play( sound_flags::exclusive );
}
else {
m_exchangesounds.loading.stop();
}
// NBMX sygnal odjazdu
if( MoverParameters->DoorClosureWarning ) {
if( MoverParameters->DepartureSignal ) {
@@ -4878,6 +4991,16 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName,
}
}
else if( token == "unloading:" ) {
m_exchangesounds.unloading.deserialize( parser, sound_type::single );
m_exchangesounds.unloading.owner( this );
}
else if( token == "loading:" ) {
m_exchangesounds.loading.deserialize( parser, sound_type::single );
m_exchangesounds.loading.owner( this );
}
else if( token == "sand:" ) {
sSand.deserialize( parser, sound_type::multipart, sound_parameters::range );
sSand.owner( this );

View File

@@ -264,6 +264,13 @@ private:
private:
// types
struct exchange_data {
float unload_count { 0.f }; // amount to unload
float load_count { 0.f }; // amount to load
float speed_factor { 1.f }; // operation speed modifier
float time { 0.f }; // time spent on the operation
};
struct coupler_sounds {
sound_source dsbCouplerAttach { sound_placement::external }; // moved from cab
sound_source dsbCouplerDetach { sound_placement::external }; // moved from cab
@@ -282,6 +289,11 @@ private:
sound_source rsDoorClose { sound_placement::general, 25.f };
};
struct exchange_sounds {
sound_source loading { sound_placement::general };
sound_source unloading { sound_placement::general };
};
struct axle_sounds {
double distance; // distance to rail joint
double offset; // axle offset from centre of the vehicle
@@ -318,6 +330,8 @@ private:
void ABuBogies();
void ABuModelRoll();
void TurnOff();
// update state of load exchange operation
void update_exchange( double const Deltatime );
// members
TButton btCoupler1; // sprzegi
@@ -385,6 +399,9 @@ private:
sound_source rscurve { sound_placement::external, EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; // youBy
sound_source rsDerailment { sound_placement::external, 250.f }; // McZapkie-051202
exchange_data m_exchange; // state of active load exchange procedure, if any
exchange_sounds m_exchangesounds; // sounds associated with the load exchange
Math3D::vector3 modelShake;
bool renderme; // yB - czy renderowac
@@ -466,6 +483,8 @@ private:
void create_controller( std::string const Type, bool const Trainset );
void AttachPrev(TDynamicObject *Object, int iType = 1);
bool UpdateForce(double dt, double dt1, bool FullVer);
// initiates load change by specified amounts, with a platform on specified side
void LoadExchange( int const Disembark, int const Embark, int const Platform );
void LoadUpdate();
void update_load_sections();
void update_load_visibility();
@@ -553,6 +572,7 @@ private:
void DestinationSet(std::string to, std::string numer);
std::string TextureTest(std::string const &name);
void OverheadTrack(float o);
double MED[9][8]; // lista zmiennych do debugowania hamulca ED
static std::string const MED_labels[ 8 ];
};

View File

@@ -153,7 +153,8 @@ enum coupling {
gangway = 0x10,
mainhose = 0x20,
heating = 0x40,
permanent = 0x80
permanent = 0x80,
uic = 0x100
};
// possible effect ranges for control commands; exclusive
enum range {
@@ -176,6 +177,15 @@ enum light {
redmarker_right = 0x20,
rearendsignals = 0x40
};
// door operation methods; exclusive
enum control {
passenger, // local, opened/closed for duration of loading
driver, // remote, operated by the driver
autonomous, // local, closed when vehicle moves and/or after timeout
conductor, // remote, operated by the conductor
mixed // primary manual but answers also to remote control
};
/*typ hamulca elektrodynamicznego*/
static int const dbrake_none = 0;
static int const dbrake_passive = 1;
@@ -307,7 +317,7 @@ struct TCommand
std::string Command; /*komenda*/
double Value1 = 0.0; /*argumenty komendy*/
double Value2 = 0.0;
int Coupling{ ctrain_controll }; // coupler flag used to determine command propagation
int Coupling { coupling::control }; // coupler flag used to determine command propagation
TLocation Location;
};
@@ -821,10 +831,10 @@ public:
static std::vector<std::string> const eimc_labels;
double InverterFrequency { 0.0 }; // current frequency of power inverters
/*-dla wagonow*/
double MaxLoad = 0.0; /*masa w T lub ilosc w sztukach - ladownosc*/
float MaxLoad = 0.f; /*masa w T lub ilosc w sztukach - ladownosc*/
std::string LoadAccepted; std::string LoadQuantity; /*co moze byc zaladowane, jednostki miary*/
double OverLoadFactor = 0.0; /*ile razy moze byc przekroczona ladownosc*/
double LoadSpeed = 0.0; double UnLoadSpeed = 0.0;/*szybkosc na- i rozladunku jednostki/s*/
float LoadSpeed = 0.f; float UnLoadSpeed = 0.f;/*szybkosc na- i rozladunku jednostki/s*/
int DoorOpenCtrl = 0; int DoorCloseCtrl = 0; /*0: przez pasazera, 1: przez maszyniste, 2: samoczynne (zamykanie)*/
double DoorStayOpen = 0.0; /*jak dlugo otwarte w przypadku DoorCloseCtrl=2*/
bool DoorClosureWarning = false; /*czy jest ostrzeganie przed zamknieciem*/
@@ -887,7 +897,7 @@ public:
bool CompressorAllowLocal{ true }; // local device state override (most units don't have this fitted so it's set to true not to intefere)
bool CompressorGovernorLock{ false }; // indicates whether compressor pressure switch was activated due to reaching cut-out pressure
// TODO converter parameters, for when we start cleaning up mover parameters
start ConverterStart{ manual }; // whether converter is started manually, or by other means
start ConverterStart{ start::manual }; // whether converter is started manually, or by other means
float ConverterStartDelay{ 0.0f }; // delay (in seconds) before the converter is started, once its activation conditions are met
double ConverterStartDelayTimer{ 0.0 }; // helper, for tracking whether converter start delay passed
bool ConverterAllow = false; /*zezwolenie na prace przetwornicy NBMX*/
@@ -1024,7 +1034,7 @@ public:
double eAngle = M_PI * 0.5;
/*-dla wagonow*/
double Load = 0.0; /*masa w T lub ilosc w sztukach - zaladowane*/
float Load = 0.f; /*masa w T lub ilosc w sztukach - zaladowane*/
std::string LoadType; /*co jest zaladowane*/
int LoadStatus = 0; //+1=trwa rozladunek,+2=trwa zaladunek,+4=zakończono,0=zaktualizowany model
double LastLoadChangeTime = 0.0; //raz (roz)ładowania
@@ -1220,8 +1230,8 @@ public:
/* funckje dla wagonow*/
bool LoadingDone(double LSpeed, std::string LoadInit);
bool DoorLeft(bool State); //obsluga drzwi lewych
bool DoorRight(bool State); //obsluga drzwi prawych
bool DoorLeft(bool State, int const Notify = range::consist ); //obsluga drzwi lewych
bool DoorRight(bool State, int const Notify = range::consist ); //obsluga drzwi prawych
bool DoorBlockedFlag(void); //sprawdzenie blokady drzwi
bool signal_departure( bool const State, int const Notify = range::consist ); // toggles departure warning

View File

@@ -6006,65 +6006,80 @@ bool TMoverParameters::DoorBlockedFlag(void)
// Q: 20160713
// Otwiera / zamyka lewe drzwi
// *************************************************************************************************
bool TMoverParameters::DoorLeft(bool State)
{
bool DL = false;
if ((DoorLeftOpened != State) && (DoorBlockedFlag() == false) && (Battery == true))
{
DL = true;
DoorLeftOpened = State;
if (State == true)
{
if (CabNo > 0)
SendCtrlToNext("DoorOpen", 1, CabNo); // 1=lewe, 2=prawe
else
SendCtrlToNext("DoorOpen", 2, CabNo); // zamiana
CompressedVolume -= 0.003;
}
else
{
if (CabNo > 0)
SendCtrlToNext("DoorClose", 1, CabNo);
else
SendCtrlToNext("DoorClose", 2, CabNo);
}
// NOTE: door methods work regardless of vehicle door control type,
// but commands issued through the command system work only for vehicles which accept remote door control
bool TMoverParameters::DoorLeft(bool State, int const Notify ) {
if( DoorLeftOpened == State ) {
// TBD: should the command be passed to other vehicles regardless of whether it affected the primary target?
// (for the time being no, methods are often invoked blindly which would lead to commands spam)
return false;
}
else
DL = false;
return DL;
bool result { false };
if( ( Battery == true )
&& ( DoorBlockedFlag() == false ) ) {
DoorLeftOpened = State;
result = true;
}
if( Notify != range::local ) {
SendCtrlToNext(
( State == true ?
"DoorOpen" :
"DoorClose" ),
( CabNo > 0 ? // 1=lewe, 2=prawe (swap if reversed)
1 :
2 ),
CabNo,
( Notify == range::unit ?
coupling::control | coupling::permanent :
coupling::control ) );
}
return result;
}
// *************************************************************************************************
// Q: 20160713
// Otwiera / zamyka prawe drzwi
// *************************************************************************************************
bool TMoverParameters::DoorRight(bool State)
{
bool DR = false;
if ((DoorRightOpened != State) && (DoorBlockedFlag() == false) && (Battery == true))
{
DR = true;
DoorRightOpened = State;
if (State == true)
{
if (CabNo > 0)
SendCtrlToNext("DoorOpen", 2, CabNo); // 1=lewe, 2=prawe
else
SendCtrlToNext("DoorOpen", 1, CabNo); // zamiana
CompressedVolume -= 0.003;
}
else
{
if (CabNo > 0)
SendCtrlToNext("DoorClose", 2, CabNo);
else
SendCtrlToNext("DoorClose", 1, CabNo);
}
}
else
DR = false;
// NOTE: door methods work regardless of vehicle door control type,
// but commands issued through the command system work only for vehicles which accept remote door control
bool TMoverParameters::DoorRight(bool State, int const Notify ) {
return DR;
if( DoorRightOpened == State ) {
// TBD: should the command be passed to other vehicles regardless of whether it affected the primary target?
// (for the time being no, methods are often invoked blindly which would lead to commands spam)
return false;
}
bool result { false };
if( ( Battery == true )
&& ( DoorBlockedFlag() == false ) ) {
DoorRightOpened = State;
result = true;
}
if( Notify != range::local ) {
SendCtrlToNext(
( State == true ?
"DoorOpen" :
"DoorClose" ),
( CabNo > 0 ? // 1=lewe, 2=prawe (swap if reversed)
2 :
1 ),
CabNo,
( Notify == range::unit ?
coupling::control | coupling::permanent :
coupling::control ) );
}
return result;
}
// toggles departure warning
@@ -6086,8 +6101,8 @@ TMoverParameters::signal_departure( bool const State, int const Notify ) {
0 ),
CabNo,
( Notify == range::unit ?
ctrain_controll | ctrain_depot :
ctrain_controll ) );
coupling::control | coupling::permanent :
coupling::control ) );
}
return true;
@@ -7184,8 +7199,8 @@ void TMoverParameters::LoadFIZ_Brake( std::string const &line ) {
auto lookup = compressorpowers.find( extract_value( "CompressorPower", line ) );
CompressorPower =
lookup != compressorpowers.end() ?
lookup->second :
1;
lookup->second :
1;
}
if( true == extract_value( AirLeakRate, "AirLeakRate", line, "" ) ) {
@@ -7196,16 +7211,31 @@ void TMoverParameters::LoadFIZ_Brake( std::string const &line ) {
void TMoverParameters::LoadFIZ_Doors( std::string const &line ) {
DoorOpenCtrl = 0;
std::string openctrl; extract_value( openctrl, "OpenCtrl", line, "" );
if( openctrl == "DriverCtrl" ) { DoorOpenCtrl = 1; }
DoorCloseCtrl = 0;
std::string closectrl; extract_value( closectrl, "CloseCtrl", line, "" );
if( closectrl == "DriverCtrl" ) { DoorCloseCtrl = 1; }
else if( closectrl == "AutomaticCtrl" ) { DoorCloseCtrl = 2; }
if( DoorCloseCtrl == 2 ) { extract_value( DoorStayOpen, "DoorStayOpen", line, "" ); }
std::map<std::string, int> doorcontrols {
{ "Passenger", control::passenger },
{ "AutomaticCtrl", control::autonomous },
{ "DriverCtrl", control::driver },
{ "Conductor", control::conductor },
{ "Mixed", control::mixed }
};
// opening method
{
auto lookup = doorcontrols.find( extract_value( "OpenCtrl", line ) );
DoorOpenCtrl =
lookup != doorcontrols.end() ?
lookup->second :
control::passenger;
}
// closing method
{
auto lookup = doorcontrols.find( extract_value( "CloseCtrl", line ) );
DoorCloseCtrl =
lookup != doorcontrols.end() ?
lookup->second :
control::passenger;
}
// automatic closing timer
if( DoorCloseCtrl == control::autonomous ) { extract_value( DoorStayOpen, "DoorStayOpen", line, "" ); }
extract_value( DoorOpenSpeed, "OpenSpeed", line, "" );
extract_value( DoorCloseSpeed, "CloseSpeed", line, "" );
@@ -8315,6 +8345,7 @@ bool TMoverParameters::SendCtrlToNext( std::string const CtrlCommand, double con
// musi być wybrana niezerowa kabina
if( ( Couplers[ d ].Connected != nullptr )
&& ( TestFlag( Couplers[ d ].CouplingFlag, Couplertype ) ) ) {
if( Couplers[ d ].ConnectedNr != d ) {
// jeśli ten nastpęny jest zgodny z aktualnym
if( Couplers[ d ].Connected->SetInternalCommand( CtrlCommand, ctrlvalue, dir, Couplertype ) )
@@ -8473,38 +8504,48 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C
}
else if (Command == "DoorOpen") /*NBMX*/
{ // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów
if ((CValue2 > 0))
{ // normalne ustawienie pojazdu
if ((CValue1 == 1) || (CValue1 == 3))
DoorLeftOpened = true;
if ((CValue1 == 2) || (CValue1 == 3))
DoorRightOpened = true;
}
else
{ // odwrotne ustawienie pojazdu
if ((CValue1 == 2) || (CValue1 == 3))
DoorLeftOpened = true;
if ((CValue1 == 1) || (CValue1 == 3))
DoorRightOpened = true;
}
if( ( DoorCloseCtrl == control::conductor )
|| ( DoorCloseCtrl == control::driver )
|| ( DoorCloseCtrl == control::mixed ) ) {
// ignore remote command if the door is only operated locally
if( CValue2 > 0 ) {
// normalne ustawienie pojazdu
if( ( CValue1 == 1 ) || ( CValue1 == 3 ) )
DoorLeftOpened = true;
if( ( CValue1 == 2 ) || ( CValue1 == 3 ) )
DoorRightOpened = true;
}
else {
// odwrotne ustawienie pojazdu
if( ( CValue1 == 2 ) || ( CValue1 == 3 ) )
DoorLeftOpened = true;
if( ( CValue1 == 1 ) || ( CValue1 == 3 ) )
DoorRightOpened = true;
}
}
OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype );
}
else if (Command == "DoorClose") /*NBMX*/
{ // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów
if ((CValue2 > 0))
{ // normalne ustawienie pojazdu
if ((CValue1 == 1) || (CValue1 == 3))
DoorLeftOpened = false;
if ((CValue1 == 2) || (CValue1 == 3))
DoorRightOpened = false;
}
else
{ // odwrotne ustawienie pojazdu
if ((CValue1 == 2) || (CValue1 == 3))
DoorLeftOpened = false;
if ((CValue1 == 1) || (CValue1 == 3))
DoorRightOpened = false;
}
if( ( DoorCloseCtrl == control::conductor )
|| ( DoorCloseCtrl == control::driver )
|| ( DoorCloseCtrl == control::mixed ) ) {
// ignore remote command if the door is only operated locally
if( CValue2 > 0 ) {
// normalne ustawienie pojazdu
if( ( CValue1 == 1 ) || ( CValue1 == 3 ) )
DoorLeftOpened = false;
if( ( CValue1 == 2 ) || ( CValue1 == 3 ) )
DoorRightOpened = false;
}
else {
// odwrotne ustawienie pojazdu
if( ( CValue1 == 2 ) || ( CValue1 == 3 ) )
DoorLeftOpened = false;
if( ( CValue1 == 1 ) || ( CValue1 == 3 ) )
DoorRightOpened = false;
}
}
OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype );
}
else if( Command == "DepartureSignal" ) {

View File

@@ -375,12 +375,12 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d)
}
}
*/
MechSpring.Init(250);
MechSpring.Init(125.0);
vMechVelocity = Math3D::vector3(0, 0, 0);
pMechOffset = Math3D::vector3( 0, 0, 0 );
fMechSpringX = 1;
fMechSpringY = 0.5;
fMechSpringZ = 0.5;
fMechSpringX = 0.2;
fMechSpringY = 0.2;
fMechSpringZ = 0.1;
fMechMaxSpring = 0.15;
fMechRoll = 0.05;
fMechPitch = 0.1;
@@ -3283,9 +3283,6 @@ void TTrain::OnCommand_doorlocktoggle( TTrain *Train, command_data const &Comman
void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Command ) {
if( Train->mvOccupied->DoorOpenCtrl != 1 ) {
return;
}
if( Command.action == GLFW_PRESS ) {
// NOTE: test how the door state check works with consists where the occupied vehicle doesn't have opening doors
if( false == (
@@ -3293,76 +3290,74 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman
Train->mvOccupied->DoorLeftOpened :
Train->mvOccupied->DoorRightOpened ) ) {
// open
if( Train->mvOccupied->DoorOpenCtrl != control::driver ) {
return;
}
if( Train->mvOccupied->ActiveCab == 1 ) {
if( Train->mvOccupied->DoorLeft( true ) ) {
Train->ggDoorLeftButton.UpdateValue( 1.0, Train->dsbSwitch );
}
Train->mvOccupied->DoorLeft( true );
}
else {
// in the rear cab sides are reversed...
if( Train->mvOccupied->DoorRight( true ) ) {
// ...but so are the switches
Train->ggDoorLeftButton.UpdateValue( 1.0, Train->dsbSwitch );
}
Train->mvOccupied->DoorRight( true );
}
// visual feedback
Train->ggDoorLeftButton.UpdateValue( 1.0, Train->dsbSwitch );
}
else {
// close
if( Train->mvOccupied->DoorCloseCtrl != control::driver ) {
return;
}
// TODO: move door opening/closing to the update, so the switch animation doesn't hinge on door working
if( Train->mvOccupied->ActiveCab == 1 ) {
if( Train->mvOccupied->DoorLeft( false ) ) {
Train->ggDoorLeftButton.UpdateValue( 0.0, Train->dsbSwitch );
}
Train->mvOccupied->DoorLeft( false );
}
else {
// in the rear cab sides are reversed...
if( Train->mvOccupied->DoorRight( false ) ) {
// ...but so are the switches
Train->ggDoorLeftButton.UpdateValue( 0.0, Train->dsbSwitch );
}
Train->mvOccupied->DoorRight( false );
}
// visual feedback
Train->ggDoorLeftButton.UpdateValue( 0.0, Train->dsbSwitch );
}
}
}
void TTrain::OnCommand_doortoggleright( TTrain *Train, command_data const &Command ) {
if( Train->mvOccupied->DoorOpenCtrl != 1 ) {
return;
}
if( Command.action == GLFW_PRESS ) {
// NOTE: test how the door state check works with consists where the occupied vehicle doesn't have opening doors
if( false == (
Train->mvOccupied->ActiveCab == 1 ?
Train->mvOccupied->DoorRightOpened :
Train->mvOccupied->DoorLeftOpened ) ) {
Train->mvOccupied->DoorRightOpened :
Train->mvOccupied->DoorLeftOpened ) ) {
// open
if( Train->mvOccupied->DoorOpenCtrl != control::driver ) {
return;
}
if( Train->mvOccupied->ActiveCab == 1 ) {
if( Train->mvOccupied->DoorRight( true ) ) {
Train->ggDoorRightButton.UpdateValue( 1.0, Train->dsbSwitch );
}
Train->mvOccupied->DoorRight( true );
}
else {
// in the rear cab sides are reversed...
if( Train->mvOccupied->DoorLeft( true ) ) {
// ...but so are the switches
Train->ggDoorRightButton.UpdateValue( 1.0, Train->dsbSwitch );
}
Train->mvOccupied->DoorLeft( true );
}
// visual feedback
Train->ggDoorRightButton.UpdateValue( 1.0, Train->dsbSwitch );
}
else {
// close
if( Train->mvOccupied->DoorCloseCtrl != control::driver ) {
return;
}
if( Train->mvOccupied->ActiveCab == 1 ) {
if( Train->mvOccupied->DoorRight( false ) ) {
Train->ggDoorRightButton.UpdateValue( 0.0, Train->dsbSwitch );
}
Train->mvOccupied->DoorRight( false );
}
else {
// in the rear cab sides are reversed...
if( Train->mvOccupied->DoorLeft( false ) ) {
// ...but so are the switches
Train->ggDoorRightButton.UpdateValue( 0.0, Train->dsbSwitch );
}
Train->mvOccupied->DoorLeft( false );
}
// visual feedback
Train->ggDoorRightButton.UpdateValue( 0.0, Train->dsbSwitch );
}
}
}

View File

@@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/.
// exchanges load with consist attached to specified vehicle, operating on specified schedule
double
basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule ) {
basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule, int const Platform ) {
auto const firststop { Schedule.StationIndex == 1 };
auto const laststop { Schedule.StationIndex == Schedule.StationCount };
@@ -30,7 +30,13 @@ basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Sch
auto const stationsizemodifier { ( trainstop ? 1.0 : 2.0 ) };
// go through all vehicles and update their load
// NOTE: for the time being we limit ourselves to passenger-carrying cars only
auto exchangetime { 0.0 };
auto exchangetime { 0.f };
// platform (1:left, 2:right, 3:both)
// with exchange performed on both sides waiting times are halved
auto const exchangetimemodifier { (
Platform == 3 ?
0.5f :
1.0f ) };
auto *vehicle { First };
while( vehicle != nullptr ) {
@@ -47,24 +53,29 @@ basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Sch
// NOTE: for the time being we're doing simple, random load change calculation
// TODO: exchange driven by station parameters and time of the day
auto unloadcount = static_cast<int>(
firststop ? 0 :
laststop ? parameters.Load :
std::min(
firststop ? 0 :
std::min<float>(
parameters.Load,
Random( parameters.MaxLoad * 0.10 * stationsizemodifier ) ) );
auto loadcount = static_cast<int>(
laststop ?
0 :
Random( parameters.MaxLoad * 0.15 * stationsizemodifier ) );
Random( parameters.MaxLoad * 0.15f * stationsizemodifier ) );
if( true == firststop ) {
// slightly larger group at the initial station
loadcount *= 2;
}
parameters.Load = std::min( parameters.MaxLoad, parameters.Load - unloadcount + loadcount );
vehicle->LoadUpdate();
vehicle->update_load_visibility();
exchangetime = std::max( exchangetime, unloadcount / parameters.UnLoadSpeed + loadcount / parameters.LoadSpeed);
if( ( unloadcount > 0 ) || ( loadcount > 0 ) ) {
vehicle->LoadExchange( unloadcount, loadcount, Platform );
/*
vehicle->LoadUpdate();
vehicle->update_load_visibility();
*/
exchangetime = std::max( exchangetime, exchangetimemodifier * ( unloadcount / parameters.UnLoadSpeed + loadcount / parameters.LoadSpeed ) );
}
}
vehicle = vehicle->Next();
}

View File

@@ -18,5 +18,5 @@ public:
// methods
// exchanges load with consist attached to specified vehicle, operating on specified schedule; returns: time needed for exchange, in seconds
double
update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule );
update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule, int const Platform );
};