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

lpt diagnostics, tempomat configuration enhancements, ai cargo train driving logic tweaks, partial ai support for universal controller in diesel-electric vehicles

This commit is contained in:
tmj-fstate
2020-05-23 23:14:54 +02:00
parent 52167f7955
commit 10dabd66c3
10 changed files with 208 additions and 95 deletions

View File

@@ -9,6 +9,7 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h" #include "stdafx.h"
#include "LPT.h" #include "LPT.h"
#include "Logs.h"
// LPT na USB: // LPT na USB:
// USB\VID_067B&PID_2305&REV_0200 // USB\VID_067B&PID_2305&REV_0200
@@ -24,6 +25,8 @@ OutPortType OutPort;
bool TLPT::Connect(int port) bool TLPT::Connect(int port)
{ {
WriteLog( "LPT: trying to access port " + std::to_string(port) );
// ladowanie dll-ki // ladowanie dll-ki
hDLL = LoadLibrary("inpout32.dll"); hDLL = LoadLibrary("inpout32.dll");
if (hDLL) if (hDLL)
@@ -32,9 +35,11 @@ bool TLPT::Connect(int port)
OutPort = (OutPortType)GetProcAddress(hDLL, "Out32"); OutPort = (OutPortType)GetProcAddress(hDLL, "Out32");
} }
else else
return false; // MessageBox(NULL,"ERROR","Błąd przy ładowaniu pliku",MB_OK); {
address = WriteLog("LPT: failed to load inpout32");
port; //&0xFFFFFC; //ostatnie 2 bity mają być zerowe -> a niech sobie OUT-ują, gdzie chcą return false;
}
address = port;
switch (address) // nie dotyczy 0x3BC switch (address) // nie dotyczy 0x3BC
{ {
case 0x0378: case 0x0378:
@@ -45,6 +50,9 @@ bool TLPT::Connect(int port)
case 0xBD00: case 0xBD00:
OutPort(address + 0x006, 0); // 0xBC06? czysta improwizacja OutPort(address + 0x006, 0); // 0xBC06? czysta improwizacja
} }
WriteLog("LPT: setup complete");
return OutPort != 0; return OutPort != 0;
}; };

View File

