diff --git a/Driver.cpp b/Driver.cpp index de34adbd..66e6b0b4 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -194,9 +194,27 @@ bool TSpeedPos::Update(vector3 *p, vector3 *dir, double &len) // v.SafeNormalize(); //normalizacja w celu określenia znaku (nie potrzebna?) if (len == 0.0) { // jeżeli liczymy względem pojazdu - double iska = dir ? dir->x * v.x + dir->z * v.z : - fDist; // iloczyn skalarny to rzut na chwilową prostą ruchu - if (iska < 0.0) // iloczyn skalarny jest ujemny, gdy punkt jest z tyłu + double angle; + if( dir ) { + glm::vec3 target{ glm::normalize( glm::make_vec3( &v.x ) ) }; + glm::vec3 direction{ glm::normalize( glm::make_vec3( &dir->x ) ) }; + angle = glm::dot( target, direction ); +// assert( angle < M_PI_2 ); +/* + angle = std::atan2( + glm::length( + glm::cross( + target, + direction ) ), + glm::dot( + target, + direction ) ); +*/ + } + else { + angle = fDist; + } + if (angle < 0.0) // iloczyn skalarny jest ujemny, gdy punkt jest z tyłu { // jeśli coś jest z tyłu, to dokładna odległość nie ma już większego znaczenia fDist = -fDist; // potrzebne do badania wyjechania składem poza ograniczenie if (iFlags & spElapsed) // 32 ustawione, gdy obiekt już został minięty @@ -204,20 +222,27 @@ bool TSpeedPos::Update(vector3 *p, vector3 *dir, double &len) } else { - iFlags ^= spElapsed; // 32-minięty - będziemy liczyć odległość względem przeciwnego końca + // minięty - będziemy liczyć odległość względem przeciwnego końca // toru (nadal może być z przodu i ograniczać) - if ((iFlags & 0x43) == 3) // tylko jeśli (istotny) tor, bo eventy są punktowe - if (trTrack) // może być NULL, jeśli koniec toru (????) + iFlags ^= spElapsed; + if ((iFlags & (spEnd | spTrack | spEnabled)) == (spTrack | spEnabled)) { + // tylko jeśli (istotny) tor, bo eventy są punktowe + if (trTrack) { + // może być NULL, jeśli koniec toru (????) vPos = (iFlags & spReverse) ? trTrack->CurrentSegment()->FastGetPoint_0() : trTrack->CurrentSegment()->FastGetPoint_1(); // drugi koniec istotny + } + } } } +/* else if (fDist < 50.0) // przy dużym kącie łuku iloczyn skalarny bardziej zaniży odległość // niż cięciwa fDist = iska; // ale przy małych odległościach rzut na chwilową prostą ruchu da // dokładniejsze wartości +*/ } if (fDist > 0.0) // nie może być 0.0, a przypadkiem mogło by się trafić i było by źle if ((iFlags & spElapsed) == 0) // 32 ustawione, gdy obiekt już został minięty @@ -297,7 +322,7 @@ std::string TSpeedPos::TableText() if (iFlags & spEnabled) { // o ile pozycja istotna return "Flags:" + to_hex_str(iFlags, 6) + ", Dist:" + to_string(fDist, 1, 6) + - ", Vel:" + (fVelNext == -1.0 ? " * " : to_string(static_cast(fVelNext), 0, 3)) + ", Name:" + GetName(); + ", Vel:" + (fVelNext == -1.0 ? " - " : to_string(static_cast(fVelNext), 0, 3)) + ", Name:" + GetName(); //if (iFlags & spTrack) // jeśli tor // return "Flags=#" + IntToHex(iFlags, 8) + ", Dist=" + FloatToStrF(fDist, ffFixed, 7, 1) + // ", Vel=" + AnsiString(fVelNext) + ", Track=" + trTrack->NameGet(); @@ -373,19 +398,22 @@ void TSpeedPos::Set(TTrack *track, double dist, int flag) } }; -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void TController::TableClear() { // wyczyszczenie tablicy +#ifdef EU07_USE_OLD_SPEEDTABLE iFirst = iLast = 0; - iTableDirection = 0; // nieznany for (int i = 0; i < iSpeedTableSize; ++i) // czyszczenie tabeli prędkości sSpeedTable[i].Clear(); - tLast = NULL; - fLastVel = -1; - eSignSkip = NULL; // nic nie pomijamy +#else + sSpeedTable.clear(); +#endif + iLast = -1; + iTableDirection = 0; // nieznany + tLast = nullptr; + fLastVel = -1.0; + eSignSkip = nullptr; // nic nie pomijamy }; TEvent * TController::CheckTrackEvent(double fDirection, TTrack *Track) @@ -401,17 +429,23 @@ TEvent * TController::CheckTrackEvent(double fDirection, TTrack *Track) bool TController::TableAddNew() { // zwiększenie użytej tabelki o jeden rekord - iLast = (iLast + 1) % iSpeedTableSize; +#ifdef EU07_USE_OLD_SPEEDTABLE + iLast = ( iLast + 1 ) % iSpeedTableSize; // TODO: jeszcze sprawdzić, czy się na iFirst nie nałoży // TODO: wstawić tu wywołanie odtykacza - teraz jest to w TableTraceRoute() // TODO: jeśli ostatnia pozycja zajęta, ustawiać dodatkowe flagi - teraz jest to w // TableTraceRoute() // TODO: przydało by się też posortować tabelkę wg odległości (ale nie w tym miejscu) +#else + sSpeedTable.emplace_back(); // add a new slot + iLast = sSpeedTable.size() - 1; +#endif return true; // false gdy się nałoży }; -bool TController::TableNotFound(TEvent *e) +bool TController::TableNotFound(TEvent const *Event) const { // sprawdzenie, czy nie został już dodany do tabelki (np. podwójne W4 robi problemy) +#ifdef EU07_USE_OLD_SPEEDTABLE int j = (iLast + 1) % iSpeedTableSize; // j, aby sprawdzić też ostatnią pozycję for (int i = iFirst; i != j; i = (i + 1) % iSpeedTableSize) if ((sSpeedTable[i].iFlags & (spEnabled | spEvent)) == (spEnabled | @@ -423,42 +457,59 @@ bool TController::TableNotFound(TEvent *e) return false; // już jest, drugi raz dodawać nie ma po co } return true; // nie ma, czyli można dodać +#else + auto lookup = std::find_if( + sSpeedTable.begin(), + sSpeedTable.end(), + [Event]( TSpeedPos const &speedpoint ){ + return ( ( true == TestFlag( speedpoint.iFlags, spEnabled | spEvent ) ) + && ( speedpoint.evEvent == Event ) ); } ); + + if( ( Global::iWriteLogEnabled & 8 ) + && ( lookup != sSpeedTable.end() ) ) { + WriteLog( "Speed table for " + OwnerName() + " already contains event " + lookup->evEvent->asName ); + } + + return lookup == sSpeedTable.end(); +#endif }; void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) { // skanowanie trajektorii na odległość (fDistance) od (pVehicle) w kierunku przodu składu i // uzupełnianie tabelki // WriteLog("Starting TableTraceRoute"); - if (!iDirection) // kierunek pojazdu z napędem - { // jeśli kierunek jazdy nie jest okreslony - iTableDirection = 0; // czekamy na ustawienie kierunku - } - TTrack *pTrack; // zaczynamy od ostatniego analizowanego toru - // double fDistChVel=-1; //odległość do toru ze zmianą prędkości - double fTrackLength; // długość aktualnego toru (krótsza dla pierwszego) - double fCurrentDistance; // aktualna przeskanowana długość - TEvent *pEvent; - double fLastDir; // kierunek na ostatnim torze - if (iTableDirection != iDirection) - { // jeśli zmiana kierunku, zaczynamy od toru ze wskazanym pojazdem + + TTrack *pTrack{ nullptr }; // zaczynamy od ostatniego analizowanego toru + double fTrackLength{ 0.0 }; // długość aktualnego toru (krótsza dla pierwszego) + double fCurrentDistance{ 0.0 }; // aktualna przeskanowana długość + TEvent *pEvent{ nullptr }; + double fLastDir{ 0.0 }; + + if (iTableDirection != iDirection) { + // jeśli zmiana kierunku, zaczynamy od toru ze wskazanym pojazdem + iTableDirection = iDirection; // ustalenie w jakim kierunku jest wypełniana tabelka względem pojazdu pTrack = pVehicle->RaTrackGet(); // odcinek, na którym stoi - fLastDir = pVehicle->DirectionGet() * - pVehicle->RaDirectionGet(); // ustalenie kierunku skanowania na torze - fCurrentDistance = 0; // na razie nic nie przeskanowano fTrackLength = pVehicle->RaTranslationGet(); // pozycja na tym torze (odległość od Point1) - if (fLastDir > 0) // jeśli w kierunku Point2 toru - fTrackLength = - pTrack->Length() - fTrackLength; // przeskanowana zostanie odległość do Point2 + fLastDir = pVehicle->DirectionGet() * pVehicle->RaDirectionGet(); // ustalenie kierunku skanowania na torze + if( fLastDir > 0.0 ) { + // jeśli w kierunku Point2 toru + fTrackLength = pTrack->Length() - fTrackLength; // przeskanowana zostanie odległość do Point2 + } fLastVel = pTrack->VelocityGet(); // aktualna prędkość - iTableDirection = - iDirection; // ustalenie w jakim kierunku jest wypełniana tabelka względem pojazdu +#ifdef EU07_USE_OLD_SPEEDTABLE iFirst = iLast = 0; - tLast = NULL; //żaden nie sprawdzony +#endif + sSpeedTable.clear(); + iLast = -1; + tLast = nullptr; //żaden nie sprawdzony + SemNextIndex = -1; + SemNextStopIndex = -1; } - else - { // kontynuacja skanowania od ostatnio sprawdzonego toru (w ostatniej pozycji zawsze jest tor) + else { + // kontynuacja skanowania od ostatnio sprawdzonego toru (w ostatniej pozycji zawsze jest tor) // WriteLog("TableTraceRoute: check last track"); - if (sSpeedTable[iLast].iFlags & spEndOfTable) // zatkanie +#ifdef EU07_USE_OLD_SPEEDTABLE + if( sSpeedTable[ iLast ].iFlags & spEndOfTable ) // zatkanie { // jeśli zapełniła się tabelka if ((iLast + 1) % iSpeedTableSize == iFirst) // jeśli nadal jest zapełniona { @@ -474,22 +525,38 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) } sSpeedTable[iLast].iFlags &= 0xBE; // kontynuować próby doskanowania } - // znaleziono semafor lub tarczę lub tor z prędkością zero - // trzeba sprawdzić czy to nadał semafor - // WriteLog("TableTraceRoute: "+OwnerName()+" check semaphor... "); - // if (sSemNext) - // WriteLog(sSemNext->TableText()); - if (sSemNextStop && - sSemNextStop->fVelNext == - 0.0) // jeśli jest następny semafor to sprawdzamy czy to on nadał zero - { - // WriteLog("TableTraceRoute: "+sSemNext->TableText()); - if ((OrderCurrentGet() & Obey_train) && (sSemNextStop->iFlags & spSemaphor)) - return; - else if ((OrderCurrentGet() < 0x40) && - (sSemNextStop->iFlags & (spSemaphor | spShuntSemaphor | spOutsideStation))) +#endif + // znaleziono semafor lub tarczę lub tor z prędkością zero, trzeba sprawdzić czy to nadał semafor +#ifdef EU07_USE_OLD_SPEEDTABLE + if( ( sSemNextStop != nullptr ) + && ( sSemNextStop->fVelNext < 1.0 ) ) { +#else + if( ( SemNextStopIndex != -1 ) + && ( sSpeedTable[SemNextStopIndex].fVelNext < 1.0 ) ) { +#endif + // jeśli jest następny semafor to sprawdzamy czy to on nadał zero +#ifdef EU07_USE_OLD_SPEEDTABLE + if( ( OrderCurrentGet() & Obey_train ) + && ( sSemNextStop->iFlags & spSemaphor ) ) { +#else + if( ( OrderCurrentGet() & Obey_train ) + && ( sSpeedTable[SemNextStopIndex].iFlags & spSemaphor ) ) { +#endif return; + } + else { +#ifdef EU07_USE_OLD_SPEEDTABLE + if( ( OrderCurrentGet() < 0x40 ) + && ( sSemNextStop->iFlags & ( spSemaphor | spShuntSemaphor | spOutsideStation ) ) ) { +#else + if( ( OrderCurrentGet() < 0x40 ) + && ( sSpeedTable[SemNextStopIndex].iFlags & ( spSemaphor | spShuntSemaphor | spOutsideStation ) ) ) { +#endif + return; + } + } } +#ifdef EU07_USE_OLD_SPEEDTABLE pTrack = sSpeedTable[iLast].trTrack; // ostatnio sprawdzony tor if (!pTrack) return; // koniec toru, to nie ma co sprawdzać (nie ma prawa tak być) @@ -498,217 +565,305 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) fTrackLength = (sSpeedTable[iLast].iFlags & (spElapsed | spEnd)) ? 0.0 : pTrack->Length(); // nie dolicza� d�ugo�ci gdy: // 32-minięty początek, // 64-jazda do końca toru +#else + auto const &lastspeedpoint = sSpeedTable[ iLast ]; + pTrack = lastspeedpoint.trTrack; + assert( pTrack != nullptr ); + // flaga ustawiona, gdy Point2 toru jest blizej + fLastDir = ( + TestFlag( lastspeedpoint.iFlags, spReverse ) ? + -1.0 : + 1.0 ); + // aktualna odleglosc do jego Point1 + fCurrentDistance = lastspeedpoint.fDist; + // nie doliczac dlugosci gdy: miniety początek lub jazda do konca toru + fTrackLength = ( + ( lastspeedpoint.iFlags & ( spElapsed | spEnd ) != 0 ) ? + 0.0 : + pTrack->Length() ); +#endif } - if (fCurrentDistance < fDistance) - { // jeśli w ogóle jest po co analizować - // WriteLog("TableTraceRoute: checking next tracks"); - --iLast; // jak coś się znajdzie, zostanie wpisane w tę pozycję, którą właśnie odczytano - while (fCurrentDistance < fDistance) - { - if (pTrack != tLast) // ostatni zapisany w tabelce nie był jeszcze sprawdzony - { // jeśli tor nie był jeszcze sprawdzany - // if (pTrack) - // WriteLog("TableTraceRoute: " + OwnerName() + " checking track " + - // pTrack->NameGet()); - if ((pEvent = CheckTrackEvent(fLastDir, pTrack)) != - NULL) // jeśli jest semafor na tym torze - { // trzeba sprawdzić tabelkę, bo dodawanie drugi raz tego samego przystanku nie - // jest korzystne - if (TableNotFound(pEvent)) // jeśli nie ma - if (TableAddNew()) + + if( fCurrentDistance >= fDistance ) { + // all done + return; + } + +#ifdef EU07_USE_OLD_SPEEDTABLE + // jak coś się znajdzie, zostanie wpisane w tę pozycję, którą właśnie odczytano + --iLast; +#endif + + while (fCurrentDistance < fDistance) + { + if (pTrack != tLast) // ostatni zapisany w tabelce nie był jeszcze sprawdzony + { // jeśli tor nie był jeszcze sprawdzany + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "Speed table for " + OwnerName() + " tracing through track " + pTrack->NameGet() ); + } + + if( ( pEvent = CheckTrackEvent( fLastDir, pTrack ) ) != nullptr ) // jeśli jest semafor na tym torze + { // trzeba sprawdzić tabelkę, bo dodawanie drugi raz tego samego przystanku nie jest korzystne + if (TableNotFound(pEvent)) // jeśli nie ma + if( TableAddNew() ) + { + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "Speed table for " + OwnerName() + " found new event, " + pEvent->asName ); + } +#ifdef EU07_USE_OLD_SPEEDTABLE + if( sSpeedTable[ iLast ].Set( pEvent, fCurrentDistance, OrderCurrentGet() ) ) // dodanie odczytu sygnału { + fDistance = fCurrentDistance; // jeśli sygnał stop, to nie ma + // potrzeby dalej skanować + sSemNextStop = &sSpeedTable[iLast]; + if (!sSemNext) + sSemNext = &sSpeedTable[iLast]; if (Global::iWriteLogEnabled & 8) - WriteLog("TableTraceRoute: new event found " + pEvent->asName + " by " + OwnerName()); - if (sSpeedTable[iLast].Set( pEvent, fCurrentDistance, OrderCurrentGet())) // dodanie odczytu sygnału + WriteLog("Signal stop. Next Semaphor ", false); + if (sSemNextStop) { - fDistance = fCurrentDistance; // jeśli sygnał stop, to nie ma - // potrzeby dalej skanować - sSemNextStop = &sSpeedTable[iLast]; - if (!sSemNext) - sSemNext = &sSpeedTable[iLast]; if (Global::iWriteLogEnabled & 8) - WriteLog("Signal stop. Next Semaphor ", false); - if (sSemNextStop) - { - if (Global::iWriteLogEnabled & 8) - WriteLog(sSemNextStop->GetName()); - } - else - { - if (Global::iWriteLogEnabled & 8) - WriteLog("none"); - } + WriteLog(sSemNextStop->GetName()); } else { - if (sSpeedTable[iLast].IsProperSemaphor(OrderCurrentGet()) && - sSemNext == NULL) - sSemNext = - &sSpeedTable[iLast]; // sprawdzamy czy pierwszy na drodze if (Global::iWriteLogEnabled & 8) - WriteLog("Signal forward. Next Semaphor ", false); - if (sSemNext) - { - if (Global::iWriteLogEnabled & 8) - WriteLog(sSemNext->GetName()); - } - else - { - if (Global::iWriteLogEnabled & 8) - WriteLog("none"); - } + WriteLog("none"); } } - } // event dodajemy najpierw, żeby móc sprawdzić, czy tor został dodany po - // odczytaniu prędkości następnego - if ((pTrack->VelocityGet() == 0.0) // zatrzymanie - || (pTrack->iAction) // jeśli tor ma własności istotne dla skanowania - || (pTrack->VelocityGet() != fLastVel)) // następuje zmiana prędkości - { // odcinek dodajemy do tabelki, gdy jest istotny dla ruchu - if (TableAddNew()) - { // teraz dodatkowo zapamiętanie wybranego segmentu dla skrzyżowania - sSpeedTable[iLast].Set( - pTrack, fCurrentDistance, - fLastDir < 0 ? - 5 : - 1); // dodanie odcinka do tabelki z flagą kierunku wejścia - if (pTrack->eType == tt_Cross) // na skrzyżowaniach trzeba wybrać segment, - // po którym pojedzie pojazd - { // dopiero tutaj jest ustalany kierunek segmentu na skrzyżowaniu - sSpeedTable[iLast].iFlags |= - (pTrack->CrossSegment((fLastDir < 0) ? tLast->iPrevDirection : - tLast->iNextDirection, - iRouteWanted) & - 15) - << 28; // ostatnie 4 bity pola flag - sSpeedTable[iLast].iFlags &= - ~spReverse; // usunięcie flagi kierunku, bo może być błędna - if (sSpeedTable[iLast].iFlags < 0) - sSpeedTable[iLast].iFlags |= - spReverse; // ustawienie flagi kierunku na podstawie wybranego segmentu - if (int(fLastDir) * sSpeedTable[iLast].iFlags < 0) - fLastDir = -fLastDir; - if (AIControllFlag) // dla AI na razie losujemy kierunek na kolejnym - // skrzyżowaniu - iRouteWanted = 1 + Random(3); + else { + if( sSpeedTable[ iLast ].IsProperSemaphor( OrderCurrentGet() ) && + sSemNext == NULL ) + sSemNext = + &sSpeedTable[ iLast ]; // sprawdzamy czy pierwszy na drodze + if( Global::iWriteLogEnabled & 8 ) + WriteLog( "Signal forward. Next Semaphor ", false ); + if( sSemNext ) { + if( Global::iWriteLogEnabled & 8 ) + WriteLog( sSemNext->GetName() ); + } + else { + if( Global::iWriteLogEnabled & 8 ) + WriteLog( "none" ); + } + } +#else + auto &newspeedpoint = sSpeedTable[ iLast ]; + if( newspeedpoint.Set( pEvent, fCurrentDistance, OrderCurrentGet() ) ) { + + fDistance = fCurrentDistance; // jeśli sygnał stop, to nie ma potrzeby dalej skanować +#ifdef EU07_USE_OLD_SPEEDTABLE + sSemNextStop = &newspeedpoint; + if( sSemNext == nullptr ) { + sSemNext = &newspeedpoint; + } + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "(stop signal from " + + ( sSemNextStop ? sSemNextStop->GetName() : "unknown semaphor" ) + + ")" ); + } +#else + SemNextStopIndex = iLast; + if( SemNextIndex == -1 ) { + SemNextIndex = iLast; + } + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "(stop signal from " + + ( SemNextStopIndex != -1 ? sSpeedTable[SemNextStopIndex].GetName() : "unknown semaphor" ) + + ")" ); + } +#endif + } + else { +#ifdef EU07_USE_OLD_SPEEDTABLE + if( ( true == newspeedpoint.IsProperSemaphor( OrderCurrentGet() ) ) + && ( sSemNext == nullptr ) ) { + sSemNext = &newspeedpoint; // sprawdzamy czy pierwszy na drodze + } + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "(forward signal for " + + ( sSemNext ? sSemNext->GetName() : "unknown semaphor" ) + + ")" ); + } +#else + if( ( true == newspeedpoint.IsProperSemaphor( OrderCurrentGet() ) ) + && ( SemNextIndex == -1 ) ) { + SemNextIndex = iLast; // sprawdzamy czy pierwszy na drodze + } + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "(forward signal for " + + ( SemNextIndex != -1 ? sSpeedTable[SemNextIndex].GetName() : "unknown semaphor" ) + + ")" ); + } +#endif + } +#endif + } + } // event dodajemy najpierw, żeby móc sprawdzić, czy tor został dodany po odczytaniu prędkości następnego + + if ((pTrack->VelocityGet() == 0.0) // zatrzymanie + || (pTrack->iAction) // jeśli tor ma własności istotne dla skanowania + || (pTrack->VelocityGet() != fLastVel)) // następuje zmiana prędkości + { // odcinek dodajemy do tabelki, gdy jest istotny dla ruchu + if (TableAddNew()) + { // teraz dodatkowo zapamiętanie wybranego segmentu dla skrzyżowania + sSpeedTable[ iLast ].Set( + pTrack, fCurrentDistance, + ( fLastDir < 0 ? + spEnabled | spReverse : + spEnabled ) ); // dodanie odcinka do tabelki z flagą kierunku wejścia + if (pTrack->eType == tt_Cross) { + // na skrzyżowaniach trzeba wybrać segment, po którym pojedzie pojazd + // dopiero tutaj jest ustalany kierunek segmentu na skrzyżowaniu + sSpeedTable[iLast].iFlags |= + ( ( pTrack->CrossSegment( + ( fLastDir < 0 ? + tLast->iPrevDirection : + tLast->iNextDirection ), + iRouteWanted ) & 0xf ) << 28 ); // ostatnie 4 bity pola flag + sSpeedTable[iLast].iFlags &= ~spReverse; // usunięcie flagi kierunku, bo może być błędna + if( sSpeedTable[ iLast ].iFlags < 0 ) { + sSpeedTable[ iLast ].iFlags |= spReverse; // ustawienie flagi kierunku na podstawie wybranego segmentu + } + if( int( fLastDir ) * sSpeedTable[ iLast ].iFlags < 0 ) { + fLastDir = -fLastDir; + } + if( AIControllFlag ) { + // dla AI na razie losujemy kierunek na kolejnym skrzyżowaniu + iRouteWanted = 1 + Random( 3 ); } } } - else if ((pTrack->fRadius != 0.0) // odległość na łuku lepiej aproksymować cięciwami - || (tLast ? tLast->fRadius != 0.0 : false)) // koniec łuku też jest istotny - { // albo dla liczenia odległości przy pomocy cięciw - te usuwać po przejechaniu - if (TableAddNew()) - sSpeedTable[iLast].Set(pTrack, fCurrentDistance, - fLastDir < 0 ? 0x85 : - 0x81); // dodanie odcinka do tabelki - // 0x85 = spEnabled, spReverse, SpCurve + } + else if ( ( pTrack->fRadius != 0.0 ) // odległość na łuku lepiej aproksymować cięciwami + || ( ( tLast != nullptr ) + && ( tLast->fRadius != 0.0 ) )) // koniec łuku też jest istotny + { // albo dla liczenia odległości przy pomocy cięciw - te usuwać po przejechaniu + if (TableAddNew()) + sSpeedTable[iLast].Set( + pTrack, fCurrentDistance, + ( fLastDir < 0 ? + spEnabled | spCurve | spReverse : + spEnabled | spCurve ) ); // dodanie odcinka do tabelki + } + } + + fCurrentDistance += fTrackLength; // doliczenie kolejnego odcinka do przeskanowanej długości + tLast = pTrack; // odhaczenie, że sprawdzony + fLastVel = pTrack->VelocityGet(); // prędkość na poprzednio sprawdzonym odcinku + pTrack = pTrack->Neightbour( + ( pTrack->eType == tt_Cross ? + (sSpeedTable[iLast].iFlags >> 28) : + static_cast(fLastDir) ), + fLastDir); // może być NULL + + if (pTrack != nullptr ) + { // jeśli kolejny istnieje + if( tLast != nullptr ) { + if( ( pTrack->VelocityGet() < 0 ? + tLast->VelocityGet() > 0 : + pTrack->VelocityGet() > tLast->VelocityGet() ) ) { + // jeśli kolejny ma większą prędkość niż poprzedni, to zapamiętać poprzedni (do czasu wyjechania) + if( ( ( ( iLast != -1 ) + && ( TestFlag( sSpeedTable[ iLast ].iFlags, spEnabled | spTrack ) ) ) ? + ( sSpeedTable[ iLast ].trTrack != tLast ) : + true ) ) { + // jeśli nie był dodany do tabelki + if( TableAddNew() ) { + // zapisanie toru z ograniczeniem prędkości + sSpeedTable[ iLast ].Set( + tLast, fCurrentDistance, + ( fLastDir > 0 ? + pTrack->iPrevDirection : + pTrack->iNextDirection ) ? + spEnabled : + spEnabled | spReverse ); + } + } } } - fCurrentDistance += - fTrackLength; // doliczenie kolejnego odcinka do przeskanowanej długości - tLast = pTrack; // odhaczenie, że sprawdzony - // Track->ScannedFlag=true; //do pokazywania przeskanowanych torów - fLastVel = pTrack->VelocityGet(); // prędkość na poprzednio sprawdzonym odcinku - pTrack = pTrack->Neightbour( - (pTrack->eType == tt_Cross) ? (sSpeedTable[iLast].iFlags >> 28) : int(fLastDir), - fLastDir); // może być NULL - /* - if (fLastDir>0) - {//jeśli szukanie od Point1 w kierunku Point2 - pTrack=pTrack->CurrentNext(); //może być NULL - if (pTrack) //jeśli dalej brakuje toru, to zostajemy na tym samym, z tą samą - orientacją - if (tLast->iNextDirection) - fLastDir=-fLastDir; //można by zamiętać i zmienić tylko jeśli jest pTrack - } - else //if (fDirection<0) - {//jeśli szukanie od Point2 w kierunku Point1 - pTrack=pTrack->CurrentPrev(); //może być NULL - if (pTrack) //jeśli dalej brakuje toru, to zostajemy na tym samym, z tą samą - orientacją - if (!tLast->iPrevDirection) - fLastDir=-fLastDir; - } - */ - if (pTrack) - { // jeśli kolejny istnieje - if (tLast) - if (pTrack->VelocityGet() < 0 ? tLast->VelocityGet() > 0 : - pTrack->VelocityGet() > tLast->VelocityGet()) - { // jeśli kolejny ma większą prędkość niż poprzedni, to zapamiętać poprzedni - // (do czasu wyjechania) - if ((sSpeedTable[iLast].iFlags & 3) == 3 ? - (sSpeedTable[iLast].trTrack != tLast) : - true) // jeśli nie był dodany do tabelki - if (TableAddNew()) - sSpeedTable[iLast].Set( - tLast, fCurrentDistance, - (fLastDir > 0 ? pTrack->iPrevDirection : - pTrack->iNextDirection) ? - 1 : - 5); // zapisanie toru z ograniczeniem prędkości - } +#ifdef EU07_USE_OLD_SPEEDTABLE + if( ( ( iLast + 3 ) % iSpeedTableSize == iFirst ) ? + true : + ((iLast + 2) % iSpeedTableSize == iFirst)) // czy tabelka się nie zatka? + { // jest ryzyko nieznalezienia ograniczenia - ograniczyć prędkość do pozwalającej + // na zatrzymanie na końcu przeskanowanej drogi + TablePurger(); // usunąć pilnie zbędne pozycje if (((iLast + 3) % iSpeedTableSize == iFirst) ? true : ((iLast + 2) % iSpeedTableSize == iFirst)) // czy tabelka się nie zatka? - { // jest ryzyko nieznalezienia ograniczenia - ograniczyć prędkość do pozwalającej - // na zatrzymanie na końcu przeskanowanej drogi - TablePurger(); // usunąć pilnie zbędne pozycje - if (((iLast + 3) % iSpeedTableSize == iFirst) ? - true : - ((iLast + 2) % iSpeedTableSize == iFirst)) // czy tabelka się nie zatka? - { // jeśli odtykacz nie pomógł (TODO: zwiększyć rozmiar tabelki) - if (TableAddNew()) - sSpeedTable[iLast].Set( - pTrack, fCurrentDistance, - fLastDir < 0 ? - 0x10045 : - 0x10041); // zapisanie toru jako końcowego (ogranicza prędkosć) - // zapisać w logu, że należy poprawić scenerię? - return; // nie skanujemy dalej, bo nie ma miejsca - } + { // jeśli odtykacz nie pomógł (TODO: zwiększyć rozmiar tabelki) + if (TableAddNew()) + sSpeedTable[iLast].Set( + pTrack, fCurrentDistance, + fLastDir < 0 ? + 0x10045 : + 0x10041); // zapisanie toru jako końcowego (ogranicza prędkosć) + // zapisać w logu, że należy poprawić scenerię? + return; // nie skanujemy dalej, bo nie ma miejsca } - fTrackLength = pTrack->Length(); // zwiększenie skanowanej odległości tylko jeśli - // istnieje dalszy tor } - else - { // definitywny koniec skanowania, chyba że dalej puszczamy samochód po gruncie... - if (TableAddNew()) // kolejny, bo się cofnęliśmy o 1 +#endif + // zwiększenie skanowanej odległości tylko jeśli istnieje dalszy tor + fTrackLength = pTrack->Length(); + } + else + { // definitywny koniec skanowania, chyba że dalej puszczamy samochód po gruncie... + if( ( iLast == -1 ) + || ( false == TestFlag( sSpeedTable[iLast].iFlags, spEnabled | spEnd ) ) ) { + // only if we haven't already marked end of the track + if( TableAddNew() ) sSpeedTable[iLast].Set( tLast, fCurrentDistance, - fLastDir < 0 ? 0x45 : 0x41); // zapisanie ostatniego sprawdzonego toru - return; // to ostatnia pozycja, bo NULL nic nie da, a może się podpiąć obrotnica, - // czy jakieś transportery + ( fLastDir < 0 ? + spEnabled | spEnd | spReverse : + spEnabled | spEnd )); // zapisanie ostatniego sprawdzonego toru } + // to ostatnia pozycja, bo NULL nic nie da, a może się podpiąć obrotnica, czy jakieś transportery + return; } - if (TableAddNew()) - sSpeedTable[iLast].Set(pTrack, fCurrentDistance, - fLastDir < 0 ? 4 : 0); // zapisanie ostatniego sprawdzonego toru + } + if( TableAddNew() ) { + // zapisanie ostatniego sprawdzonego toru + sSpeedTable[ iLast ].Set( + pTrack, fCurrentDistance, + ( fLastDir < 0 ? + spNone | spReverse : + spNone ) ); } }; void TController::TableCheck(double fDistance) { // przeliczenie odległości w tabelce, ewentualnie doskanowanie (bez analizy prędkości itp.) - if (iTableDirection != iDirection) - TableTraceRoute(fDistance, - pVehicles[1]); // jak zmiana kierunku, to skanujemy od końca składu + if( iTableDirection != iDirection ) { + // jak zmiana kierunku, to skanujemy od końca składu + TableTraceRoute( fDistance, pVehicles[ 1 ] ); + } else if (iTableDirection) { // trzeba sprawdzić, czy coś się zmieniło - vector3 dir = - pVehicles[0]->VectorFront() * pVehicles[0]->DirectionGet(); // wektor kierunku jazdy + vector3 dir = pVehicles[0]->VectorFront() * pVehicles[0]->DirectionGet(); // wektor kierunku jazdy vector3 pos = pVehicles[0]->HeadPosition(); // zaczynamy od pozycji pojazdu - // double lastspeed=-1; //prędkość na torze do usunięcia double len = 0.0; // odległość będziemy zliczać narastająco +#ifdef EU07_USE_OLD_SPEEDTABLE for (int i = iFirst; i != iLast; i = (i + 1) % iSpeedTableSize) +#else + assert( iLast == sSpeedTable.size() - 1 ); + for( int i = 0; i <= iLast; ++i ) +#endif { // aktualizacja rekordów z wyjątkiem ostatniego if (sSpeedTable[i].iFlags & spEnabled) // jeśli pozycja istotna { if (sSpeedTable[i].Update(&pos, &dir, len)) { - if (Global::iWriteLogEnabled & 8) - WriteLog("TableCheck: Switch change. Delete next entries. (" + sSpeedTable[i].trTrack->NameGet() + ")"); - int k = (iLast + 1) % iSpeedTableSize; // skanujemy razem z ostatnią pozycją - for (int j = (i+1) % iSpeedTableSize; j != k; j = (j + 1) % iSpeedTableSize) + if( Global::iWriteLogEnabled & 8 ) { +/* + WriteLog( "Speed table for " + OwnerName() + " detected switch change at " + sSpeedTable[ i ].trTrack->NameGet() + " (deleting subsequent records)" ); +*/ + WriteLog( "Speed table for " + OwnerName() + " detected switch change at " + sSpeedTable[ i ].trTrack->NameGet() + " (generating fresh trace)" ); + } +#ifdef EU07_USE_OLD_SPEEDTABLE + int k = ( iLast + 1 ) % iSpeedTableSize; // skanujemy razem z ostatnią pozycją + for (int j = (i+1) % iSpeedTableSize; j != k; j = (j + 1) % iSpeedTableSize) { // kasowanie wszystkich rekordów za zmienioną zwrotnicą if (Global::iWriteLogEnabled & 8) WriteLog("TableCheck: Delete from table: " + sSpeedTable[j].GetName()); @@ -718,69 +873,87 @@ void TController::TableCheck(double fDistance) if (&sSpeedTable[j] == sSemNextStop) sSemNextStop = NULL; // przy kasowaniu tabelki zrzucamy także semafor } +#else +/* + while( iLast > i ) { + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "Speed table for" + OwnerName() + " deleting record " + sSpeedTable[ iLast ].GetName() ); + } + // przy kasowaniu tabelki zrzucamy także semafor + if( sSemNext == &sSpeedTable[ iLast ] ) { + sSemNext = nullptr; + } + if( sSemNextStop == &sSpeedTable[ iLast ] ) { + sSemNextStop = nullptr; + } + sSpeedTable.pop_back(); + --iLast; + } + + tLast = sSpeedTable[ i ].trTrack; if (Global::iWriteLogEnabled & 8) { - WriteLog("TableCheck: Delete entries OK."); - WriteLog("TableCheck: New last element: " + sSpeedTable[i].GetName()); + WriteLog( "Speed table for" + OwnerName() + " has new last element, " + sSpeedTable[i].GetName() ); } - iLast = i; // pokazujemy gdzie jest ostatni kawałek +*/ + // NOTE: alternative to the above, dirty trick to perform scan from the beginning + // a workaround for current routines not preserving properly scan distance if started from the middle + // and the scan extending far beyond specified limit + iTableDirection = -iDirection; + TableTraceRoute( fDistance, pVehicles[ 1 ] ); +#endif +#ifdef EU07_USE_OLD_SPEEDTABLE + iLast = i; // pokazujemy gdzie jest ostatni kawałek +#endif break; // nie kontynuujemy pętli, trzeba doskanować ciąg dalszy } if (sSpeedTable[i].iFlags & spTrack) // jeśli odcinek { if (sSpeedTable[i].fDist < -fLength) // a skład wyjechał całą długością poza { // degradacja pozycji - // WriteLog( "TableCheck: Track is behind. Delete from table: " + sSpeedTable[i].trTrack->NameGet()); sSpeedTable[i].iFlags &= ~spEnabled; // nie liczy się } - else if ((sSpeedTable[i].iFlags & 0xF0000028) == - spElapsed) // jest z tyłu (najechany) i nie jest zwrotnicą ani skrzyżowaniem - if (sSpeedTable[i].fVelNext < 0) // a nie ma ograniczenia prędkości + else if( ( sSpeedTable[ i ].iFlags & 0xF0000028 ) == spElapsed ) { + // jest z tyłu (najechany) i nie jest zwrotnicą ani skrzyżowaniem + if( sSpeedTable[ i ].fVelNext < 0 ) // a nie ma ograniczenia prędkości { - sSpeedTable[i].iFlags = - 0; // to nie ma go po co trzymać (odtykacz usunie ze środka) - // WriteLog("TableCheck: Track without speed. Delete from table: " + sSpeedTable[i].trTrack->NameGet()); + sSpeedTable[ i ].iFlags = 0; // to nie ma go po co trzymać (odtykacz usunie ze środka) } + } } else if (sSpeedTable[i].iFlags & spEvent) // jeśli event { - if (sSpeedTable[i].fDist < (sSpeedTable[i].evEvent->Type == tp_PutValues ? - -fLength : - 0)) // jeśli jest z tyłu + if (sSpeedTable[i].fDist < ( + sSpeedTable[i].evEvent->Type == tp_PutValues ? + -fLength : + 0)) // jeśli jest z tyłu if ((mvOccupied->CategoryFlag & 1) ? false : sSpeedTable[i].fDist < -fLength) - { // pociąg staje zawsze, a samochód tylko jeśli nie przejedzie całą - // długością (może być zaskoczony zmianą) + { // pociąg staje zawsze, a samochód tylko jeśli nie przejedzie całą długością (może być zaskoczony zmianą) // WriteLog("TableCheck: Event is behind. Delete from table: " + sSpeedTable[i].evEvent->asName); - sSpeedTable[i].iFlags &= ~1; // degradacja pozycji dla samochodu; - // semafory usuwane tylko przy sprawdzaniu, - // bo wysyłają komendy + sSpeedTable[i].iFlags &= ~spEnabled; // degradacja pozycji dla samochodu; + // semafory usuwane tylko przy sprawdzaniu, bo wysyłają komendy } } - // if (sSpeedTable[i].fDist<-20.0*fLength) //jeśli to coś jest 20 razy dalej niż - // długość składu - //{sSpeedTable[i].iFlags&=~1; //to jest to jakby błąd w scenerii - // //WriteLog("Error: too distant object in scan table"); - //} - // if (sSpeedTable[i].fDist>20.0*fLength) //jeśli to coś jest 20 razy dalej niż - // długość składu - //{sSpeedTable[i].iFlags&=~1; //to jest to jakby błąd w scenerii - // //WriteLog("Error: too distant object in scan table"); - //} } - if (i == iFirst) // jeśli jest pierwszą pozycją tabeli +#ifdef EU07_USE_OLD_SPEEDTABLE + if( i == iFirst ) // jeśli jest pierwszą pozycją tabeli { // pozbycie się początkowej pozycji - if ((sSpeedTable[i].iFlags & 1) == - 0) // jeśli pozycja istotna (po Update() może się zmienić) + if ((sSpeedTable[i].iFlags & spEnabled) == 0) // jeśli pozycja istotna (po Update() może się zmienić) // if (iFirst!=iLast) //ostatnia musi zostać - to załatwia for() - iFirst = (iFirst + 1) % - iSpeedTableSize; // kolejne sprawdzanie będzie już od następnej pozycji + iFirst = (iFirst + 1) % iSpeedTableSize; // kolejne sprawdzanie będzie już od następnej pozycji } +#endif } sSpeedTable[iLast].Update(&pos, &dir, len); // aktualizacja ostatniego // WriteLog("TableCheck: Upate last track. Dist=" + AnsiString(sSpeedTable[iLast].fDist)); - if (sSpeedTable[iLast].fDist < fDistance) - TableTraceRoute(fDistance, pVehicles[1]); // doskanowanie dalszego odcinka + if( sSpeedTable[ iLast ].fDist < fDistance ) { + TableTraceRoute( fDistance, pVehicles[ 1 ] ); // doskanowanie dalszego odcinka + } +#ifndef EU07_USE_OLD_SPEEDTABLE + // garbage collection + TablePurger(); +#endif } }; @@ -796,12 +969,17 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN double d_to_next_sem = 10000.0; //ustaiwamy na pewno dalej niż widzi AI TCommandType go = cm_Unknown; eSignNext = NULL; + // te flagi są ustawiane tutaj, w razie potrzeby + iDrivigFlags &= ~(moveTrackEnd | moveSwitchFound | moveSemaphorFound | moveSpeedLimitFound); + +#ifdef EU07_USE_OLD_SPEEDTABLE int i, k = iLast - iFirst + 1; if (k < 0) k += iSpeedTableSize; // ilość pozycji do przeanalizowania - iDrivigFlags &= ~(moveTrackEnd | moveSwitchFound | moveSemaphorFound | - moveSpeedLimitFound); // te flagi są ustawiane tutaj, w razie potrzeby for (i = iFirst; k > 0; --k, i = (i + 1) % iSpeedTableSize) +#else + for( std::size_t i = 0; i < sSpeedTable.size(); ++i ) +#endif { // sprawdzenie rekordów od (iFirst) do (iLast), o ile są istotne if (sSpeedTable[i].iFlags & spEnabled) // badanie istotności { // o ile dana pozycja tabelki jest istotna @@ -810,21 +988,20 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // first 19 chars of the command is expected to be "PassengerStopPoint:" so we skip them if ( ToLower(sSpeedTable[i].evEvent->CommandGet()).compare( 19, sizeof(asNextStop), ToLower(asNextStop)) != 0 ) { // jeśli nazwa nie jest zgodna - if (sSpeedTable[i].fDist < -fLength) // jeśli został przejechany - sSpeedTable[i].iFlags = - 0; // to można usunąć (nie mogą być usuwane w skanowaniu) + if( sSpeedTable[ i ].fDist < -fLength ) { + // jeśli został przejechany + sSpeedTable[ i ].iFlags = 0; // to można usunąć (nie mogą być usuwane w skanowaniu) + } continue; // ignorowanie jakby nie było tej pozycji } - else if (iDrivigFlags & - moveStopPoint) // jeśli pomijanie W4, to nie sprawdza czasu odjazdu + else if (iDrivigFlags & moveStopPoint) // jeśli pomijanie W4, to nie sprawdza czasu odjazdu { // tylko gdy nazwa zatrzymania się zgadza if (!TrainParams->IsStop()) { // jeśli nie ma tu postoju sSpeedTable[i].fVelNext = -1; // maksymalna prędkość w tym miejscu - if (sSpeedTable[i].fDist < - 200.0) // przy 160km/h jedzie 44m/s, to da dokładność rzędu 5 sekund - { // zaliczamy posterunek w pewnej odległości przed (choć W4 nie zasłania -// już semafora) + // przy 160km/h jedzie 44m/s, to da dokładność rzędu 5 sekund + if (sSpeedTable[i].fDist < 200.0) { + // zaliczamy posterunek w pewnej odległości przed (choć W4 nie zasłania już semafora) #if LOGSTOPS WriteLog( pVehicle->asName + " as " + TrainParams->TrainName @@ -879,56 +1056,43 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN 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 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); - // if (p2&3) //żeby jeszcze poczekał chwilę, zanim - // zamknie + // if (p2&3) //żeby jeszcze poczekał chwilę, zanim zamknie // WaitingSet(10); //10 sekund (wziąć z rozkładu????) } } 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) - p7 = int(floor(sSpeedTable[i].evEvent->ValueGet(2))) % - 10; // tu będzie jeszcze długość peronu zaokrąglona do 10m + { // 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 - lewe = (p->DirectionGet() > 0) ? 1 : 2; // jeśli jedzie do - // tyłu, to drzwi - // otwiera odwrotnie + { // 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; - p->MoverParameters->BatterySwitch(true); // wagony muszą - // mieć baterię - // załączoną do - // otwarcia - // drzwi... + // 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); - p = p->Next(); // pojazd podłączony z tyłu (patrząc od - // czoła) + // pojazd podłączony z tyłu (patrząc od czoła) + p = p->Next(); } // if (p7&3) //żeby jeszcze poczekał chwilę, zanim zamknie // WaitingSet(10); //10 sekund (wziąć z rozkładu????) } - if (fStopTime > - -5) // na końcu rozkładu się ustawia 60s i tu by było skrócenie + if (fStopTime > -5) // na końcu rozkładu się ustawia 60s i tu by było skrócenie WaitingSet(10); // 10 sekund (wziąć z rozkładu????) - czekanie - // niezależne od sposobu obsługi drzwi, bo - // opóźnia również kierownika + // niezależne od sposobu obsługi drzwi, bo opóźnia również kierownika } if (TrainParams->UpdateMTable( simulation::Time, asNextStop) ) { // to się wykona tylko raz po zatrzymaniu na W4 @@ -1062,35 +1226,71 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN else if (sSpeedTable[i].iFlags & spEvent) // W4 może się deaktywować { // jeżeli event, może być potrzeba wysłania komendy, aby ruszył // sprawdzanie eventów pasywnych miniętych +#ifdef EU07_USE_OLD_SPEEDTABLE if (sSpeedTable[i].fDist < 0.0 && sSemNext == &sSpeedTable[i]) +#else + if( (sSpeedTable[ i ].fDist < 0.0) && (SemNextIndex == i) ) +#endif { - if (Global::iWriteLogEnabled & 8) - WriteLog("TableUpdate: semaphor " + sSemNext->GetName() + " passed by " + OwnerName()); - sSemNext = NULL; // jeśli minęliśmy semafor od ograniczenia to go kasujemy ze - // zmiennej sprawdzającej dla skanowania w przód + if( Global::iWriteLogEnabled & 8 ) { +#ifdef EU07_USE_OLD_SPEEDTABLE + WriteLog( "TableUpdate: semaphor " + sSemNext->GetName() + " passed by " + OwnerName() ); + } + sSemNext = NULL; // jeśli minęliśmy semafor od ograniczenia to go kasujemy ze zmiennej sprawdzającej dla skanowania w przód +#else + WriteLog( "Speed table update for " + OwnerName() + ", passed semaphor " + sSpeedTable[ SemNextIndex ].GetName() ); + } + SemNextIndex = -1; // jeśli minęliśmy semafor od ograniczenia to go kasujemy ze zmiennej sprawdzającej dla skanowania w przód +#endif } - if (sSpeedTable[i].fDist < 0.0 && sSemNextStop == &sSpeedTable[i]) +#ifdef EU07_USE_OLD_SPEEDTABLE + if( sSpeedTable[ i ].fDist < 0.0 && sSemNextStop == &sSpeedTable[ i ] ) +#else + if( (sSpeedTable[ i ].fDist < 0.0) && (SemNextStopIndex == i) ) +#endif { - if (Global::iWriteLogEnabled & 8) - WriteLog("TableUpdate: semaphor " + sSemNextStop->GetName() + " passed by " + OwnerName()); - sSemNextStop = NULL; // jeśli minęliśmy semafor od ograniczenia to go kasujemy ze - // zmiennej sprawdzającej dla skanowania w przód +#ifdef EU07_USE_OLD_SPEEDTABLE + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "TableUpdate: semaphor " + sSemNextStop->GetName() + " passed by " + OwnerName() ); + } + sSemNextStop = NULL; // jeśli minęliśmy semafor od ograniczenia to go kasujemy ze zmiennej sprawdzającej dla skanowania w przód +#else + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "Speed table update for " + OwnerName() + ", passed semaphor " + sSpeedTable[ SemNextStopIndex ].GetName() ); + } + SemNextStopIndex = -1; // jeśli minęliśmy semafor od ograniczenia to go kasujemy ze zmiennej sprawdzającej dla skanowania w przód +#endif } if (sSpeedTable[i].fDist > 0.0 && sSpeedTable[i].IsProperSemaphor(OrderCurrentGet())) { - if (!sSemNext) +#ifdef EU07_USE_OLD_SPEEDTABLE + if( !sSemNext ) { - sSemNext = &sSpeedTable[i]; // jeśli jest mienięty poprzedni - // semafor a wcześniej - // byl nowy to go dorzucamy do zmiennej, żeby cały - // czas widział najbliższy + // jeśli jest mienięty poprzedni semafor a wcześniej + // byl nowy to go dorzucamy do zmiennej, żeby cały czas widział najbliższy + sSemNext = &sSpeedTable[ i ]; if (Global::iWriteLogEnabled & 8) WriteLog("TableUpdate: Next semaphor: " + sSemNext->GetName() + " by " + OwnerName()); } - if (!sSemNextStop || (sSemNextStop && sSemNextStop->fVelNext != 0 && - sSpeedTable[i].fVelNext == 0)) - sSemNextStop = &sSpeedTable[i]; + if( !sSemNextStop || ( sSemNextStop && sSemNextStop->fVelNext != 0 && sSpeedTable[ i ].fVelNext == 0 ) ) { + sSemNextStop = &sSpeedTable[ i ]; + } +#else + if( SemNextIndex == -1 ) { + // jeśli jest mienięty poprzedni semafor a wcześniej + // byl nowy to go dorzucamy do zmiennej, żeby cały czas widział najbliższy + SemNextIndex = i; + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "Speed table update for " + OwnerName() + ", next semaphor is " + sSpeedTable[ SemNextIndex ].GetName() ); + } + } + if( ( SemNextStopIndex == -1 ) + || ( ( sSpeedTable[SemNextStopIndex].fVelNext != 0 ) + && ( sSpeedTable[ i ].fVelNext == 0 ) ) ) { + SemNextStopIndex = i; + } +#endif } if (sSpeedTable[i].iFlags & spOutsideStation) { // jeśli W5, to reakcja zależna od trybu jazdy @@ -1373,9 +1573,10 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN void TController::TablePurger() { // odtykacz: usuwa mniej istotne pozycje ze środka tabelki, aby uniknąć zatkania //(np. brak ograniczenia pomiędzy zwrotnicami, usunięte sygnały, minięte odcinki łuku) - if (Global::iWriteLogEnabled & 8) - WriteLog("TablePurger: Czyszczenie tabelki."); - int i, j, k = iLast - iFirst; // może być 15 albo 16 pozycji, ostatniej nie ma co sprawdzać +#ifdef EU07_USE_OLD_SPEEDPOS + if( Global::iWriteLogEnabled & 8 ) + WriteLog("Speed table garbage collection for " + OwnerName()); + int i, j, k = iLast - iFirst; // może być 15 albo 16 pozycji, ostatniej nie ma co sprawdzać if (k < 0) k += iSpeedTableSize; // ilość pozycji do przeanalizowania for (i = iFirst; k > 0; --k, i = (i + 1) % iSpeedTableSize) @@ -1394,7 +1595,7 @@ void TController::TablePurger() sSemNextStop = &sSpeedTable[i]; // przeniesienie znacznika o semaforze } if (Global::iWriteLogEnabled & 8) - WriteLog("Odtykacz usuwa pozycję"); + WriteLog("Speed table garbage collection for " + OwnerName() + ": slot removed"); iLast = (iLast - 1 + iSpeedTableSize) % iSpeedTableSize; // cofnięcie z zawinięciem return; } @@ -1419,10 +1620,57 @@ void TController::TablePurger() sSpeedTable = t; // bo jest nowe iSpeedTableSize += 16; if (Global::iWriteLogEnabled & 8) - WriteLog("Tabelka powiększona do "+std::to_string(iSpeedTableSize)+" pozycji"); + WriteLog("Speed table garbage collection for " + OwnerName() + ": table increased to "+std::to_string(iSpeedTableSize)+" slots"); +#else + // simplest approach should be good enough for start -- just copy whatever is still relevant, then swap + // do a trial run first, to see if we need to bother at all + std::size_t trimcount{ 0 }; + for( std::size_t idx = 0; idx < sSpeedTable.size() - 1; ++idx ) { + auto const &speedpoint = sSpeedTable[ idx ]; + if( ( 0 == ( speedpoint.iFlags & spEnabled ) ) + || ( ( speedpoint.iFlags & ( spElapsed | spTrack | spCurve | spSwitch ) == ( spElapsed | spTrack | spCurve ) ) + && ( speedpoint.fVelNext < 0.0 ) ) ) { + // NOTE: we could break out early here, but running through entire thing gives us exact size needed for new table + ++trimcount; + } + } + if( trimcount == 0 ) { + // there'd be no gain, may as well bail + return; + } + std::vector trimmedtable; trimmedtable.reserve( sSpeedTable.size() - trimcount ); + // we can only update pointers safely after new table is finalized, so record their indices until then + for( std::size_t idx = 0; idx < sSpeedTable.size() - 1; ++idx ) { + // cache placement of semaphors in the new table, if we encounter them + // NOTE: we rely on the semaphor actually getting added in the next step rather than skipped, but it's a pretty safe bet + if( idx == SemNextIndex ) { + SemNextIndex = trimmedtable.size(); + } + if( idx == SemNextStopIndex ) { + SemNextStopIndex = trimmedtable.size(); + } + auto const &speedpoint = sSpeedTable[ idx ]; + if( ( 0 == ( speedpoint.iFlags & spEnabled ) ) + || ( ( speedpoint.iFlags & ( spElapsed | spTrack | spCurve | spSwitch ) == ( spElapsed | spTrack | spCurve ) ) + && ( speedpoint.fVelNext < 0.0 ) ) ) { + continue; + } + // we're left with useful speed point record we should copy + trimmedtable.emplace_back( speedpoint ); + } + // always copy the last entry + trimmedtable.emplace_back( sSpeedTable.back() ); + + if( Global::iWriteLogEnabled & 8 ) { + WriteLog( "Speed table garbage collection for " + OwnerName() + " cut away " + std::to_string( trimcount ) + ( trimcount == 1 ? " record" : " records" ) ); + } + + // update the data + sSpeedTable = trimmedtable; + iLast = sSpeedTable.size() - 1; +#endif }; -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- + //--------------------------------------------------------------------------- TController::TController(bool AI, TDynamicObject *NewControll, bool InitPsyche, bool primary) :// czy ma aktywnie prowadzić? @@ -1473,7 +1721,9 @@ TController::TController(bool AI, TDynamicObject *NewControll, bool InitPsyche, } SetDriverPsyche(); // na końcu, bo wymaga ustawienia zmiennych +#ifdef EU07_USE_OLD_SPEEDTABLE sSpeedTable = new TSpeedPos[iSpeedTableSize]; +#endif TableClear(); if( WriteLogFlag ) { @@ -1512,7 +1762,9 @@ TController::~TController() { // wykopanie mechanika z roboty delete tsGuardSignal; delete TrainParams; +#ifdef EU07_USE_OLD_SPEEDTABLE delete[] sSpeedTable; +#endif CloseLog(); }; @@ -2302,7 +2554,7 @@ bool TController::IncBrake() if (mvOccupied->LocalBrake == ManualBrake) OK = mvOccupied->IncManualBrakeLevel( 1 + static_cast( std::floor( 0.5 + std::fabs(AccDesired))) ); else - OK = mvOccupied->IncLocalBrakeLevel( 1 + static_cast( std::floor( 0.5 + std::fabs(AccDesired))) ); + OK = mvOccupied->IncLocalBrakeLevel( std::floor( 1.5 + std::abs( AccDesired ) ) ); break; case Pneumatic: if ((mvOccupied->Couplers[0].Connected == NULL) && @@ -4180,7 +4432,13 @@ bool TController::UpdateSituation(double dt) TrainParams ->TTVmax); // jesli nie spozniony to nie przekraczać rozkladowej if (VelDesired > 0.0) - if ((sSemNext && sSemNext->fVelNext != 0.0) || (iDrivigFlags & moveStopHere)==0) +#ifdef EU07_USE_OLD_SPEEDPOS + if( ( sSemNext && sSemNext->fVelNext != 0.0 ) || ( iDrivigFlags & moveStopHere ) == 0 ) +#else + if( ( ( SemNextIndex != -1 ) + && ( sSpeedTable[SemNextIndex].fVelNext != 0.0 ) ) + || ( ( iDrivigFlags & moveStopHere ) == 0 ) ) +#endif { // jeśli można jechać, to odpalić dźwięk kierownika oraz zamknąć drzwi w // składzie, jeśli nie mamy czekać na sygnał też trzeba odpalić @@ -5372,13 +5630,22 @@ void TController::ControllingSet() mvControlling = pVehicle->ControlledFind()->MoverParameters; // poszukiwanie członu sterowanego }; -std::string TController::TableText(int i) +std::string TController::TableText( std::size_t const Index ) { // pozycja tabelki prędkości +#ifdef EU07_USE_OLD_SPEEDTABLE i = (iFirst + i) % iSpeedTableSize; // numer pozycji if (i != iLast) // w (iLast) znajduje się kolejny tor do przeskanowania, ale nie jest ona // aktywną return sSpeedTable[i].TableText(); return ""; // wskaźnik końca +#else + if( Index < sSpeedTable.size() ) { + return sSpeedTable[ Index ].TableText(); + } + else { + return ""; + } +#endif }; int TController::CrossRoute(TTrack *tr) @@ -5386,13 +5653,19 @@ int TController::CrossRoute(TTrack *tr) // pożądany numer segmentu jest określany podczas skanowania drogi // droga powinna być określona sposobem przejazdu przez skrzyżowania albo współrzędnymi miejsca // docelowego +#ifdef EU07_USE_OLD_SPEEDTABLE for (int i = iFirst; i != iLast; i = (i + 1) % iSpeedTableSize) +#else + for( std::size_t i = 0; i < sSpeedTable.size(); ++i ) +#endif { // trzeba przejrzeć tabelę skanowania w poszukiwaniu (tr) // i jak się znajdzie, to zwrócić zapamiętany numer segmentu i kierunek przejazdu // (-6..-1,1..6) - if ((sSpeedTable[i].iFlags & 3) == 3) // jeśli pozycja istotna (1) oraz odcinek (2) - if (sSpeedTable[i].trTrack == tr) // jeśli pozycja odpowiadająca skrzyżowaniu (tr) - return (sSpeedTable[i].iFlags >> 28); // najstarsze 4 bity jako liczba -8..7 + if( ( true == TestFlag( sSpeedTable[ i ].iFlags, spEnabled | spTrack ) ) + && ( sSpeedTable[ i ].trTrack == tr ) ) { + // jeśli pozycja odpowiadająca skrzyżowaniu (tr) + return ( sSpeedTable[ i ].iFlags >> 28 ); // najstarsze 4 bity jako liczba -8..7 + } } return 0; // nic nie znaleziono? }; @@ -5400,29 +5673,48 @@ int TController::CrossRoute(TTrack *tr) void TController::RouteSwitch(int d) { // ustawienie kierunku jazdy z kabiny d &= 3; - if (d) - if (iRouteWanted != d) - { // nowy kierunek + if( d ) { + if( iRouteWanted != d ) { // nowy kierunek iRouteWanted = d; // zapamiętanie - if (mvOccupied->CategoryFlag & 2) // jeśli samochód - for (int i = iFirst; i != iLast; i = (i + 1) % iSpeedTableSize) - { // szukanie pierwszego skrzyżowania i resetowanie kierunku na nim - if ((sSpeedTable[i].iFlags & 3) == - 3) // jeśli pozycja istotna (1) oraz odcinek (2) - if ((sSpeedTable[i].iFlags & 32) == 0) // odcinek nie może być miniętym - if (sSpeedTable[i].trTrack->eType == tt_Cross) // jeśli skrzyżowanie - { // obcięcie tabelki skanowania przed skrzyżowaniem, aby ponownie - // wybrać drogę - iLast = i - 1; // ponowne skanowanie skrzyżowania (w zwrotnicach - // jest iLast=i, ale tam jest prościej) - if (iLast < 0) + if( mvOccupied->CategoryFlag & 2 ) { + // jeśli samochód +#ifdef EU07_USE_OLD_SPEEDTABLE + for( int i = iFirst; i != iLast; i = ( i + 1 ) % iSpeedTableSize ) { +#else + for( std::size_t i = 0; i < sSpeedTable.size(); ++i ) { +#endif + // szukanie pierwszego skrzyżowania i resetowanie kierunku na nim + if( true == TestFlag( sSpeedTable[ i ].iFlags, spEnabled | spTrack ) ) { + // jeśli pozycja istotna (1) oraz odcinek (2) + if( false == TestFlag( sSpeedTable[ i ].iFlags, spElapsed ) ) { + // odcinek nie może być miniętym + if( sSpeedTable[ i ].trTrack->eType == tt_Cross ) // jeśli skrzyżowanie +#ifdef EU07_USE_OLD_SPEEDTABLE + { // obcięcie tabelki skanowania przed skrzyżowaniem, aby ponownie wybrać drogę + iLast = i - 1; // ponowne skanowanie skrzyżowania (w zwrotnicach jest iLast=i, ale tam jest prościej) + if( iLast < 0 ) iLast += iSpeedTableSize; // bo tabelka jest zapętlona return; } +#else + { + while( sSpeedTable.size() >= i ) { + // NOTE: we're ignoring semaphor flags and not resetting them like we do for train route trimming + // but what if there's street lights? + // TODO: investigate + sSpeedTable.pop_back(); + } + iLast = sSpeedTable.size(); + } +#endif + } + } } + } } + } }; -std::string TController::OwnerName() +std::string TController::OwnerName() const { return ( pVehicle ? pVehicle->MoverParameters->Name : "none" ); }; diff --git a/Driver.h b/Driver.h index eeddc1a1..4cc24ebb 100644 --- a/Driver.h +++ b/Driver.h @@ -102,6 +102,7 @@ enum TAction enum TSpeedPosFlag { // wartości dla iFlag w TSpeedPos + spNone = 0x0, spEnabled = 0x1, // pozycja brana pod uwagę spTrack = 0x2, // to jest tor spReverse = 0x4, // odwrotnie @@ -126,11 +127,11 @@ enum TSpeedPosFlag class TSpeedPos { // pozycja tabeli prędkości dla AI public: - double fDist; // aktualna odległość (ujemna gdy minięte) - double fVelNext; // prędkość obowiązująca od tego miejsca - double fSectionVelocityDist; // długość ograniczenia prędkości + double fDist{ 0.0 }; // aktualna odległość (ujemna gdy minięte) + double fVelNext{ -1.0 }; // prędkość obowiązująca od tego miejsca + double fSectionVelocityDist{ 0.0 }; // długość ograniczenia prędkości // double fAcc; - int iFlags; // flagi typu wpisu do tabelki + int iFlags{ spNone }; // flagi typu wpisu do tabelki // 1=istotny,2=tor,4=odwrotnie,8-zwrotnica (może się zmienić),16-stan // zwrotnicy,32-minięty,64=koniec,128=łuk // 0x100=event,0x200=manewrowa,0x400=przystanek,0x800=SBL,0x1000=wysłana komenda,0x2000=W5 @@ -138,8 +139,8 @@ class TSpeedPos vector3 vPos; // współrzędne XYZ do liczenia odległości struct { - TTrack *trTrack; // wskaźnik na tor o zmiennej prędkości (zwrotnica, obrotnica) - TEvent *evEvent; // połączenie z eventem albo komórką pamięci + TTrack *trTrack{ nullptr }; // wskaźnik na tor o zmiennej prędkości (zwrotnica, obrotnica) + TEvent *evEvent{ nullptr }; // połączenie z eventem albo komórką pamięci }; void CommandCheck(); @@ -166,16 +167,25 @@ extern bool WriteLogFlag; // logowanie parametrów fizycznych class TController { private: // obsługa tabelki prędkości (musi mieć możliwość odhaczania stacji w rozkładzie) +#ifdef EU07_USE_OLD_SPEEDTABLE TSpeedPos *sSpeedTable = nullptr; // najbliższe zmiany prędkości int iSpeedTableSize = 16; // wielkość tabelki int iFirst = 0; // aktualna pozycja w tabeli (modulo iSpeedTableSize) - int iLast = 0; // ostatnia wypełniona pozycja w tabeli sSpeedTable; +#endif double fLastVel = 0.0; // prędkość na poprzednio sprawdzonym torze TTrack *tLast = nullptr; // ostatni analizowany tor TEvent *eSignSkip = nullptr; // można pominąć ten SBL po zatrzymaniu +#ifdef EU07_USE_OLD_SPEEDTABLE TSpeedPos *sSemNext = nullptr; // następny semafor na drodze zależny od trybu jazdy TSpeedPos *sSemNextStop = nullptr; // następny semafor na drodze zależny od trybu jazdy i na stój +#else + std::size_t SemNextIndex{ -1 }; + std::size_t SemNextStopIndex{ -1 }; +#endif private: // parametry aktualnego składu 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 @@ -358,13 +368,15 @@ class TController TEvent *CheckTrackEvent(double fDirection, TTrack *Track); bool TableCheckEvent(TEvent *e); bool TableAddNew(); - bool TableNotFound(TEvent *e); + bool TableNotFound(TEvent const *Event) const; void TableClear(); TEvent *TableCheckTrackEvent(double fDirection, TTrack *Track); void TableTraceRoute(double fDistance, TDynamicObject *pVehicle = NULL); void TableCheck(double fDistance); TCommandType TableUpdate(double &fVelDes, double &fDist, double &fNext, double &fAcc); void TablePurger(); +public: + std::size_t TableSize() const { return sSpeedTable.size(); } private: // Ra: stare funkcje skanujące, używane do szukania sygnalizatora z tyłu bool BackwardTrackBusy(TTrack *Track); @@ -395,9 +407,9 @@ class TController }; void MoveTo(TDynamicObject *to); void DirectionInitial(); - std::string TableText(int i); + std::string TableText(std::size_t const Index); int CrossRoute(TTrack *tr); void RouteSwitch(int d); - std::string OwnerName(); + std::string OwnerName() const; TMoverParameters const *Controlling() const { return mvControlling; } }; diff --git a/Train.cpp b/Train.cpp index 2e4ac040..f199dcc6 100644 --- a/Train.cpp +++ b/Train.cpp @@ -2562,7 +2562,7 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman if( Train->ggDoorLeftButton.GetValue() > 0.5 ) { Train->play_sound( Train->dsbSwitch ); } - Train->play_sound( Train->dsbDoorOpen ); + Train->play_sound( Train->dsbDoorClose ); } } else { @@ -2573,7 +2573,7 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman if( Train->ggDoorRightButton.GetValue() > 0.5 ) { Train->play_sound( Train->dsbSwitch ); } - Train->play_sound( Train->dsbDoorOpen ); + Train->play_sound( Train->dsbDoorClose ); } } } @@ -2623,7 +2623,7 @@ void TTrain::OnCommand_doortoggleright( TTrain *Train, command_data const &Comma if( Train->ggDoorRightButton.GetValue() > 0.5 ) { Train->play_sound( Train->dsbSwitch ); } - Train->play_sound( Train->dsbDoorOpen ); + Train->play_sound( Train->dsbDoorClose ); } } else { @@ -2634,7 +2634,7 @@ void TTrain::OnCommand_doortoggleright( TTrain *Train, command_data const &Comma if( Train->ggDoorLeftButton.GetValue() > 0.5 ) { Train->play_sound( Train->dsbSwitch ); } - Train->play_sound( Train->dsbDoorOpen ); + Train->play_sound( Train->dsbDoorClose ); } } } diff --git a/World.cpp b/World.cpp index ac5bf33f..75b03532 100644 --- a/World.cpp +++ b/World.cpp @@ -1935,13 +1935,13 @@ TWorld::Update_UI() { break; } - int i = 0; + std::size_t i = 0; std::size_t const speedtablesize = std::min( 30, std::max( 0, static_cast(tmp->Mechanik->TableSize()) - 1 ) ); do { std::string scanline = tmp->Mechanik->TableText( i ); if( scanline.empty() ) { break; } UITable->text_lines.emplace_back( Global::Bezogonkow( scanline ), Global::UITextColor ); ++i; - } while( i < 16 ); // TController:iSpeedTableSize TODO: change when the table gets recoded + } while( i < speedtablesize ); // TController:iSpeedTableSize TODO: change when the table gets recoded } } else { @@ -2039,8 +2039,8 @@ TWorld::Update_UI() { } uitextline1 = - "vel: " + to_string(tmp->GetVelocity(), 2) + " km/h" - + "; dist: " + to_string(tmp->MoverParameters->DistCounter, 2) + " km" + "vel: " + to_string( tmp->GetVelocity(), 2 ) + " km/h" + ( tmp->MoverParameters->SlippingWheels ? " (!)" : "" ) + + "; dist: " + to_string( tmp->MoverParameters->DistCounter, 2 ) + " km" + "; pos: (" + to_string( tmp->GetPosition().x, 2 ) + ", " + to_string( tmp->GetPosition().y, 2 ) + ", " @@ -2573,7 +2573,7 @@ world_environment::update() { m_moon.update(); // ...determine source of key light and adjust global state accordingly... auto const sunlightlevel = m_sun.getIntensity(); - auto const moonlightlevel = m_moon.getIntensity(); + auto const moonlightlevel = m_moon.getIntensity() * 0.65f; // scaled down by arbitrary factor, it's pretty bright otherwise float keylightintensity; float twilightfactor; float3 keylightcolor;