From 98b82636a8caca1986c5e93d7a2351ea60342dc3 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 17 Feb 2018 03:40:44 +0100 Subject: [PATCH] basic train station object, rudimentary passenger loading/unloading on stops, load percentage based submodel visibility --- Driver.cpp | 90 +++++++++++++++------------- DynObj.cpp | 126 ++++++++++++++++++++++++++++------------ DynObj.h | 10 +++- World.cpp | 3 +- World.h | 3 + maszyna.vcxproj.filters | 6 ++ station.cpp | 72 +++++++++++++++++++++++ station.h | 22 +++++++ 8 files changed, 252 insertions(+), 80 deletions(-) create mode 100644 station.cpp create mode 100644 station.h diff --git a/Driver.cpp b/Driver.cpp index 2c6e3618..d105df18 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -892,8 +892,6 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN mvOccupied->DoorLeft(true); if (p2 & prawe) mvOccupied->DoorRight(true); - // if (p2&3) //żeby jeszcze poczekał chwilę, zanim zamknie - // WaitingSet(10); //10 sekund (wziąć z rozkładu????) } } else @@ -917,50 +915,62 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // 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 - WaitingSet( 15.0 + Random( 15.0 ) ); // 10 sekund (wziąć z rozkładu????) - czekanie - // 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 - if (TrainParams->CheckTrainLatency() < 0.0) - iDrivigFlags |= moveLate; // odnotowano spóźnienie - else - iDrivigFlags &= ~moveLate; // przyjazd o czasie - if (TrainParams->DirectionChange()) // jeśli "@" w rozkładzie, to - // wykonanie dalszych komend - { // wykonanie kolejnej komendy, nie dotyczy ostatniej stacji - if (iDrivigFlags & movePushPull) // SN61 ma się też nie ruszać, - // chyba że ma wagony - { + if (TrainParams->UpdateMTable( simulation::Time, asNextStop) ) { + // to się wykona tylko raz po zatrzymaniu na W4 + + // perform loading/unloading + auto const exchangetime { simulation::Station.update_load( pVehicles[ 0 ], *TrainParams ) }; + // TBD: adjust time to load exchange size + if( fStopTime > -55 ) { + // na końcu rozkładu się ustawia 60s i tu by było skrócenie +// WaitingSet( 15.0 + Random( 15.0 ) ); // 10 sekund (wziąć z rozkładu????) - czekanie + // with door open on both sides calculated loading time is halved + // p7=platform side (1:left, 2:right, 3:both) + auto const platformside = static_cast( std::floor( sSpeedTable[ i ].evEvent->ValueGet( 2 ) ) ) % 10; + WaitingSet( + platformside == 3 ? + exchangetime * 0.5 : + exchangetime ); + } + if( TrainParams->CheckTrainLatency() < 0.0 ) { + // odnotowano spóźnienie + iDrivigFlags |= moveLate; + } + else { + // przyjazd o czasie + iDrivigFlags &= ~moveLate; + } + if (TrainParams->DirectionChange()) { + // jeśli "@" w rozkładzie, to wykonanie dalszych komend + // wykonanie kolejnej komendy, nie dotyczy ostatniej stacji + if (iDrivigFlags & movePushPull) { + // SN61 ma się też nie ruszać, chyba że ma wagony iDrivigFlags |= moveStopHere; // EZT ma stać przy peronie - if (OrderNextGet() != Change_direction) - { + if (OrderNextGet() != Change_direction) { OrderPush(Change_direction); // zmiana kierunku - OrderPush(TrainParams->StationIndex < - TrainParams->StationCount ? - Obey_train : - Shunt); // to dalej wg rozkładu + OrderPush( + TrainParams->StationIndex < TrainParams->StationCount ? + Obey_train : + Shunt); // to dalej wg rozkładu } } - else // a dla lokomotyw... - iDrivigFlags &= - ~(moveStopPoint | moveStopHere); // pozwolenie na - // przejechanie za W4 - // przed czasem i nie - // ma stać - JumpToNextOrder(); // przejście do kolejnego rozkazu (zmiana - // kierunku, odczepianie) - iDrivigFlags &= ~moveStopCloser; // ma nie podjeżdżać pod W4 po - // przeciwnej stronie - sSpeedTable[i].iFlags = 0; // ten W4 nie liczy się już zupełnie - // (nie wyśle SetVelocity) - sSpeedTable[i].fVelNext = -1; // jechać - continue; // nie analizować prędkości + else { + // a dla lokomotyw... + // pozwolenie na przejechanie za W4 przed czasem i nie ma stać + iDrivigFlags &= ~( moveStopPoint | moveStopHere ); + } + // przejście do kolejnego rozkazu (zmiana kierunku, odczepianie) + JumpToNextOrder(); + // ma nie podjeżdżać pod W4 po przeciwnej stronie + iDrivigFlags &= ~moveStopCloser; + // ten W4 nie liczy się już zupełnie (nie wyśle SetVelocity) + sSpeedTable[i].iFlags = 0; + // jechać + sSpeedTable[i].fVelNext = -1; + // nie analizować prędkości + continue; } } if (OrderCurrentGet() == Shunt) diff --git a/DynObj.cpp b/DynObj.cpp index 4aa0136c..62d9b37b 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -976,6 +976,10 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) section.load->SetLightLevel( section.level, true ); } } + // load chunks visibility + for( auto const §ion : SectionLoadVisibility ) { + section.submodel->iVisible = section.visible; + } } // ABu 29.01.05 koniec przeklejenia ************************************* @@ -2010,9 +2014,9 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" // check the low poly interior for potential compartments of interest, ie ones which can be individually lit // TODO: definition of relevant compartments in the .mmd file TSubModel *submodel { nullptr }; - if( ( submodel = mdLowPolyInt->GetFromName( "cab1" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); } - if( ( submodel = mdLowPolyInt->GetFromName( "cab2" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); } - if( ( submodel = mdLowPolyInt->GetFromName( "cab0" ) ) != nullptr ) { SectionLightLevels.emplace_back( submodel, nullptr, 0.0f ); } + if( ( submodel = mdLowPolyInt->GetFromName( "cab1" ) ) != nullptr ) { SectionLightLevels.push_back( { submodel, nullptr, 0.0f } ); } + if( ( submodel = mdLowPolyInt->GetFromName( "cab2" ) ) != nullptr ) { SectionLightLevels.push_back( { submodel, nullptr, 0.0f } ); } + if( ( submodel = mdLowPolyInt->GetFromName( "cab0" ) ) != nullptr ) { SectionLightLevels.push_back( { submodel, nullptr, 0.0f } ); } // passenger car compartments std::vector nameprefixes = { "corridor", "korytarz", "compartment", "przedzial" }; int compartmentindex; @@ -2027,19 +2031,17 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" std::to_string( compartmentindex ) ); submodel = mdLowPolyInt->GetFromName( compartmentname ); if( submodel != nullptr ) { - // if specified compartment was found we check also for potential matching section in the currently assigned load - // NOTE: if the load gets changed this will invalidate stored pointers. TODO: rebuild the table on load change - SectionLightLevels.emplace_back( + SectionLightLevels.push_back( { submodel, - ( mdLoad != nullptr ? - mdLoad->GetFromName( compartmentname ): - nullptr ), - 0.0f ); + nullptr, // pointers to load sections are generated afterwards + 0.0f } ); } ++compartmentindex; } while( ( submodel != nullptr ) || ( compartmentindex < 2 ) ); // chain can start from prefix00 or prefix01 } + update_load_sections(); + update_load_visibility(); } // wyszukiwanie zderzakow if( mdModel ) { @@ -2428,31 +2430,83 @@ bool TDynamicObject::UpdateForce(double dt, double dt1, bool FullVer) return true; } -void TDynamicObject::LoadUpdate() -{ // przeładowanie modelu ładunku +void TDynamicObject::LoadUpdate() { + // przeładowanie modelu ładunku // Ra: nie próbujemy wczytywać modeli miliony razy podczas renderowania!!! - if ((mdLoad == NULL) && (MoverParameters->Load > 0)) - { - std::string asLoadName = - asBaseDir + MoverParameters->LoadType + ".t3d"; // zapamiętany katalog pojazdu - // asLoadName=MoverParameters->LoadType; - // if (MoverParameters->LoadType!=AnsiString("passengers")) - Global.asCurrentTexturePath = asBaseDir; // bieżąca ścieżka do tekstur to dynamic/... - mdLoad = TModelsManager::GetModel(asLoadName.c_str()); // nowy ładunek - Global.asCurrentTexturePath = - std::string(szTexturePath); // z powrotem defaultowa sciezka do tekstur - // Ra: w MMD można by zapisać położenie modelu ładunku (np. węgiel) w - // zależności od - // załadowania + if( ( mdLoad == nullptr ) + && ( MoverParameters->Load > 0 ) ) { + + if( false == MoverParameters->LoadType.empty() ) { + + Global.asCurrentTexturePath = asBaseDir; // bieżąca ścieżka do tekstur to dynamic/... + + // try first specialized version of the load model, vehiclename_loadname + auto const specializedloadfilename { asBaseDir + MoverParameters->TypeName + "_" + MoverParameters->LoadType }; + if( ( true == FileExists( specializedloadfilename + ".e3d" ) ) + || ( true == FileExists( specializedloadfilename + ".t3d" ) ) ) { + mdLoad = TModelsManager::GetModel( specializedloadfilename, true ); + } + if( mdLoad == nullptr ) { + // if this fails, try generic load model + mdLoad = TModelsManager::GetModel( asBaseDir + MoverParameters->LoadType, true ); + } + // update bindings between lowpoly sections and potential load chunks placed inside them + update_load_sections(); + + Global.asCurrentTexturePath = std::string( szTexturePath ); // z powrotem defaultowa sciezka do tekstur + } + // Ra: w MMD można by zapisać położenie modelu ładunku (np. węgiel) w zależności od załadowania + } + else if( MoverParameters->Load == 0 ) { + // nie ma ładunku + mdLoad = nullptr; + // erase bindings between lowpoly sections and potential load chunks placed inside them + update_load_sections(); } - else if (MoverParameters->Load == 0) - mdLoad = NULL; // nie ma ładunku - // if ((mdLoad==NULL)&&(MoverParameters->Load>0)) - // { - // mdLoad=NULL; //Ra: to jest tu bez sensu - co autor miał na myśli? - // } MoverParameters->LoadStatus &= 3; // po zakończeniu będzie równe zero -}; +} + +void +TDynamicObject::update_load_sections() { + + SectionLoadVisibility.clear(); + + for( auto §ion : SectionLightLevels ) { + + section.load = ( + mdLoad != nullptr ? + mdLoad->GetFromName( section.compartment->pName ) : + nullptr ); + + if( section.load != nullptr ) { + SectionLoadVisibility.push_back( { section.load, false } ); + } + } + std::shuffle( std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ), Global.random_engine ); +} + +void +TDynamicObject::update_load_visibility() { + + if( Random() < 0.25 ) { + std::shuffle( std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ), Global.random_engine ); + } + + auto loadpercentage { ( + MoverParameters->MaxLoad == 0.0 ? + 0.0 : + 100.0 * MoverParameters->Load / MoverParameters->MaxLoad ) }; + auto const sectionloadpercentage { ( + SectionLoadVisibility.empty() ? + 0.0 : + 100.0 / SectionLoadVisibility.size() ) }; + // set as many sections as we can, given overall load percentage and how much of full percentage is covered by each chunk + std::for_each( + std::begin( SectionLoadVisibility ), std::end( SectionLoadVisibility ), + [&]( section_visibility §ion ) { + section.visible = ( loadpercentage > 0.0 ); + loadpercentage -= sectionloadpercentage; } ); +} /* Ra: @@ -3637,7 +3691,7 @@ void TDynamicObject::RenderSounds() { } // NBMX Obsluga drzwi, MC: zuniwersalnione if( ( true == MoverParameters->DoorLeftOpened ) - && ( dDoorMoveL < MoverParameters->DoorMaxShiftL ) ) { + && ( dDoorMoveL < MoverParameters->DoorMaxShiftL ) ) { for( auto &door : m_doorsounds ) { if( door.rsDoorClose.offset().x > 0.f ) { @@ -3648,7 +3702,7 @@ void TDynamicObject::RenderSounds() { } } if( ( false == MoverParameters->DoorLeftOpened ) - && ( dDoorMoveL > 0.01 ) ) { + && ( dDoorMoveL > 0.01 ) ) { for( auto &door : m_doorsounds ) { if( door.rsDoorClose.offset().x > 0.f ) { @@ -3659,7 +3713,7 @@ void TDynamicObject::RenderSounds() { } } if( ( true == MoverParameters->DoorRightOpened ) - && ( dDoorMoveR < MoverParameters->DoorMaxShiftR ) ) { + && ( dDoorMoveR < MoverParameters->DoorMaxShiftR ) ) { for( auto &door : m_doorsounds ) { if( door.rsDoorClose.offset().x < 0.f ) { @@ -3670,7 +3724,7 @@ void TDynamicObject::RenderSounds() { } } if( ( false == MoverParameters->DoorRightOpened ) - && ( dDoorMoveR > 0.01 ) ) { + && ( dDoorMoveR > 0.01 ) ) { for( auto &door : m_doorsounds ) { if( door.rsDoorClose.offset().x < 0.f ) { diff --git a/DynObj.h b/DynObj.h index 5157478d..2cbe2230 100644 --- a/DynObj.h +++ b/DynObj.h @@ -194,13 +194,15 @@ public: TSubModel *compartment; TSubModel *load; float level; - section_light( TSubModel *Compartment, TSubModel *Load, float const Level ) : - compartment(Compartment), load(Load), level(Level) - {} }; std::vector SectionLightLevels; // table of light levels for specific compartments of associated 3d model bool SectionLightsActive { false }; // flag indicating whether section lights were set. float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?) + struct section_visibility { + TSubModel *submodel; + bool visible; + }; + std::vector SectionLoadVisibility; // visibility of specific sections of the load 3d model private: // zmienne i metody do animacji submodeli; Ra: sprzatam animacje w pojeździe @@ -464,6 +466,8 @@ private: void AttachPrev(TDynamicObject *Object, int iType = 1); bool UpdateForce(double dt, double dt1, bool FullVer); void LoadUpdate(); + void update_load_sections(); + void update_load_visibility(); bool Update(double dt, double dt1); bool FastUpdate(double dt); void Move(double fDistance); diff --git a/World.cpp b/World.cpp index edc44db8..952d2e41 100644 --- a/World.cpp +++ b/World.cpp @@ -45,6 +45,7 @@ namespace simulation { simulation_time Time; +basic_station Station; } extern "C" @@ -1338,7 +1339,7 @@ TWorld::Update_UI() { if( ( vehicle->MoverParameters->BrakeDelayFlag & bdelay_M ) == bdelay_M ) uitextline2 += "+Mg"; - uitextline2 += ", Load: " + to_string( vehicle->MoverParameters->LoadFlag, 0 ); + uitextline2 += ", Load: " + to_string( vehicle->MoverParameters->LoadFlag, 0 ) + " (" + to_string( vehicle->MoverParameters->Load, 0 ) + ")"; uitextline2 += "; Pant: " diff --git a/World.h b/World.h index c9654100..b0664611 100644 --- a/World.h +++ b/World.h @@ -20,6 +20,7 @@ http://mozilla.org/MPL/2.0/. #include "stars.h" #include "skydome.h" #include "messaging.h" +#include "station.h" // wrapper for simulation time class simulation_time { @@ -59,6 +60,8 @@ namespace simulation { extern simulation_time Time; +extern basic_station Station; // temporary object, for station functionality tests + } // wrapper for environment elements -- sky, sun, stars, clouds etc diff --git a/maszyna.vcxproj.filters b/maszyna.vcxproj.filters index 755e3ad1..9c66e203 100644 --- a/maszyna.vcxproj.filters +++ b/maszyna.vcxproj.filters @@ -237,6 +237,9 @@ Source Files\input + + Source Files + @@ -461,6 +464,9 @@ Header Files\input + + Header Files + diff --git a/station.cpp b/station.cpp new file mode 100644 index 00000000..2e4a7a98 --- /dev/null +++ b/station.cpp @@ -0,0 +1,72 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "station.h" + +#include "dynobj.h" +#include "mtable.h" + +// exchanges load with consist attached to specified vehicle, operating on specified schedule +double +basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule ) { + + auto const firststop { Schedule.StationIndex == 1 }; + auto const laststop { Schedule.StationIndex == Schedule.StationCount }; + // HACK: determine whether current station is a (small) stop from the presence of "po" at the name end + auto const stationname { Schedule.TimeTable[ Schedule.StationIndex ].StationName }; + auto const stationequipment { Schedule.TimeTable[ Schedule.StationIndex ].StationWare }; + auto const trainstop { ( + ( ( stationname.size() >= 2 ) && ( stationname.substr( stationname.size() - 2 ) == "po" ) ) + || ( stationequipment.find( "po" ) != std::string::npos ) ) }; + // train stops exchange smaller groups than regular stations + // NOTE: this is crude and unaccurate, but for now will do + auto const stationsizemodifier { ( trainstop ? 1.0 : 2.0 ) }; + // go through all vehicles and update their load + // NOTE: for the time being we limit ourselves to passenger-carrying cars only + auto exchangetime { 0.0 }; + + auto *vehicle { First }; + while( vehicle != nullptr ) { + + auto ¶meters { *vehicle->MoverParameters }; + + if( ( true == parameters.LoadType.empty() ) + && ( parameters.LoadAccepted.find( "passengers" ) != std::string::npos ) ) { + // set the type for empty cars + parameters.LoadType = "passengers"; + } + + 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 loadcount = static_cast( + laststop ? + 0 : + Random( parameters.MaxLoad * 0.15 * stationsizemodifier ) ); + if( true == firststop ) { + // 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 ); + vehicle->LoadUpdate(); + vehicle->update_load_visibility(); + + exchangetime = std::max( exchangetime, loadcount / parameters.LoadSpeed + unloadcount / parameters.UnLoadSpeed ); + } + vehicle = vehicle->Next(); + } + + return exchangetime; +} diff --git a/station.h b/station.h new file mode 100644 index 00000000..34b915f7 --- /dev/null +++ b/station.h @@ -0,0 +1,22 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include "classes.h" + +// a simple station, performs freight and passenger exchanges with visiting consists +class basic_station { + +public: +// methods + // exchanges load with consist attached to specified vehicle, operating on specified schedule; returns: time needed for exchange, in seconds + double + update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule ); +}; \ No newline at end of file