@@ -153,6 +153,8 @@ const double EasyReactionTime = 0.5; //[s] przebłyski świadomości dla zwykłe
const double HardReactionTime = 0.2; const double HardReactionTime = 0.2;
const double EasyAcceleration = 0.85; //[m/ss] const double EasyAcceleration = 0.85; //[m/ss]
const double HardAcceleration = 9.81; const double HardAcceleration = 9.81;
const double CargoTrainAcceleration = 0.25;
const double HeavyCargoTrainAcceleration = 0.10;
const double PrepareTime = 2.0; //[s] przebłyski świadomości przy odpalaniu const double PrepareTime = 2.0; //[s] przebłyski świadomości przy odpalaniu
bool WriteLogFlag = false; bool WriteLogFlag = false;
double const deltalog = 0.05; // przyrost czasu double const deltalog = 0.05; // przyrost czasu
@@ -1822,7 +1824,7 @@ void TController::OrdersClear()
for (int b = 0; b < maxorders; b++) for (int b = 0; b < maxorders; b++)
OrderList[b] = Wait_for_orders; OrderList[b] = Wait_for_orders;
#if LOGORDERS #if LOGORDERS
WriteLog("--> OrdersClear"); OrdersDump("OrdersClear", false);
#endif #endif
}; };
@@ -2052,7 +2054,7 @@ void TController::AutoRewident()
} }
IsCargoTrain = ( mvOccupied->CategoryFlag == 1 ) && ( ( mvOccupied->BrakeDelayFlag & bdelay_G ) != 0 ); IsCargoTrain = ( mvOccupied->CategoryFlag == 1 ) && ( ( mvOccupied->BrakeDelayFlag & bdelay_G ) != 0 );
IsHeavyCargoTrain = ( true == IsCargoTrain ) && ( fBrake_a0[ 1 ] > 0.4 ); IsHeavyCargoTrain = ( true == IsCargoTrain ) && ( fBrake_a0[ 1 ] > 0.4 ) && ( iVehicles - ControlledEnginesCount > 0 ) && ( fMass / iVehicles > 50000 );
BrakingInitialLevel = ( BrakingInitialLevel = (
IsHeavyCargoTrain ? 1.25 : IsHeavyCargoTrain ? 1.25 :
@@ -3176,6 +3178,10 @@ bool TController::IncSpeed()
// gdy jest nakaz poczekać z jazdą, to nie ruszać // gdy jest nakaz poczekać z jazdą, to nie ruszać
return false; return false;
} }
if( IsAnyCouplerStretched ) {
// train is already stretched past its limits, don't pull even harder
return false;
}
bool OK = true; bool OK = true;
if( ( iDrivigFlags & moveDoorOpened ) if( ( iDrivigFlags & moveDoorOpened )
&& ( VelDesired > 0.0 ) ) { // to prevent door shuffle on stop && ( VelDesired > 0.0 ) ) { // to prevent door shuffle on stop
@@ -3221,8 +3227,8 @@ bool TController::IncSpeed()
// if the power station is heavily burdened, // if the power station is heavily burdened,
// if it generates enough traction force // if it generates enough traction force
// to build up speed to 30/40 km/h for passenger/cargo train (10 km/h less if going uphill) // to build up speed to 30/40 km/h for passenger/cargo train (10 km/h less if going uphill)
auto const sufficienttractionforce { std::abs( mvControlling->Ft ) > ( IsHeavyCargoTrain ? 125 : 100 ) * 1000.0 }; auto const sufficienttractionforce { std::abs( mvControlling->Ft ) > ( IsHeavyCargoTrain ? 75 : 50 ) * 1000.0 };
auto const sufficientacceleration { AbsAccS_pub >= ( IsHeavyCargoTrain ? 0.02 : 0.04 ) }; auto const sufficientacceleration { AbsAccS_pub >= ( IsHeavyCargoTrain ? 0.03 : IsCargoTrain ? 0.06 : 0.09 ) };
auto const seriesmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn == 1 ) }; auto const seriesmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn == 1 ) };
auto const parallelmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 ) }; auto const parallelmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 ) };
auto const useseriesmodevoltage { auto const useseriesmodevoltage {
@@ -3233,9 +3239,9 @@ bool TController::IncSpeed()
auto const useseriesmode = ( auto const useseriesmode = (
( mvControlling->Imax > mvControlling->ImaxLo ) ( mvControlling->Imax > mvControlling->ImaxLo )
|| ( fVoltage < useseriesmodevoltage ) || ( fVoltage < useseriesmodevoltage )
|| ( ( true == sufficienttractionforce ) || ( ( true == sufficientacceleration )
&& ( true == sufficientacceleration ) && ( true == sufficienttractionforce )
&& ( mvOccupied->Vel <= ( IsCargoTrain ? 35 : 25 ) + ( seriesmodefieldshunting ? 5 : 0 ) - ( ( fAccGravity < -0.025 ) ? 10 : 0 ) ) ) ); && ( mvOccupied->Vel <= ( IsCargoTrain ? 40 : 30 ) + ( seriesmodefieldshunting ? 5 : 0 ) - ( ( fAccGravity < -0.025 ) ? 10 : 0 ) ) ) );
// when not in series mode use the first available parallel mode configuration until 50/60 km/h for passenger/cargo train // when not in series mode use the first available parallel mode configuration until 50/60 km/h for passenger/cargo train
// (if there's only one parallel mode configuration it'll be used regardless of current speed) // (if there's only one parallel mode configuration it'll be used regardless of current speed)
auto const usefieldshunting = ( auto const usefieldshunting = (
@@ -3243,9 +3249,9 @@ bool TController::IncSpeed()
&& ( mvControlling->RList[ mvControlling->MainCtrlPos ].R < 0.01 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].R < 0.01 )
&& ( useseriesmode ? && ( useseriesmode ?
mvControlling->RList[ mvControlling->MainCtrlPos ].Bn == 1 : mvControlling->RList[ mvControlling->MainCtrlPos ].Bn == 1 :
( ( true == sufficienttractionforce ) ( ( true == sufficientacceleration )
&& ( true == sufficientacceleration ) && ( true == sufficienttractionforce )
&& ( mvOccupied->Vel <= ( IsCargoTrain ? 55 : 45 ) + ( parallelmodefieldshunting ? 5 : 0 ) ) ? && ( mvOccupied->Vel <= ( IsCargoTrain ? 60 : 50 ) + ( parallelmodefieldshunting ? 5 : 0 ) ) ?
mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 : mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 :
mvControlling->MainCtrlPos == mvControlling->MainCtrlPosNo ) ) ); mvControlling->MainCtrlPos == mvControlling->MainCtrlPosNo ) ) );
@@ -3282,7 +3288,7 @@ bool TController::IncSpeed()
mvControlling->RList[ std::min( mvControlling->MainCtrlPos + 1, mvControlling->MainCtrlPosNo ) ].Bn == 1 ? mvControlling->RList[ std::min( mvControlling->MainCtrlPos + 1, mvControlling->MainCtrlPosNo ) ].Bn == 1 ?
mvControlling->EnginePowerSource.CollectorParameters.MinV : mvControlling->EnginePowerSource.CollectorParameters.MinV :
useseriesmodevoltage ) useseriesmodevoltage )
> ( IsHeavyCargoTrain ? 80.0 : 60.0 ) * ControlledEnginesCount }; > ( IsHeavyCargoTrain ? 100.0 : 75.0 ) * ControlledEnginesCount };
OK = ( OK = (
( sufficientpowermargin && ( false == mvControlling->DelayCtrlFlag ) ) ? ( sufficientpowermargin && ( false == mvControlling->DelayCtrlFlag ) ) ?
@@ -3425,20 +3431,20 @@ bool TController::DecSpeed(bool force)
iDrivigFlags &= ~moveIncSpeed; // usunięcie flagi jazdy iDrivigFlags &= ~moveIncSpeed; // usunięcie flagi jazdy
if (force) // przy aktywacji kabiny jest potrzeba natychmiastowego wyzerowania if (force) // przy aktywacji kabiny jest potrzeba natychmiastowego wyzerowania
if (mvControlling->MainCtrlPosNo > 0) // McZapkie-041003: wagon sterowniczy, np. EZT if (mvControlling->MainCtrlPosNo > 0) // McZapkie-041003: wagon sterowniczy, np. EZT
mvControlling->DecMainCtrl((mvControlling->MainCtrlPowerPos() > 1 ? 2 : 1)); mvControlling->DecMainCtrl( std::min( mvControlling->MainCtrlPowerPos(), 2 ) );
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
return false; return false;
case TEngineType::ElectricSeriesMotor: case TEngineType::ElectricSeriesMotor:
OK = mvControlling->DecScndCtrl(2); // najpierw bocznik na zero OK = mvControlling->DecScndCtrl(2); // najpierw bocznik na zero
if (!OK) if (!OK)
OK = mvControlling->DecMainCtrl((mvControlling->MainCtrlPowerPos() > 1 ? 2 : 1)); OK = mvControlling->DecMainCtrl( std::min( mvControlling->MainCtrlPowerPos(), 2 ) );
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
break; break;
case TEngineType::Dumb: case TEngineType::Dumb:
case TEngineType::DieselElectric: case TEngineType::DieselElectric:
OK = mvControlling->DecScndCtrl(2); OK = mvControlling->DecScndCtrl(2);
if (!OK) if (!OK)
OK = mvControlling->DecMainCtrl(2 + (mvControlling->MainCtrlPowerPos() / 2)); OK = mvControlling->DecMainCtrl( std::min( mvControlling->MainCtrlPowerPos(), ( 2 + (mvControlling->MainCtrlPowerPos() / 2) ) ) );
break; break;
case TEngineType::ElectricInductionMotor: case TEngineType::ElectricInductionMotor:
OK = DecSpeedEIM(); OK = DecSpeedEIM();
@@ -3515,13 +3521,13 @@ bool TController::IncSpeedEIM() {
return OK; return OK;
} }
bool TController::DecSpeedEIM() bool TController::DecSpeedEIM( int const Amount )
{ // zmniejszenie prędkości (ale nie hamowanie) { // zmniejszenie prędkości (ale nie hamowanie)
bool OK = false; // domyślnie false, aby wyszło z pętli while bool OK = false; // domyślnie false, aby wyszło z pętli while
switch (mvControlling->EIMCtrlType) switch (mvControlling->EIMCtrlType)
{ {
case 0: case 0:
OK = mvControlling->DecMainCtrl(1); OK = mvControlling->DecMainCtrl( Amount );
break; break;
case 1: case 1:
OK = mvControlling->MainCtrlPos > 4; OK = mvControlling->MainCtrlPos > 4;
@@ -3909,6 +3915,38 @@ void TController::SetTimeControllers()
} }
} }
} }
// 5.5 universal control for diesel electric vehicles
if ((mvControlling->EngineType == TEngineType::DieselElectric) && (mvControlling->EIMCtrlType == 3))
{
// NOTE: partial implementation for speedinc/dec
// TBD, TODO: implement fully?
if( ( AccDesired >= 0.0 )
&& ( mvControlling->StLinFlag ) ) {
auto const PosInc { mvControlling->MainCtrlPosNo };
auto PosKeep { 0 };
auto PosDec{ 0 };
for( int i = PosInc; i >= 0; --i ) {
if( mvControlling->UniCtrlList[ i ].SetCtrlVal <= 0 ) {
if( mvControlling->UniCtrlList[ i ].SpeedDown == 0.0 ) {
PosKeep = i;
}
if( mvControlling->UniCtrlList[ i ].SpeedDown > 0.01 ) {
PosDec = i;
break;
}
}
}
auto const DesiredPos { (
AccDesired > AbsAccS_pub + 0.05 ? PosInc :
AccDesired < AbsAccS_pub - 0.05 ? PosDec :
PosKeep ) };
while( ( mvControlling->MainCtrlPos > DesiredPos ) && mvControlling->DecMainCtrl( 1 ) ) { ; }
while( ( mvControlling->MainCtrlPos < DesiredPos ) && mvControlling->IncMainCtrl( 1 ) ) { ; }
}
}
//6. UniversalBrakeButtons //6. UniversalBrakeButtons
//6.1. Checking flags for Over pressure //6.1. Checking flags for Over pressure
if (std::abs(BrakeCtrlPosition - gbh_FS)<0.5) { if (std::abs(BrakeCtrlPosition - gbh_FS)<0.5) {
@@ -4493,8 +4531,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
OrderPos = 1; OrderPos = 1;
} }
#if LOGORDERS #if LOGORDERS
WriteLog("--> Jump_to_order"); OrdersDump("Jump_to_order");
OrdersDump();
#endif #endif
} }
return true; return true;
@@ -4658,6 +4695,7 @@ TController::UpdateSituation(double dt) {
fAccGravity = 0.0; // przyspieszenie wynikające z pochylenia fAccGravity = 0.0; // przyspieszenie wynikające z pochylenia
double dy; // składowa styczna grawitacji, w przedziale <0,1> double dy; // składowa styczna grawitacji, w przedziale <0,1>
double AbsAccS = 0; double AbsAccS = 0;
IsAnyCouplerStretched = false;
IsAnyDoorOpen[ side::right ] = IsAnyDoorOpen[ side::left ] = false; IsAnyDoorOpen[ side::right ] = IsAnyDoorOpen[ side::left ] = false;
ConsistShade = 0.0; ConsistShade = 0.0;
TDynamicObject *p = pVehicles[0]; // pojazd na czole składu TDynamicObject *p = pVehicles[0]; // pojazd na czole składu
@@ -4696,6 +4734,11 @@ TController::UpdateSituation(double dt) {
// ciężar razy składowa styczna grawitacji // ciężar razy składowa styczna grawitacji
fAccGravity -= vehicle->TotalMassxg * dy * ( p->DirectionGet() == iDirection ? 1 : -1 ); fAccGravity -= vehicle->TotalMassxg * dy * ( p->DirectionGet() == iDirection ? 1 : -1 );
} }
// check coupler state
IsAnyCouplerStretched =
IsAnyCouplerStretched
|| ( vehicle->Couplers[ end::front ].stretch_duration > 0.0 )
|| ( vehicle->Couplers[ end::rear ].stretch_duration > 0.0 );
// check door state // check door state
auto const switchsides { p->DirectionGet() != iDirection }; auto const switchsides { p->DirectionGet() != iDirection };
IsAnyDoorOpen[ side::right ] = IsAnyDoorOpen[ side::right ] =
@@ -5385,8 +5428,7 @@ TController::UpdateSituation(double dt) {
else else
++OrderPos; ++OrderPos;
#if LOGORDERS #if LOGORDERS
WriteLog("--> Jump_to_first_order"); OrdersDump("Jump_to_first_order");
OrdersDump();
#endif #endif
break; break;
case Wait_for_orders: // jeśli czeka, też ma skanować, żeby odpalić się od semafora case Wait_for_orders: // jeśli czeka, też ma skanować, żeby odpalić się od semafora
@@ -6174,6 +6216,14 @@ TController::UpdateSituation(double dt) {
*/ */
} }
} }
// HACK: limit acceleration for cargo trains, to reduce probability of breaking couplers on sudden jolts
// TBD: expand this behaviour to all trains with car(s) exceeding certain weight?
if( IsCargoTrain ) {
AccDesired = std::min( AccDesired, CargoTrainAcceleration );
}
if( IsHeavyCargoTrain ) {
AccDesired = std::min( AccDesired, HeavyCargoTrainAcceleration );
}
} }
else { else {
// for cars the older version works better // for cars the older version works better
@@ -6283,7 +6333,7 @@ TController::UpdateSituation(double dt) {
if( vel < VelDesired ) { if( vel < VelDesired ) {
// don't adjust acceleration when going above current goal speed // don't adjust acceleration when going above current goal speed
if( -AccDesired * BrakeAccFactor() < ( if( -AccDesired * BrakeAccFactor() < (
( ( fReady > ( ( mvOccupied->Vel > 5.0 ) ? 0.5 : 0.4 ) ) ( ( fReady > ( IsHeavyCargoTrain ? 0.4 : ( mvOccupied->Vel > 5.0 ) ? 0.45 : 0.4 ) )
|| ( VelNext > vel - 40.0 ) ) ? || ( VelNext > vel - 40.0 ) ) ?
fBrake_a0[ 0 ] * 0.8 : fBrake_a0[ 0 ] * 0.8 :
-fAccThreshold ) -fAccThreshold )
@@ -6386,6 +6436,7 @@ TController::UpdateSituation(double dt) {
", VelSignal=" + AnsiString(VelSignal) + ", VelNext=" + ", VelSignal=" + AnsiString(VelSignal) + ", VelNext=" +
AnsiString(VelNext)); AnsiString(VelNext));
#endif #endif
/*
if( ( vel < 10.0 ) if( ( vel < 10.0 )
&& ( AccDesired > 0.1 ) ) { && ( AccDesired > 0.1 ) ) {
// Ra 2F1H: jeśli prędkość jest mała, a można przyspieszać, // Ra 2F1H: jeśli prędkość jest mała, a można przyspieszać,
@@ -6393,6 +6444,7 @@ TController::UpdateSituation(double dt) {
// przy małych prędkościach może być trudno utrzymać // przy małych prędkościach może być trudno utrzymać
AccDesired = std::max( 0.9, AccDesired ); AccDesired = std::max( 0.9, AccDesired );
} }
*/
// małe przyspieszenie // małe przyspieszenie
// Ra 2F1I: wyłączyć kiedyś to uśrednianie i przeanalizować skanowanie, czemu migocze // Ra 2F1I: wyłączyć kiedyś to uśrednianie i przeanalizować skanowanie, czemu migocze
if (AccDesired > -0.05) // hamowania lepeiej nie uśredniać if (AccDesired > -0.05) // hamowania lepeiej nie uśredniać
@@ -6454,10 +6506,11 @@ TController::UpdateSituation(double dt) {
else if( ( vel > VelDesired + SpeedCtrlMargin) else if( ( vel > VelDesired + SpeedCtrlMargin)
|| ( fAccGravity < -0.01 ? || ( fAccGravity < -0.01 ?
AccDesired < 0.0 : AccDesired < 0.0 :
(AbsAccS > AccDesired && AccDesired < 0.75) ) ) { (AbsAccS > AccDesired + 0.05) )
|| ( IsAnyCouplerStretched ) ) {
// jak za bardzo przyspiesza albo prędkość przekroczona // jak za bardzo przyspiesza albo prędkość przekroczona
// dodany wyjatek na "pelna w przod" // dodany wyjatek na "pelna w przod"
DecSpeed(); // pojedyncze cofnięcie pozycji, bo na zero to przesada DecSpeed();
} }
} }
if( mvOccupied->TrainType == dt_EZT ) { if( mvOccupied->TrainType == dt_EZT ) {
@@ -6669,8 +6722,7 @@ void TController::JumpToNextOrder()
} }
OrderCheck(); OrderCheck();
#if LOGORDERS #if LOGORDERS
WriteLog("--> JumpToNextOrder"); OrdersDump("JumpToNextOrder"); // normalnie nie ma po co tego wypisywać
OrdersDump(); // normalnie nie ma po co tego wypisywać
#endif #endif
}; };
@@ -6681,8 +6733,7 @@ void TController::JumpToFirstOrder()
OrderTop = 1; OrderTop = 1;
OrderCheck(); OrderCheck();
#if LOGORDERS #if LOGORDERS
WriteLog("--> JumpToFirstOrder"); OrdersDump("JumpToFirstOrder"); // normalnie nie ma po co tego wypisywać
OrdersDump(); // normalnie nie ma po co tego wypisywać
#endif #endif
}; };
@@ -6733,8 +6784,7 @@ void TController::OrderNext(TOrders NewOrder)
} }
OrderList[OrderTop++] = NewOrder; // dodanie rozkazu jako następnego OrderList[OrderTop++] = NewOrder; // dodanie rozkazu jako następnego
#if LOGORDERS #if LOGORDERS
WriteLog("--> OrderNext"); OrdersDump( "OrderNext" ); // normalnie nie ma po co tego wypisywać
OrdersDump(); // normalnie nie ma po co tego wypisywać
#endif #endif
} }
@@ -6749,17 +6799,17 @@ void TController::OrderPush(TOrders NewOrder)
if (OrderTop >= maxorders) if (OrderTop >= maxorders)
ErrorLog("Commands overflow: The program will now crash"); ErrorLog("Commands overflow: The program will now crash");
#if LOGORDERS #if LOGORDERS
WriteLog("--> OrderPush: [" + Order2Str( NewOrder ) + "]"); OrdersDump( "OrderPush: [" + Order2Str( NewOrder ) + "]" ); // normalnie nie ma po co tego wypisywać
OrdersDump(); // normalnie nie ma po co tego wypisywać
#endif #endif
} }
void TController::OrdersDump() void TController::OrdersDump( std::string const Neworder, bool const Verbose )
{ // wypisanie kolejnych rozkazów do logu { // wypisanie kolejnych rozkazów do logu
WriteLog("Orders for " + pVehicle->asName + ":"); WriteLog( "New order for [" + pVehicle->asName + "]: " + Neworder );
if( false == Verbose ) { return; }
for (int b = 0; b < maxorders; ++b) for (int b = 0; b < maxorders; ++b)
{ {
WriteLog((std::to_string(b) + ": " + Order2Str(OrderList[b]) + (OrderPos == b ? " <-" : ""))); WriteLog( ( OrderPos == b ? ">" : " " ) + (std::to_string(b) + ": " + Order2Str(OrderList[b])));
if (b) // z wyjątkiem pierwszej pozycji if (b) // z wyjątkiem pierwszej pozycji
if (OrderList[b] == Wait_for_orders) // jeśli końcowa komenda if (OrderList[b] == Wait_for_orders) // jeśli końcowa komenda
break; // dalej nie trzeba break; // dalej nie trzeba
@@ -6848,10 +6898,8 @@ void TController::OrdersInit(double fVel)
SetVelocity(0, 0, stopSleep); // prędkość w przedziale (0;1) oznacza, że ma stać SetVelocity(0, 0, stopSleep); // prędkość w przedziale (0;1) oznacza, że ma stać
} }
#if LOGORDERS #if LOGORDERS
WriteLog("--> OrdersInit"); OrdersDump( "OrdersInit", DebugModeFlag ); // wypisanie kontrolne tabelki rozkazów
#endif #endif
if (DebugModeFlag) // normalnie nie ma po co tego wypisywać
OrdersDump(); // wypisanie kontrolne tabelki rozkazów
// McZapkie! - zeby w ogole AI ruszyl to musi wykonac powyzsze rozkazy // McZapkie! - zeby w ogole AI ruszyl to musi wykonac powyzsze rozkazy
// Ale mozna by je zapodac ze scenerii // Ale mozna by je zapodac ze scenerii
}; };

