diff --git a/Driver.cpp b/Driver.cpp index d105df18..52585c06 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -934,6 +934,11 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN exchangetime * 0.5 : exchangetime ); } + // update brake settings and ai braking tables + // NOTE: this calculation is run after completing loading/unloading + // remember to move it to a more fitting spot, when loading/unloading taks some actual time + AutoRewident(); // nastawianie hamulca do jazdy pociągowej + if( TrainParams->CheckTrainLatency() < 0.0 ) { // odnotowano spóźnienie iDrivigFlags |= moveLate; @@ -1783,34 +1788,55 @@ void TController::AutoRewident() p = 0; // będziemy tu liczyć wagony od lokomotywy dla nastawy GP while (d) { // 3. Nastawianie - switch (ustaw) - { - case bdelay_P: // towarowy P - lokomotywa na G, reszta na P. - d->MoverParameters->BrakeDelaySwitch(d->MoverParameters->Power > 1 ? bdelay_G : - bdelay_P); - break; - case bdelay_G: // towarowy G - wszystko na G, jeśli nie ma to P (powinno się wyłączyć - // hamulec) - d->MoverParameters->BrakeDelaySwitch( - TestFlag(d->MoverParameters->BrakeDelays, bdelay_G) ? bdelay_G : bdelay_P); - break; - case bdelay_R: // towarowy GP - lokomotywa oraz 5 pierwszych pojazdów przy niej na G, reszta - // na P - if (d->MoverParameters->Power > 1) - { - d->MoverParameters->BrakeDelaySwitch(bdelay_G); - p = 0; // a jak będzie druga w środku? + if( ( true == AIControllFlag ) + || ( d != pVehicle ) ) { + // don't touch human-controlled vehicle, but others are free game + switch( ustaw ) { + + case bdelay_P: { + // towarowy P - lokomotywa na G, reszta na P. + d->MoverParameters->BrakeDelaySwitch( + d->MoverParameters->Power > 1 ? + bdelay_G : + bdelay_P ); + break; + } + case bdelay_G: { + // towarowy G - wszystko na G, jeśli nie ma to P (powinno się wyłączyć hamulec) + d->MoverParameters->BrakeDelaySwitch( + TestFlag( d->MoverParameters->BrakeDelays, bdelay_G ) ? + bdelay_G : + bdelay_P ); + break; + } + case bdelay_R: { + // towarowy GP - lokomotywa oraz 5 pierwszych pojazdów przy niej na G, reszta na P + if( d->MoverParameters->Power > 1 ) { + d->MoverParameters->BrakeDelaySwitch( bdelay_G ); + p = 0; // a jak będzie druga w środku? + } + else { + d->MoverParameters->BrakeDelaySwitch( + ++p <= 5 ? + bdelay_G : + bdelay_P ); + } + break; + } + case 16 + bdelay_R: { + // pasażerski R - na R, jeśli nie ma to P + d->MoverParameters->BrakeDelaySwitch( + TestFlag( d->MoverParameters->BrakeDelays, bdelay_R ) ? + bdelay_R : + bdelay_P ); + break; + } + case 16 + bdelay_P: { + // pasażerski P - wszystko na P + d->MoverParameters->BrakeDelaySwitch( bdelay_P ); + break; + } } - else - d->MoverParameters->BrakeDelaySwitch(++p <= 5 ? bdelay_G : bdelay_P); - break; - case 16 + bdelay_R: // pasażerski R - na R, jeśli nie ma to P - d->MoverParameters->BrakeDelaySwitch( - TestFlag(d->MoverParameters->BrakeDelays, bdelay_R) ? bdelay_R : bdelay_P); - break; - case 16 + bdelay_P: // pasażerski P - wszystko na P - d->MoverParameters->BrakeDelaySwitch(bdelay_P); - break; } d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) } @@ -2105,22 +2131,16 @@ void TController::SetVelocity(double NewVel, double NewVelNext, TStopReason r) if (OrderList[OrderPos] ? OrderList[OrderPos] & (Obey_train | Shunt | Connect | Prepare_engine) : true) // jeśli jedzie w dowolnym trybie - if ((mvOccupied->Vel < - 1.0)) // jesli stoi (na razie, bo chyba powinien też, gdy hamuje przed semaforem) + if ((mvOccupied->Vel < 1.0)) // jesli stoi (na razie, bo chyba powinien też, gdy hamuje przed semaforem) if (iDrivigFlags & moveStartHorn) // jezeli trąbienie włączone - if (!(iDrivigFlags & (moveStartHornDone | moveConnect))) // jeśli nie zatrąbione - // i nie jest to moment - // podłączania składu - if (mvOccupied->CategoryFlag & 1) // tylko pociągi trąbią (unimogi tylko na - // torach, więc trzeba raczej sprawdzać - // tor) - if ((NewVel >= 1.0) || (NewVel < 0.0)) // o ile prędkość jest znacząca - { // fWarningDuration=0.3; //czas trąbienia - // if (AIControllFlag) //jak siedzi krasnoludek, to włączy trąbienie - // mvOccupied->WarningSignal=pVehicle->iHornWarning; //wysokość tonu - // (2=wysoki) - // iDrivigFlags|=moveStartHornDone; //nie trąbić aż do ruszenia - iDrivigFlags |= moveStartHornNow; // zatrąb po odhamowaniu + if (!(iDrivigFlags & (moveStartHornDone | moveConnect))) + // jeśli nie zatrąbione i nie jest to moment podłączania składu + if (mvOccupied->CategoryFlag & 1) + // tylko pociągi trąbią (unimogi tylko na torach, więc trzeba raczej sprawdzać tor) + if ((NewVel >= 1.0) || (NewVel < 0.0)) { + // o ile prędkość jest znacząca + // zatrąb po odhamowaniu + iDrivigFlags |= moveStartHornNow; } } VelSignal = NewVel; // prędkość zezwolona na aktualnym odcinku @@ -3659,12 +3679,9 @@ TController::UpdateSituation(double dt) { // but when enabled all the time it produces silly effect // przy prowadzeniu samochodu trzeba każdą oś odsuwać oddzielnie, inaczej kicha wychodzi if (mvOccupied->CategoryFlag & 2) // jeśli samochód - // if (fabs(mvOccupied->OffsetTrackH)Dim.W) //Ra: szerokość drogi tu - // powinna być? - if (!mvOccupied->ChangeOffsetH(-0.01 * mvOccupied->Vel * dt)) // ruch w poprzek - // drogi - mvOccupied->ChangeOffsetH(0.01 * mvOccupied->Vel * - dt); // Ra: co to miało być, to nie wiem + // if (fabs(mvOccupied->OffsetTrackH)Dim.W) //Ra: szerokość drogi tu powinna być? + if (!mvOccupied->ChangeOffsetH(-0.01 * mvOccupied->Vel * dt)) // ruch w poprzek drogi + mvOccupied->ChangeOffsetH(0.01 * mvOccupied->Vel * dt); // Ra: co to miało być, to nie wiem */ } @@ -3731,7 +3748,7 @@ TController::UpdateSituation(double dt) { if( fWarningDuration < 0.05 ) mvOccupied->WarningSignal = 0; // a tu się kończy } - if( mvOccupied->Vel >= 3.0 ) { + if( mvOccupied->Vel >= 5.0 ) { // jesli jedzie, można odblokować trąbienie, bo się wtedy nie włączy iDrivigFlags &= ~moveStartHornDone; // zatrąbi dopiero jak następnym razem stanie iDrivigFlags |= moveStartHorn; // i trąbić przed następnym ruszeniem @@ -3740,7 +3757,7 @@ TController::UpdateSituation(double dt) { if( ( true == TestFlag( iDrivigFlags, moveStartHornNow ) ) && ( true == Ready ) && ( iEngineActive != 0 ) - && ( fStopTime >= 0 ) ) { + && ( mvControlling->MainCtrlPos > 0 ) ) { // uruchomienie trąbienia przed ruszeniem fWarningDuration = 0.3; // czas trąbienia mvOccupied->WarningSignal = pVehicle->iHornWarning; // wysokość tonu (2=wysoki) diff --git a/DynObj.cpp b/DynObj.cpp index 62d9b37b..255fc17c 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -554,14 +554,14 @@ TDynamicObject::toggle_lights() { for( auto §ionlight : SectionLightLevels ) { std::string const &compartmentname = sectionlight.compartment->pName; - if( ( compartmentname.find( "corridor" ) != std::string::npos ) - || ( compartmentname.find( "korytarz" ) != std::string::npos ) ) { + if( ( compartmentname.find( "corridor" ) == 0 ) + || ( compartmentname.find( "korytarz" ) == 0 ) ) { // corridors are lit 100% of time sectionlight.level = 0.75f; } else if( - ( compartmentname.find( "compartment" ) != std::string::npos ) - || ( compartmentname.find( "przedzial" ) != std::string::npos ) ) { + ( compartmentname.find( "compartment" ) == 0 ) + || ( compartmentname.find( "przedzial" ) == 0 ) ) { // compartments are lit with 75% probability sectionlight.level = ( Random() < 0.75 ? 0.75f : 0.15f ); } @@ -979,6 +979,18 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) // load chunks visibility for( auto const §ion : SectionLoadVisibility ) { section.submodel->iVisible = section.visible; + if( false == section.visible ) { + // if the section root isn't visible we can skip meddling with its children + continue; + } + // if the section root is visible set the state of section chunks + auto *sectionchunk { section.submodel->ChildGet() }; + auto visiblechunkcount { section.visible_chunks }; + while( sectionchunk != nullptr ) { + sectionchunk->iVisible = ( visiblechunkcount > 0 ); + --visiblechunkcount; + sectionchunk = sectionchunk->NextGet(); + } } } // ABu 29.01.05 koniec przeklejenia ************************************* @@ -2450,6 +2462,11 @@ void TDynamicObject::LoadUpdate() { // if this fails, try generic load model mdLoad = TModelsManager::GetModel( asBaseDir + MoverParameters->LoadType, true ); } + if( mdLoad != nullptr ) { + // TODO: discern from vehicle component which merely uses vehicle directory and has no animations, so it can be initialized outright + // and actual vehicles which get their initialization after their animations are set up + mdLoad->Init(); + } // update bindings between lowpoly sections and potential load chunks placed inside them update_load_sections(); @@ -2478,18 +2495,19 @@ TDynamicObject::update_load_sections() { mdLoad->GetFromName( section.compartment->pName ) : nullptr ); - if( section.load != nullptr ) { + if( ( section.load != nullptr ) + && ( section.load->count_children() > 0 ) ) { SectionLoadVisibility.push_back( { section.load, false } ); } } - std::shuffle( std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ), Global.random_engine ); + shuffle_load_sections(); } void TDynamicObject::update_load_visibility() { if( Random() < 0.25 ) { - std::shuffle( std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ), Global.random_engine ); + shuffle_load_sections(); } auto loadpercentage { ( @@ -2505,7 +2523,33 @@ TDynamicObject::update_load_visibility() { std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ), [&]( section_visibility §ion ) { section.visible = ( loadpercentage > 0.0 ); - loadpercentage -= sectionloadpercentage; } ); + section.visible_chunks = 0; + auto const sectionchunkcount { section.submodel->count_children() }; + auto const sectionchunkloadpercentage{ ( + sectionchunkcount == 0 ? + 0.0 : + sectionloadpercentage / sectionchunkcount ) }; + auto *sectionchunk { section.submodel->ChildGet() }; + while( sectionchunk != nullptr ) { + if( loadpercentage > 0.0 ) { + ++section.visible_chunks; + loadpercentage -= sectionchunkloadpercentage; + } + sectionchunk = sectionchunk->NextGet(); + } } ); +} + +void +TDynamicObject::shuffle_load_sections() { + + std::shuffle( std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ), Global.random_engine ); + // shift chunks assigned to corridors to the end of the list, so they show up last + std::stable_partition( + std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ), + []( section_visibility const §ion ) { + return ( + ( section.submodel->pName.find( "compartment" ) == 0 ) + || ( section.submodel->pName.find( "przedzial" ) == 0 ) ); } ); } /* @@ -3907,7 +3951,7 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, std::string asFileName = BaseDir + TypeName + ".mmd"; std::string asLoadName; if( false == MoverParameters->LoadType.empty() ) { - asLoadName = BaseDir + MoverParameters->LoadType + ".t3d"; + asLoadName = BaseDir + MoverParameters->LoadType; } std::string asAnimName; diff --git a/DynObj.h b/DynObj.h index 2cbe2230..9d48f188 100644 --- a/DynObj.h +++ b/DynObj.h @@ -201,6 +201,7 @@ public: struct section_visibility { TSubModel *submodel; bool visible; + int visible_chunks; }; std::vector SectionLoadVisibility; // visibility of specific sections of the load 3d model @@ -468,6 +469,7 @@ private: void LoadUpdate(); void update_load_sections(); void update_load_visibility(); + void shuffle_load_sections(); bool Update(double dt, double dt1); bool FastUpdate(double dt); void Move(double fDistance); diff --git a/Model3d.cpp b/Model3d.cpp index f232eb02..697c3552 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -725,6 +725,25 @@ void TSubModel::NextAdd(TSubModel *SubModel) Next = SubModel; }; +int TSubModel::count_siblings() { + + auto siblingcount { 0 }; + auto *sibling { Next }; + while( sibling != nullptr ) { + ++siblingcount; + sibling = sibling->Next; + } + return siblingcount; +} + +int TSubModel::count_children() { + + return ( + Child == nullptr ? + 0 : + 1 + Child->count_siblings() ); +} + int TSubModel::FlagsCheck() { // analiza koniecznych zmian pomiędzy submodelami // samo pomijanie glBindTexture() nie poprawi wydajności diff --git a/Model3d.h b/Model3d.h index b7ee145f..73e18d8d 100644 --- a/Model3d.h +++ b/Model3d.h @@ -160,6 +160,8 @@ public: void NextAdd(TSubModel *SubModel); TSubModel * NextGet() { return Next; }; TSubModel * ChildGet() { return Child; }; + int count_siblings(); + int count_children(); int TriangleAdd(TModel3d *m, material_handle tex, int tri); void SetRotate(float3 vNewRotateAxis, float fNewAngle); void SetRotateXYZ( Math3D::vector3 vNewAngles); diff --git a/station.cpp b/station.cpp index 2e4a7a98..601efe6f 100644 --- a/station.cpp +++ b/station.cpp @@ -46,6 +46,12 @@ basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Sch if( parameters.LoadType == "passengers" ) { // NOTE: for the time being we're doing simple, random load change calculation // TODO: exchange driven by station parameters and time of the day + auto unloadcount = static_cast( + firststop ? 0 : + laststop ? parameters.Load : + std::min( + parameters.Load, + Random( parameters.MaxLoad * 0.10 * stationsizemodifier ) ) ); auto loadcount = static_cast( laststop ? 0 : @@ -54,16 +60,11 @@ basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Sch // slightly larger group at the initial station loadcount *= 2; } - auto unloadcount = static_cast( - firststop ? 0 : - laststop ? parameters.Load : - Random( parameters.MaxLoad * 0.10 * stationsizemodifier ) ); - - parameters.Load = clamp( parameters.Load + loadcount - unloadcount, 0, parameters.MaxLoad ); + parameters.Load = std::min( parameters.MaxLoad, parameters.Load - unloadcount + loadcount ); vehicle->LoadUpdate(); vehicle->update_load_visibility(); - exchangetime = std::max( exchangetime, loadcount / parameters.LoadSpeed + unloadcount / parameters.UnLoadSpeed ); + exchangetime = std::max( exchangetime, unloadcount / parameters.UnLoadSpeed + loadcount / parameters.LoadSpeed); } vehicle = vehicle->Next(); }