View File

@@ -240,7 +240,7 @@ private:
bool IncBrakeEIM(); bool IncBrakeEIM();
bool DecBrakeEIM(); bool DecBrakeEIM();
bool IncSpeedEIM(); bool IncSpeedEIM();
bool DecSpeedEIM(); bool DecSpeedEIM( int const Amount = 1 );
void BrakeLevelSet( double b ); void BrakeLevelSet( double b );
bool BrakeLevelAdd( double b ); bool BrakeLevelAdd( double b );
void SpeedSet(); void SpeedSet();
@@ -370,7 +370,7 @@ private:
void OrderCheck(); void OrderCheck();
void OrdersInit( double fVel ); void OrdersInit( double fVel );
void OrdersClear(); void OrdersClear();
void OrdersDump(); void OrdersDump( std::string const Neworder, bool const Verbose = true );
std::string Order2Str( TOrders Order ) const; std::string Order2Str( TOrders Order ) const;
// members // members
std::string m_assignment; std::string m_assignment;
@@ -484,6 +484,7 @@ private:
bool IsAnyMotorOverloadRelayOpen{ false }; // state of motor overload relays in all vehicles under control bool IsAnyMotorOverloadRelayOpen{ false }; // state of motor overload relays in all vehicles under control
bool IsAnyGroundRelayOpen{ false }; bool IsAnyGroundRelayOpen{ false };
bool IsAnyCompressorEnabled{ false }; bool IsAnyCompressorEnabled{ false };
bool IsAnyCouplerStretched{ false }; // whether there's a coupler in the consist stretched above limit
// logs // logs
// methods // methods

View File

@@ -1552,7 +1552,7 @@ TDynamicObject::uncouple( int const Side ) {
} }
bool bool
TDynamicObject::attach_coupler_adapter( int const Side ) { TDynamicObject::attach_coupler_adapter( int const Side, bool const Enforce ) {
auto &coupler { MoverParameters->Couplers[ Side ] }; auto &coupler { MoverParameters->Couplers[ Side ] };
// sanity check(s) // sanity check(s)
@@ -1572,7 +1572,8 @@ TDynamicObject::attach_coupler_adapter( int const Side ) {
// explicit coupler adapter definition overrides default parameters // explicit coupler adapter definition overrides default parameters
adapterdata = neighbour->m_coupleradapter; adapterdata = neighbour->m_coupleradapter;
} }
if( MoverParameters->Neighbours[ Side ].distance - adapterdata.position.x < 0.5 ) { if( ( MoverParameters->Neighbours[ Side ].distance - adapterdata.position.x < 0.5 )
&& ( false == Enforce ) ) {
// arbitrary amount of free room required to install the adapter // arbitrary amount of free room required to install the adapter
// NOTE: this also covers cases with established physical connection // NOTE: this also covers cases with established physical connection
return false; return false;
@@ -2483,13 +2484,51 @@ void TDynamicObject::Move(double fDistance)
} }
}; };
void TDynamicObject::AttachPrev(TDynamicObject *Object, int iType) void TDynamicObject::AttachNext(TDynamicObject *Object, int iType)
{ // Ra: doczepia Object na końcu składu (nazwa funkcji może być myląca) { // Ra: doczepia Object na końcu składu (nazwa funkcji może być myląca)
// Ra: używane tylko przy wczytywaniu scenerii // Ra: używane tylko przy wczytywaniu scenerii
MoverParameters->Attach( iDirection, Object->iDirection ^ 1, Object->MoverParameters, iType, true, false ); auto const vehicleend { iDirection };
auto const othervehicleend { Object->iDirection ^ 1 };
MoverParameters->Attach( vehicleend, othervehicleend, Object->MoverParameters, iType, true, false );
// update neighbour data for both affected vehicles // update neighbour data for both affected vehicles
update_neighbours(); update_neighbours();
Object->update_neighbours(); Object->update_neighbours();
// potentially attach automatic coupler adapter to allow the connection
// HACK: we're doing it after establishin actual connection, as the method needs valid neighbour data
auto &coupler { MoverParameters->Couplers[ vehicleend ] };
auto &othercoupler { Object->MoverParameters->Couplers[ ( othervehicleend != 2 ? othervehicleend : coupler.ConnectedNr ) ] };
if( coupler.type() != othercoupler.type() ) {
if( othercoupler.type() == TCouplerType::Automatic ) {
// try to attach adapter to the vehicle
attach_coupler_adapter(
vehicleend,
true );
}
else if( coupler.type() == TCouplerType::Automatic ) {
// try to attach adapter to the other vehicle
Object->attach_coupler_adapter(
( othervehicleend != 2 ? othervehicleend : coupler.ConnectedNr ),
true );
}
// update distance to neighbours on account of potentially attached adapter
update_neighbours();
Object->update_neighbours();
}
// potentially adjust vehicle position to avoid collision at the simulation start
if( MoverParameters->Neighbours[ vehicleend ].distance > -0.001 ) { return; }
Object->Move( MoverParameters->Neighbours[ vehicleend ].distance * Object->DirectionGet() );
// HACK: manually update vehicle position as it's used by neighbour distance update we do next
Object->MoverParameters->Loc = {
-Object->vPosition.x,
Object->vPosition.z,
Object->vPosition.y };
// update neighbour distance data after moving our vehicle
update_neighbours();
Object->update_neighbours();
} }
bool TDynamicObject::UpdateForce(double dt) bool TDynamicObject::UpdateForce(double dt)
@@ -3063,7 +3102,8 @@ bool TDynamicObject::Update(double dt, double dt1)
if (((MoverParameters->ShuntMode) && (Frj < 0.0015 * masa)) || if (((MoverParameters->ShuntMode) && (Frj < 0.0015 * masa)) ||
(MoverParameters->V * MoverParameters->DirAbsolute < -0.2)) (MoverParameters->V * MoverParameters->DirAbsolute < -0.2))
{ {
Fzad = std::max(MoverParameters->StopBrakeDecc * masa, Fzad); auto const sbd { ( ( MoverParameters->SpringBrake.IsActive && MoverParameters->ReleaseParkingBySpringBrake ) ? 0.0 : MoverParameters->StopBrakeDecc ) };
Fzad = std::max( Fzad, sbd * masa );
} }
if ((Fzad > 1) && (!MEDLogFile.is_open()) && (MoverParameters->Vel > 1)) if ((Fzad > 1) && (!MEDLogFile.is_open()) && (MoverParameters->Vel > 1))
{ {

View File

@@ -564,7 +564,7 @@ private:
int init_sections( TModel3d const *Model, std::string const &Nameprefix, bool const Overrideselfillum ); int init_sections( TModel3d const *Model, std::string const &Nameprefix, bool const Overrideselfillum );
bool init_destination( TModel3d *Model ); bool init_destination( TModel3d *Model );
void create_controller( std::string const Type, bool const Trainset ); void create_controller( std::string const Type, bool const Trainset );
void AttachPrev(TDynamicObject *Object, int iType = 1); void AttachNext(TDynamicObject *Object, int iType = 1);
bool UpdateForce(double dt); bool UpdateForce(double dt);
// initiates load change by specified amounts, with a platform on specified side // initiates load change by specified amounts, with a platform on specified side
void LoadExchange( int const Disembark, int const Embark, int const Platform ); void LoadExchange( int const Disembark, int const Embark, int const Platform );
@@ -660,7 +660,7 @@ private:
void couple( int const Side ); void couple( int const Side );
int uncouple( int const Side ); int uncouple( int const Side );
bool attach_coupler_adapter( int const Side ); bool attach_coupler_adapter( int const Side, bool const Enforce = false );
bool remove_coupler_adapter( int const Side ); bool remove_coupler_adapter( int const Side );
void RadioStop(); void RadioStop();
void Damage(char flag); void Damage(char flag);

View File

@@ -121,12 +121,12 @@ static int const dtrain_axle = 64;
static int const dtrain_out = 128; /*wykolejenie*/ static int const dtrain_out = 128; /*wykolejenie*/
/*wagi prawdopodobienstwa dla funkcji FuzzyLogic*/ /*wagi prawdopodobienstwa dla funkcji FuzzyLogic*/
#define p_elengproblem (1.000000E-02) #define p_elengproblem (1e-02)
#define p_elengdamage (1.000000E-01) #define p_elengdamage (1e-01)
#define p_coupldmg (2.000000E-02) #define p_coupldmg (2e-03)
#define p_derail (1.000000E-03) #define p_derail (1e-03)
#define p_accn (1.000000E-01) #define p_accn (1e-01)
#define p_slippdmg (1.000000E-03) #define p_slippdmg (1e-03)
/*typ sprzegu*/ /*typ sprzegu*/
static int const ctrain_virtual = 0; //gdy pojazdy na tym samym torze się widzą wzajemnie static int const ctrain_virtual = 0; //gdy pojazdy na tym samym torze się widzą wzajemnie
@@ -759,6 +759,8 @@ struct speed_control {
double FactorIpos = 0.0; double FactorIpos = 0.0;
double FactorIneg = 0.0; double FactorIneg = 0.0;
double BrakeInterventionVel = 30.0; double BrakeInterventionVel = 30.0;
double PowerUpSpeed = 1000;
double PowerDownSpeed = 1000;
}; };
class TMoverParameters class TMoverParameters
@@ -1127,6 +1129,7 @@ public:
int ScndInMain{ 0 }; /*zaleznosc bocznika od nastawnika*/ int ScndInMain{ 0 }; /*zaleznosc bocznika od nastawnika*/
bool MBrake = false; /*Czy jest hamulec reczny*/ bool MBrake = false; /*Czy jest hamulec reczny*/
double StopBrakeDecc = 0.0; double StopBrakeDecc = 0.0;
bool ReleaseParkingBySpringBrake { false };
TSecuritySystem SecuritySystem; TSecuritySystem SecuritySystem;
int EmergencyBrakeWarningSignal{ 0 }; // combined with basic WarningSignal when manual emergency brake is active int EmergencyBrakeWarningSignal{ 0 }; // combined with basic WarningSignal when manual emergency brake is active
TUniversalCtrlTable UniCtrlList; /*lista pozycji uniwersalnego nastawnika*/ TUniversalCtrlTable UniCtrlList; /*lista pozycji uniwersalnego nastawnika*/
@@ -1602,7 +1605,7 @@ public:
// obsługa sprzęgów // obsługa sprzęgów
static double CouplerDist( TMoverParameters const *Left, TMoverParameters const *Right ); static double CouplerDist( TMoverParameters const *Left, TMoverParameters const *Right );
static double Distance(const TLocation &Loc1, const TLocation &Loc2, const TDimension &Dim1, const TDimension &Dim2); static double Distance(const TLocation &Loc1, const TLocation &Loc2, const TDimension &Dim1, const TDimension &Dim2);
bool Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced = false, bool Audible = true); bool Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Enforce = false, bool Audible = true);
int DettachStatus(int ConnectNo); int DettachStatus(int ConnectNo);
bool Dettach(int ConnectNo); bool Dettach(int ConnectNo);
void damage_coupler( int const End ); void damage_coupler( int const End );
@@ -1645,8 +1648,8 @@ public:
bool RunCommand( std::string Command, double CValue1, double CValue2, int const Couplertype = ctrain_controll ); bool RunCommand( std::string Command, double CValue1, double CValue2, int const Couplertype = ctrain_controll );
bool RunInternalCommand(); bool RunInternalCommand();
void PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation); void PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation);
bool CabActivisation( bool const Force = false ); bool CabActivisation( bool const Enforce = false );
bool CabDeactivisation( bool const Force = false ); bool CabDeactivisation( bool const Enforce = false );
/*! funkcje zwiekszajace/zmniejszajace nastawniki*/ /*! funkcje zwiekszajace/zmniejszajace nastawniki*/
/*! glowny nastawnik:*/ /*! glowny nastawnik:*/

View File

@@ -16,6 +16,7 @@ http://mozilla.org/MPL/2.0/.
#include "Globals.h" #include "Globals.h"
#include "Logs.h" #include "Logs.h"
#include "parser.h" #include "parser.h"
#include "simulation.h"
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Ra: tu należy przenosić funcje z mover.pas, które nie są z niego wywoływane. // Ra: tu należy przenosić funcje z mover.pas, które nie są z niego wywoływane.
@@ -426,7 +427,7 @@ double TMoverParameters::CouplerDist(TMoverParameters const *Left, TMoverParamet
Left->Dim, Right->Dim); // odległość pomiędzy sprzęgami (kula!) Left->Dim, Right->Dim); // odległość pomiędzy sprzęgami (kula!)
}; };
bool TMoverParameters::Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Forced, bool Audible) bool TMoverParameters::Attach(int ConnectNo, int ConnectToNr, TMoverParameters *ConnectTo, int CouplingType, bool Enforce, bool Audible)
{ //łączenie do swojego sprzęgu (ConnectNo) pojazdu (ConnectTo) stroną (ConnectToNr) { //łączenie do swojego sprzęgu (ConnectNo) pojazdu (ConnectTo) stroną (ConnectToNr)
// Ra: zwykle wykonywane dwukrotnie, dla każdego pojazdu oddzielnie // Ra: zwykle wykonywane dwukrotnie, dla każdego pojazdu oddzielnie
// Ra: trzeba by odróżnić wymóg dociśnięcia od uszkodzenia sprzęgu przy podczepianiu AI do składu // Ra: trzeba by odróżnić wymóg dociśnięcia od uszkodzenia sprzęgu przy podczepianiu AI do składu
@@ -441,7 +442,7 @@ bool TMoverParameters::Attach(int ConnectNo, int ConnectToNr, TMoverParameters *
auto const distance { CouplerDist( this, ConnectTo ) - ( coupler.adapter_length + othercoupler.adapter_length ) }; auto const distance { CouplerDist( this, ConnectTo ) - ( coupler.adapter_length + othercoupler.adapter_length ) };
auto const couplercheck { auto const couplercheck {
( Forced ) ( Enforce )
|| ( ( distance <= dEpsilon ) || ( ( distance <= dEpsilon )
&& ( coupler.type() != TCouplerType::NoCoupler ) && ( coupler.type() != TCouplerType::NoCoupler )
&& ( coupler.type() == othercoupler.type() ) ) }; && ( coupler.type() == othercoupler.type() ) ) };
@@ -1044,12 +1045,14 @@ void TMoverParameters::CollisionDetect(int const End, double const dt)
} }
} }
if( ( coupler.Dist < 0 ) if( ( -coupler.Dist >= coupler.DmaxB )
&& ( FuzzyLogic( std::abs( CCF ), 5.0 * ( coupler.FmaxC + 1.0 ), p_coupldmg ) ) ) { && ( FuzzyLogic( std::abs( CCF ), 5.0 * ( coupler.FmaxC + 1.0 ), p_coupldmg ) ) ) {
// small chance to smash the coupler if it's hit with excessive force // small chance to smash the coupler if it's hit with excessive force
damage_coupler( End ); damage_coupler( End );
} }
if( ( coupler.CouplingFlag == coupling::faux
|| ( true == TestFlag( othervehicle->DamageFlag, dtrain_out ) ) ) ) { // HACK: limit excessive speed derailment checks to vehicles which aren't part of the same consist
auto const safevelocitylimit { 15.0 }; auto const safevelocitylimit { 15.0 };
auto const velocitydifference { auto const velocitydifference {
glm::length( glm::length(
@@ -1071,6 +1074,7 @@ void TMoverParameters::CollisionDetect(int const End, double const dt)
othervehicle->derail( 5 ); othervehicle->derail( 5 );
} }
} }
}
// adjust velocity and acceleration of affected vehicles // adjust velocity and acceleration of affected vehicles
if( false == TestFlag( DamageFlag, dtrain_out ) ) { if( false == TestFlag( DamageFlag, dtrain_out ) ) {
@@ -2291,6 +2295,9 @@ bool TMoverParameters::DecMainCtrl(int CtrlSpeed)
// nie ma sterowania // nie ma sterowania
OK = false; OK = false;
} }
else if( CtrlSpeed == 0 ) {
return false;
}
else else
{ {
// TBD, TODO: replace with mainctrlpowerpos() check? // TBD, TODO: replace with mainctrlpowerpos() check?
@@ -2324,7 +2331,7 @@ bool TMoverParameters::DecMainCtrl(int CtrlSpeed)
} }
} }
else if (CtrlSpeed > 1) else if (CtrlSpeed > 1)
OK = (DecMainCtrl(1) && DecMainCtrl(2)); // CtrlSpeed-1); OK = (DecMainCtrl(1) && DecMainCtrl(CtrlSpeed - 1)); // CtrlSpeed-1);
break; break;
} }
@@ -2569,11 +2576,11 @@ bool TMoverParameters::DecScndCtrl(int CtrlSpeed)
// Q: 20160710 // Q: 20160710
// załączenie rozrządu // załączenie rozrządu
// ************************************************************************************************* // *************************************************************************************************
bool TMoverParameters::CabActivisation( bool const Force ) bool TMoverParameters::CabActivisation( bool const Enforce )
{ {
bool OK = false; bool OK = false;
OK = Force || (CabActive == 0); // numer kabiny, z której jest sterowanie OK = Enforce || (CabActive == 0); // numer kabiny, z której jest sterowanie
if (OK) if (OK)
{ {
CabActive = CabOccupied; // sterowanie jest z kabiny z obsadą CabActive = CabOccupied; // sterowanie jest z kabiny z obsadą
@@ -2588,11 +2595,11 @@ bool TMoverParameters::CabActivisation( bool const Force )
// Q: 20160710 // Q: 20160710
// wyłączenie rozrządu // wyłączenie rozrządu
// ************************************************************************************************* // *************************************************************************************************
bool TMoverParameters::CabDeactivisation( bool const Force ) bool TMoverParameters::CabDeactivisation( bool const Enforce )
{ {
bool OK = false; bool OK = false;
OK = Force || (CabActive == CabOccupied); // o ile obsada jest w kabinie ze sterowaniem OK = Enforce || (CabActive == CabOccupied); // o ile obsada jest w kabinie ze sterowaniem
if (OK) if (OK)
{ {
CabActive = 0; CabActive = 0;
@@ -7065,6 +7072,7 @@ void TMoverParameters::CheckSpeedCtrl(double dt)
} }
if ((EIMCtrlType >= 3)&&(UniCtrlList[MainCtrlPos].SpeedUp <= 0)) { if ((EIMCtrlType >= 3)&&(UniCtrlList[MainCtrlPos].SpeedUp <= 0)) {
accfactor = 0.0; accfactor = 0.0;
eimicSpeedCtrl = 0;
} }
} }
if (SpeedCtrlUnit.IsActive) {//speed control if (SpeedCtrlUnit.IsActive) {//speed control
@@ -7079,7 +7087,7 @@ void TMoverParameters::CheckSpeedCtrl(double dt)
bool retarder_not_work = (EngineType != TEngineType::DieselEngine) || (Vel < SpeedCtrlUnit.BrakeInterventionVel); bool retarder_not_work = (EngineType != TEngineType::DieselEngine) || (Vel < SpeedCtrlUnit.BrakeInterventionVel);
if (eSCP < -1.0) if (eSCP < -1.0)
{ {
SpeedCtrlUnit.BrakeInterventionBraking = (eSCP < -1.1) && retarder_not_work; SpeedCtrlUnit.BrakeInterventionBraking = (eSCP < -1.1) && retarder_not_work && (eimicSpeedCtrl < -0.99 * SpeedCtrlUnit.DesiredPower);
eSCP = -1.0; eSCP = -1.0;
} }
SpeedCtrlUnit.BrakeInterventionUnbraking = (eSCP > 0.0) || (Vel == 0.0); SpeedCtrlUnit.BrakeInterventionUnbraking = (eSCP > 0.0) || (Vel == 0.0);
@@ -7092,7 +7100,11 @@ void TMoverParameters::CheckSpeedCtrl(double dt)
else { else {
eimicSpeedCtrlIntegral = 0; eimicSpeedCtrlIntegral = 0;
} }
eimicSpeedCtrl = clamp(eimicSpeedCtrlIntegral + eSCP, -SpeedCtrlUnit.DesiredPower, accfactor); auto const DesiredeimicSpeedCtrl { clamp( eimicSpeedCtrlIntegral + eSCP, -SpeedCtrlUnit.DesiredPower, accfactor ) };
eimicSpeedCtrl = clamp(
DesiredeimicSpeedCtrl,
eimicSpeedCtrl - SpeedCtrlUnit.PowerDownSpeed * dt,
eimicSpeedCtrl + SpeedCtrlUnit.PowerUpSpeed * dt );
if (Vel < SpeedCtrlUnit.FullPowerVelocity) { if (Vel < SpeedCtrlUnit.FullPowerVelocity) {
eimicSpeedCtrl = std::min(eimicSpeedCtrl, SpeedCtrlUnit.InitialPower); eimicSpeedCtrl = std::min(eimicSpeedCtrl, SpeedCtrlUnit.InitialPower);
} }
@@ -10090,6 +10102,7 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) {
0; 0;
extract_value( StopBrakeDecc, "SBD", line, "" ); extract_value( StopBrakeDecc, "SBD", line, "" );
extract_value( ReleaseParkingBySpringBrake, "ReleaseParkingBySpringBrake", line, "" );
std::map<std::string, start_t> starts { std::map<std::string, start_t> starts {
{ "Disabled", start_t::disabled }, { "Disabled", start_t::disabled },
@@ -10368,8 +10381,8 @@ void TMoverParameters::LoadFIZ_SpeedControl(std::string const &Line) {
extract_value(SpeedCtrlUnit.FactorIpos, "kIpos", Line, ""); extract_value(SpeedCtrlUnit.FactorIpos, "kIpos", Line, "");
extract_value(SpeedCtrlUnit.FactorIneg, "kIneg", Line, ""); extract_value(SpeedCtrlUnit.FactorIneg, "kIneg", Line, "");
extract_value(SpeedCtrlUnit.BrakeInterventionVel, "BrakeIntMaxVel", Line, ""); extract_value(SpeedCtrlUnit.BrakeInterventionVel, "BrakeIntMaxVel", Line, "");
extract_value(SpeedCtrlUnit.PowerUpSpeed, "PowerUpSpeed", Line, "" );
extract_value(SpeedCtrlUnit.PowerDownSpeed, "PowerDownSpeed", Line, "" );
} }
void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) {

View File

@@ -199,7 +199,7 @@ auto python_taskqueue::init() -> bool {
// release the lock, save the state for future use // release the lock, save the state for future use
m_mainthread = PyEval_SaveThread(); m_mainthread = PyEval_SaveThread();
WriteLog( "Python Interpreter setup complete" ); WriteLog( "Python Interpreter: setup complete" );
// init workers // init workers
for( auto &worker : m_workers ) { for( auto &worker : m_workers ) {

View File

@@ -689,7 +689,7 @@ state_serializer::deserialize_endtrainset( cParser &Input, scene::scratch_data &
} }
if( vehicleindex > 0 ) { if( vehicleindex > 0 ) {
// from second vehicle on couple it with the previous one // from second vehicle on couple it with the previous one
Scratchpad.trainset.vehicles[ vehicleindex - 1 ]->AttachPrev( Scratchpad.trainset.vehicles[ vehicleindex - 1 ]->AttachNext(
vehicle, vehicle,
Scratchpad.trainset.couplings[ vehicleindex - 1 ] ); Scratchpad.trainset.couplings[ vehicleindex - 1 ] );
} }

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#define VERSION_MAJOR 20 #define VERSION_MAJOR 20
#define VERSION_MINOR 506 #define VERSION_MINOR 509
#define VERSION_REVISION 0 #define VERSION_REVISION 0