diff --git a/CMakeLists.txt b/CMakeLists.txt index d012efe2..411e34ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,13 @@ file(GLOB HEADERS "*.h" "network/backend/*.h" "widgets/*.h") +if (APPLE) + set (CMAKE_EXE_LINKER_FLAGS "-pagezero_size 10000 -image_base 100000000") +endif() + +option(USE_IMGUI_GL3 "Use OpenGL3+ imgui implementation" ON) +option(WITH_UART "Compile with libserialport" ON) + set(SOURCES "Texture.cpp" "Timer.cpp" @@ -86,7 +93,6 @@ set(SOURCES "material.cpp" "lua.cpp" "stdafx.cpp" -"uart.cpp" "messaging.cpp" "scene.cpp" "scenenode.cpp" @@ -117,6 +123,7 @@ set(SOURCES "simulationstateserializer.cpp" "precipitation.cpp" "pythonscreenviewer.cpp" +"dictionary.cpp" "network/network.cpp" "network/message.cpp" @@ -157,8 +164,6 @@ set(SOURCES "extras/VS_Dev.cpp" ) -option(USE_IMGUI_GL3 "Use OpenGL3+ imgui implementation" ON) - if (USE_IMGUI_GL3) set(SOURCES ${SOURCES} "imgui/imgui_impl_opengl3.cpp") else() @@ -166,15 +171,20 @@ else() set(SOURCES ${SOURCES} "imgui/imgui_impl_opengl2.cpp") endif() +if (WITH_UART) + add_definitions(-DWITH_UART) + set(SOURCES ${SOURCES} "uart.cpp") +endif() + set (PREFIX "") if (WIN32) add_definitions(-DHAVE_ROUND) # to make pymath to not redefine round - add_definitions(-DEU07_BUILD_STATIC) # to make pymath to not redefine round + add_definitions(-DGLFW_DLL) set(SOURCES ${SOURCES} "windows.cpp" "Console.cpp" "Console/LPT.cpp" "Console/PoKeys55.cpp") set(GLFW3_INCLUDE_DIR ${GLFW3_INCLUDE_DIR} "${DEPS_DIR}/glfw/include/") - set(PNG_PNG_INCLUDE_DIR ${PNG_PNG_INCLUDE_DIR} "${DEPS_DIR}/libpng/include/") + set(PNG_INCLUDE_DIRS ${PNG_INCLUDE_DIRS} "${DEPS_DIR}/libpng/include/") set(ZLIB_INCLUDE_DIR ${ZLIB_INCLUDE_DIR} "${DEPS_DIR}/zlib/") set(OPENAL_INCLUDE_DIR ${OPENAL_INCLUDE_DIR} "${DEPS_DIR}/openal/include") set(LIBSNDFILE_INCLUDE_DIR ${LIBSNDFILE_INCLUDE_DIR} "${DEPS_DIR}/libsndfile/include") @@ -190,12 +200,12 @@ if (WIN32) set (PREFIX "-${ARCH}") - set(GLFW3_LIBRARIES ${GLFW3_LIBRARIES} "${DEPS_DIR}/glfw/lib/${ARCH}/glfw3.lib") - set(PNG_LIBRARY ${PNG_LIBRARY} "${DEPS_DIR}/libpng/lib/${ARCH}/libpng16.lib") + set(GLFW3_LIBRARIES ${GLFW3_LIBRARIES} "${DEPS_DIR}/glfw/lib/${ARCH}/glfw3dll.lib") + set(PNG_LIBRARIES ${PNG_LIBRARIES} "${DEPS_DIR}/libpng/lib/${ARCH}/libpng16.lib") set(OPENAL_LIBRARY ${OPENAL_LIBRARY} "${DEPS_DIR}/openal/lib/${ARCH}/OpenAL32.lib") set(LIBSNDFILE_LIBRARY ${LIBSNDFILE_LIBRARY} "${DEPS_DIR}/libsndfile/lib/${ARCH}/libsndfile-1.lib") set(LUAJIT_LIBRARIES ${LUAJIT_LIBRARIES} "${DEPS_DIR}/luajit/lib/${ARCH}/lua51.lib") - set(PYTHON_LIBRARY ${PYTHON_LIBRARY} "${DEPS_DIR}/python/lib/${ARCH}/python27.lib") + set(PYTHON_LIBRARY ${PYTHON_LIBRARY} "${DEPS_DIR}/python/lib/${ARCH}/python27.lib") set(libserialport_LIBRARY ${LIBSERIALPORT_LIBRARY} "${DEPS_DIR}/libserialport/lib/${ARCH}/libserialport-0.lib") endif() @@ -236,10 +246,12 @@ find_package(PythonLibs 2 REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} ${PYTHON_LIBRARIES}) -find_package(PNG REQUIRED) -include_directories(${PNG_INCLUDE_DIRS} ${PNG_PNG_INCLUDE_DIR}) +if (NOT WIN32) + find_package(PkgConfig REQUIRED) + pkg_check_modules(PNG libpng16 REQUIRED) +endif() +include_directories(${PNG_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} ${PNG_LIBRARIES}) -target_link_libraries(${PROJECT_NAME} ${PNG_LIBRARY}) find_package(Threads REQUIRED) target_link_libraries(${PROJECT_NAME} Threads::Threads) @@ -259,9 +271,11 @@ find_package(LuaJIT REQUIRED) include_directories(${LUAJIT_INCLUDE_DIR}) target_link_libraries(${PROJECT_NAME} ${LUAJIT_LIBRARIES}) -find_package(libserialport REQUIRED) -include_directories(${libserialport_INCLUDE_DIR}) -target_link_libraries(${PROJECT_NAME} ${libserialport_LIBRARY}) +if (WITH_UART) + find_package(libserialport REQUIRED) + include_directories(${libserialport_INCLUDE_DIR}) + target_link_libraries(${PROJECT_NAME} ${libserialport_LIBRARY}) +endif() find_package(ASIO REQUIRED) target_link_libraries(${PROJECT_NAME} ASIO::ASIO) diff --git a/Classes.h b/Classes.h index 30810a44..e5f98ad4 100644 --- a/Classes.h +++ b/Classes.h @@ -40,6 +40,7 @@ class powergridsource_table; class instance_table; class vehicle_table; struct light_array; +struct dictionary_source; namespace scene { struct node_data; diff --git a/Driver.cpp b/Driver.cpp index 2c9038bf..0d5ef225 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1373,7 +1373,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( ( mvOccupied->Vel < v ) || ( v == 0.0 ) ) { // if we're going slower than the target velocity and there's enough room for safe stop, speed up - auto const brakingdistance = fBrakeDist * braking_distance_multiplier( v ); + auto const brakingdistance { 1.2 * fBrakeDist * braking_distance_multiplier( v ) }; if( brakingdistance > 0.0 ) { // maintain desired acc while we have enough room to brake safely, when close enough start paying attention // try to make a smooth transition instead of sharp change @@ -1638,7 +1638,7 @@ TController::TController(bool AI, TDynamicObject *NewControll, bool InitPsyche, if( WriteLogFlag ) { #ifdef _WIN32 CreateDirectory( "physicslog", NULL ); -#elif __linux__ +#elif __unix__ mkdir( "physicslog", 0644 ); #endif LogFile.open( std::string( "physicslog/" + VehicleName + ".dat" ), @@ -2125,6 +2125,11 @@ bool TController::CheckVehicles(TOrders user) && ( ( p->MoverParameters->Couplers[ end::rear ].CouplingFlag & ( coupling::control ) ) == 0 ) ) { // NOTE: don't set battery in the occupied vehicle, let the user/ai do it explicitly p->MoverParameters->BatterySwitch( true ); + // enable heating and converter in carriages with can be heated + if( p->MoverParameters->HeatingPower > 0 ) { + p->MoverParameters->HeatingAllow = true; + p->MoverParameters->ConverterSwitch( true, range_t::local ); + } } if (p->asDestination == "none") @@ -2356,6 +2361,7 @@ double TController::BrakeAccFactor() const double Factor = 1.0; if( ( fAccThreshold != 0.0 ) + && ( AccDesired < 0.0 ) && ( ( ActualProximityDist > fMinProximityDist ) || ( mvOccupied->Vel > VelDesired + fVelPlus ) ) ) { Factor += ( fBrakeReaction * ( /*mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition < 0.5 ? 1.5 : 1 ) ) * mvOccupied->Vel / ( std::max( 0.0, ActualProximityDist ) + 1 ) * ( ( AccDesired - AbsAccS_pub ) / fAccThreshold ); @@ -2525,9 +2531,9 @@ bool TController::PrepareEngine() } // sync virtual brake state with the 'real' one std::unordered_map const brakepositions { - { mvOccupied->Handle->GetPos( bh_RP ), gbh_RP }, - { mvOccupied->Handle->GetPos( bh_NP ), gbh_NP }, - { mvOccupied->Handle->GetPos( bh_FS ), gbh_FS } }; + { static_cast( mvOccupied->Handle->GetPos( bh_RP ) ), gbh_RP }, + { static_cast( mvOccupied->Handle->GetPos( bh_NP ) ), gbh_NP }, + { static_cast( mvOccupied->Handle->GetPos( bh_FS ) ), gbh_FS } }; auto const lookup { brakepositions.find( static_cast( mvOccupied->fBrakeCtrlPos ) ) }; if( lookup != brakepositions.end() ) { BrakeLevelSet( lookup->second ); // GBH @@ -3545,7 +3551,7 @@ void TController::Doors( bool const Open, int const Side ) { } if( AIControllFlag ) { - if( ( true == mvOccupied->Doors.has_autowarning ) + if( ( true == mvOccupied->Doors.has_warning ) && ( false == mvOccupied->DepartureSignal ) && ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) { mvOccupied->signal_departure( true ); // załącenie bzyczka @@ -4046,7 +4052,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N } if (NewCommand == "SetSignal") { - TSignals signal = (TSignals)std::round(NewValue1); + TSignals signal = (TSignals)std::lrint(NewValue1); mvOccupied->iLights[0] = 0; mvOccupied->iLights[1] = 0; @@ -4714,11 +4720,11 @@ TController::UpdateSituation(double dt) { // TODO: test if we can use the distances calculation from obey_train fMinProximityDist = std::min( 5 + iVehicles, 25 ); fMaxProximityDist = std::min( 10 + iVehicles, 50 ); -/* - if( IsHeavyCargoTrain ) { - fMaxProximityDist *= 1.5; + // HACK: modern vehicles might brake slower at low speeds, increase safety margin as crude counter + if( mvControlling->EIMCtrlType > 0 ) { + fMinProximityDist += 5.0; + fMaxProximityDist += 5.0; } -*/ fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania // margines prędkości powodujący załączenie napędu // były problemy z jazdą np. 3km/h podczas ładowania wagonów @@ -5198,7 +5204,11 @@ TController::UpdateSituation(double dt) { 20.0 ) ); // others if( vel > VelDesired + fVelPlus ) { // if going too fast force some prompt braking - AccPreferred = std::min( -0.65, AccPreferred ); + AccPreferred = std::min( + ( ( mvOccupied->CategoryFlag & 2 ) ? + -0.65 : // cars + -0.30 ), // others + AccPreferred ); } } @@ -5710,11 +5720,11 @@ TController::UpdateSituation(double dt) { || ( VelNext > vel - 40.0 ) ) ? fBrake_a0[ 0 ] * 0.8 : -fAccThreshold ) - / braking_distance_multiplier( VelNext ) ) { + / ( 1.2 * braking_distance_multiplier( VelNext ) ) ) { AccDesired = std::max( -0.06, AccDesired ); } } - else { + if( AccDesired < -0.1 ) { // i orientuj się szybciej, jeśli hamujesz ReactionTime = 0.25; } @@ -5813,11 +5823,11 @@ TController::UpdateSituation(double dt) { } } } - // yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej - // zwiekszanie predkosci + // yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej zwiekszanie predkosci // Ra 2F1H: jest konflikt histerezy pomiędzy nastawioną pozycją a uzyskiwanym // przyspieszeniem - utrzymanie pozycji powoduje przekroczenie przyspieszenia - if( ( AccDesired - AbsAccS > 0.01 ) ) { + if( ( AccDesired > -0.06 ) // don't add power if not asked for actual speed-up + && ( AccDesired - AbsAccS > 0.05 ) ) { // jeśli przyspieszenie pojazdu jest mniejsze niż żądane oraz... if( vel < ( VelDesired == 1.0 ? // work around for trains getting stuck on tracks with speed limit = 1 @@ -5909,7 +5919,8 @@ TController::UpdateSituation(double dt) { } } } - if ((AccDesired < fAccGravity - 0.05) && (AbsAccS < AccDesired - fBrake_a1[0]*0.51)) { + if ( ( AccDesired < fAccGravity - 0.05 ) + && ( ( AccDesired - fBrake_a1[0]*0.51 ) ) - AbsAccS > 0.05 ) { // jak hamuje, to nie tykaj kranu za często // yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2 if( OrderCurrentGet() != Disconnect ) { diff --git a/Driver.h b/Driver.h index 1e4e5aed..6eb08318 100644 --- a/Driver.h +++ b/Driver.h @@ -415,10 +415,10 @@ private: // timetable // methods public: - const std::string &TrainName() const; -private: - std::string Relation() const; + const std::string &TrainName() const; Mtable::TTrainParameters const * TrainTimetable() const; +private: + std::string Relation() const; int StationIndex() const; int StationCount() const; bool IsStop() const; diff --git a/DynObj.cpp b/DynObj.cpp index 9ada45b1..a1e6d07f 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -162,6 +162,39 @@ void TAnim::Parovoz(){ // animowanie tłoka i rozrządu parowozu }; */ + + +void TDynamicObject::destination_data::deserialize( cParser &Input ) { + + while( true == deserialize_mapping( Input ) ) { + ; // all work done by while() + } +} + +bool TDynamicObject::destination_data::deserialize_mapping( cParser &Input ) { + // token can be a key or block end + auto const key { Input.getToken( true, "\n\r\t ,;[]" ) }; + + if( ( true == key.empty() ) || ( key == "}" ) ) { return false; } + + if( key == "{" ) { + script = Input.getToken(); + } + else if( key == "update:" ) { + auto const value { Input.getToken() }; + // TODO: implement + } + else if( key == "instance:" ) { + instancing = Input.getToken(); + } + else if( key == "parameters:" ) { + parameters = Input.getToken(); + } + + return true; +} + + //--------------------------------------------------------------------------- TDynamicObject * TDynamicObject::FirstFind(int &coupler_nr, int cf) { // szukanie skrajnego połączonego pojazdu w pociagu @@ -918,6 +951,8 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) btEndSignalsTab2.Turn( true ); btnOn = true; } + // destination signs + update_destinations(); // else btEndSignalsTab2.TurnOff(); // McZapkie-181002: krecenie wahaczem (korzysta z kata obrotu silnika) if (iAnimType[ANIM_LEVERS]) @@ -1535,20 +1570,12 @@ void TDynamicObject::place_on_track(TTrack *Track, double fDist, bool Reversed) // Ra: pojazdy wstawiane są na tor początkowy, a potem przesuwane case 2: // ustawianie osi na torze Axle0.Init(Track, this, iDirection ? 1 : -1); + Axle0.Reset(); Axle0.Move((iDirection ? fDist : -fDist) + fAxleDistHalf, false); Axle1.Init(Track, this, iDirection ? 1 : -1); + Axle1.Reset(); Axle1.Move((iDirection ? fDist : -fDist) - fAxleDistHalf, false); // false, żeby nie generować eventów break; - case 4: - Axle0.Init(Track, this, iDirection ? 1 : -1); - Axle0.Move((iDirection ? fDist : -fDist) + (fAxleDistHalf + MoverParameters->ADist * 0.5), false); - Axle1.Init(Track, this, iDirection ? 1 : -1); - Axle1.Move((iDirection ? fDist : -fDist) - (fAxleDistHalf + MoverParameters->ADist * 0.5), false); - // Axle2.Init(Track,this,iDirection?1:-1); - // Axle2.Move((iDirection?fDist:-fDist)-(fAxleDistHalf-MoverParameters->ADist*0.5),false); - // Axle3.Init(Track,this,iDirection?1:-1); - // Axle3.Move((iDirection?fDist:-fDist)+(fAxleDistHalf-MoverParameters->ADist*0.5),false); - break; } // potrzebne do wyliczenia aktualnej pozycji; nie może być zero, bo nie przeliczy pozycji // teraz jeszcze trzeba przypisać pojazdy do nowego toru, bo przesuwanie początkowe osi nie @@ -1982,6 +2009,10 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" init_sections( mdLowPolyInt, nameprefix ); } } + // destination sign + if( mdModel ) { + init_destination( mdModel ); + } // 'external_load' is an optional special section in the main model, pointing to submodel of external load if( mdModel ) { init_sections( mdModel, "external_load" ); @@ -2106,6 +2137,16 @@ TDynamicObject::init_sections( TModel3d const *Model, std::string const &Namepre return sectioncount; } +bool +TDynamicObject::init_destination( TModel3d *Model ) { + + if( Model->GetSMRoot() == nullptr ) { return false; } + + std::tie( DestinationSign.sign, DestinationSign.has_light ) = Model->GetSMRoot()->find_replacable4(); + + return DestinationSign.sign != nullptr; +} + void TDynamicObject::create_controller( std::string const Type, bool const Trainset ) { @@ -2579,6 +2620,27 @@ na sprzęgach, opóźnienie działania hamulca itp. Oczywiście musi mieć to pe histerezę czasową, aby te tryby pracy nie przełączały się zbyt szybko. */ +void TDynamicObject::update_destinations() { + + if( DestinationSign.sign == nullptr ) { return; } + + DestinationSign.sign->fLight = ( + ( ( DestinationSign.has_light ) && ( MoverParameters->Battery ) ) ? + 2.0 : + -1.0 ); + + // jak są 4 tekstury wymienne, to nie zmieniać rozkładem + if( std::abs( m_materialdata.multi_textures ) >= 4 ) { return; } + // TODO: dedicated setting to discern electronic signs, instead of fallback on light presence + m_materialdata.replacable_skins[ 4 ] = ( + ( ( DestinationSign.destination != null_handle ) + && ( ( false == DestinationSign.has_light ) // physical destination signs remain up until manually changed + || ( ( true == MoverParameters->Battery ) // lcd signs are off without power + && ( ctOwner != nullptr ) ) ) ) ? // lcd signs are off for carriages without engine, potentially left on a siding + DestinationSign.destination : + DestinationSign.destination_off ); +} + bool TDynamicObject::Update(double dt, double dt1) { if (dt1 == 0.0) @@ -4073,6 +4135,10 @@ void TDynamicObject::RenderSounds() { } if( volume > 0.05 ) { rscurve + .pitch( + true == rscurve.is_combined() ? + MoverParameters->Vel * 0.01f : + rscurve.m_frequencyoffset + rscurve.m_frequencyfactor * 1.f ) .gain( 2.5 * volume ) .play( sound_flags::exclusive | sound_flags::looping ); } @@ -4298,7 +4364,8 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } // potentially set blank destination texture - DestinationSet( {}, {} ); + DestinationSign.destination_off = DestinationFind( "nowhere" ); +// DestinationSet( {}, {} ); if( GfxRenderer.Material( m_materialdata.replacable_skins[ 1 ] ).get_or_guess_opacity() == 0.0f ) { // tekstura -1 z kanałem alfa - nie renderować w cyklu nieprzezroczystych @@ -5595,6 +5662,15 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co parser >> JointCabs; } + else if( token == "pydestinationsign:" ) { + DestinationSign.deserialize( parser ); + // supply vehicle folder as script path if none is provided + if( ( false == DestinationSign.script.empty() ) + && ( substr_path( DestinationSign.script ).empty() ) ) { + DestinationSign.script = asBaseDir + DestinationSign.script; + } + } + } while( token != "" ); } // internaldata: @@ -6142,45 +6218,92 @@ int TDynamicObject::RouteWish(TTrack *tr) return Mechanik ? Mechanik->CrossRoute(tr) : 0; // wg AI albo prosto }; -void TDynamicObject::DestinationSet(std::string to, std::string numer) -{ // ustawienie stacji docelowej oraz wymiennej tekstury 4, jeśli istnieje plik +void TDynamicObject::DestinationSet(std::string to, std::string numer) { + // ustawienie stacji docelowej oraz wymiennej tekstury 4, jeśli istnieje plik // w zasadzie, to każdy wagon mógłby mieć inną stację docelową // zwłaszcza w towarowych, pod kątem zautomatyzowania maewrów albo pracy górki // ale to jeszcze potrwa, zanim będzie możliwe, na razie można wpisać stację z // rozkładu - if( std::abs( m_materialdata.multi_textures ) >= 4 ) { - // jak są 4 tekstury wymienne, to nie zmieniać rozkładem - return; - } - numer = Bezogonkow(numer); + asDestination = to; - to = Bezogonkow(to); // do szukania pliku obcinamy ogonki - if( true == to.empty() ) { - to = "nowhere"; + + if( std::abs( m_materialdata.multi_textures ) >= 4 ) { return; } // jak są 4 tekstury wymienne, to nie zmieniać rozkładem + if( DestinationSign.sign == nullptr ) { return; } // no sign submodel, no problem + + // now see if we can find any version of the destination texture + std::vector const destinations = { + numer, // try dedicated timetable sign first... + to }; // ...then generic destination sign + + for( auto const &destination : destinations ) { + + DestinationSign.destination = DestinationFind( destination ); + if( DestinationSign.destination != null_handle ) { + // got what we wanted, we're done here + return; + } } + // if we didn't get static texture we might be able to make one + if( DestinationSign.script.empty() ) { return; } // no script so no way to make the texture + if( numer == "none" ) { return; } // blank or incomplete/malformed timetable, don't bother + + std::string signrequest { + "make:" + + DestinationSign.script + "?" + // timetable include + + "$timetable=" + ( + ctOwner == nullptr ? + MoverParameters->Name : // leading vehicle, can point to it directly + ctOwner->Vehicle()->MoverParameters->Name ) + "&" // owned vehicle, safer to point to owner as carriages can have identical names + // basic instancing string + // NOTE: underscore doesn't have any magic meaning for the time being, it's just less likely to conflict with regular dictionary keys + + "_id1=" + ( + ctOwner != nullptr ? ctOwner->TrainName() : + Mechanik != nullptr ? Mechanik->TrainName() : + "none" ) }; // shouldn't get here but, eh + // TBD, TODO: replace instancing with support for variables in extra parameters string? + if( false == DestinationSign.instancing.empty() ) { + signrequest += + "&_id2=" + ( + DestinationSign.instancing == "name" ? MoverParameters->Name : + DestinationSign.instancing == "type" ? MoverParameters->TypeName : + "none" ); + } + // optionl extra parameters + if( false == DestinationSign.parameters.empty() ) { + signrequest += "&" + DestinationSign.parameters; + } + + DestinationSign.destination = GfxRenderer.Fetch_Material( signrequest ); +} + +material_handle TDynamicObject::DestinationFind( std::string Destination ) { + + if( Destination.empty() ) { return null_handle; } + + Destination = Bezogonkow( Destination ); // do szukania pliku obcinamy ogonki // destination textures are kept in the vehicle's directory so we point the current texture path there auto const currenttexturepath { Global.asCurrentTexturePath }; Global.asCurrentTexturePath = asBaseDir; // now see if we can find any version of the texture - std::vector destinations = { - numer + '@' + MoverParameters->TypeName, - numer, - to + '@' + MoverParameters->TypeName, - to, - "nowhere" + '@' + MoverParameters->TypeName, - "nowhere" }; + std::vector const destinations { + Destination + '@' + MoverParameters->TypeName, + Destination }; + + auto destinationhandle { null_handle }; for( auto const &destination : destinations ) { - auto material = TextureTest( ToLower( destination ) ); if( false == material.empty() ) { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Material( material ); + destinationhandle = GfxRenderer.Fetch_Material( material ); break; } } // whether we got anything, restore previous texture path Global.asCurrentTexturePath = currenttexturepath; -}; + + return destinationhandle; +} void TDynamicObject::OverheadTrack(float o) { // ewentualne wymuszanie jazdy diff --git a/DynObj.h b/DynObj.h index 6e0a5b3b..6208f77b 100644 --- a/DynObj.h +++ b/DynObj.h @@ -204,6 +204,20 @@ public: TModel3d *mdLowPolyInt; // ABu 010305: wnetrze lowpoly std::array LowPolyIntCabs {}; // pointers to low fidelity version of individual driver cabs bool JointCabs{ false }; // flag for vehicles with multiple virtual 'cabs' sharing location and 3d model(s) + struct destination_data { + TSubModel *sign { nullptr }; // submodel mapped with replacable texture -4 + bool has_light { false }; // the submodel was originally configured with self-illumination attribute + material_handle destination { null_handle }; // most recently assigned non-blank destination texture + material_handle destination_off { null_handle }; // blank destination sign + std::string script; // potential python script used to generate texture data + int update_rate { 0 }; // -1: per stop, 0: none, >0: fps // TBD, TODO: implement? + std::string instancing; // potential method to generate more than one texture per timetable + std::string parameters; // potential extra parameters supplied by mmd file + // methods + void deserialize( cParser &Input ); + private: + bool deserialize_mapping( cParser &Input ); + } DestinationSign; float fShade; // zacienienie: 0:normalnie, -1:w ciemności, +1:dodatkowe światło (brak koloru?) float LoadOffset { 0.f }; std::unordered_map LoadModelOverrides; // potential overrides of default load visualization models @@ -520,6 +534,7 @@ private: TTrack *Track, double fDist, std::string DriverType, double fVel, std::string TrainName, float Load, std::string LoadType, bool Reversed, std::string); int init_sections( TModel3d const *Model, std::string const &Nameprefix ); + bool init_destination( TModel3d *Model ); void create_controller( std::string const Type, bool const Trainset ); void AttachPrev(TDynamicObject *Object, int iType = 1); bool UpdateForce(double dt); @@ -534,6 +549,7 @@ private: void update_load_visibility(); void update_load_offset(); void shuffle_load_sections(); + void update_destinations(); bool Update(double dt, double dt1); bool FastUpdate(double dt); void Move(double fDistance); @@ -636,6 +652,7 @@ private: // zapytanie do AI, po którym segmencie skrzyżowania jechać int RouteWish(TTrack *tr); void DestinationSet(std::string to, std::string numer); + material_handle DestinationFind( std::string Destination ); void OverheadTrack(float o); glm::dvec3 get_future_movement() const; void move_set(double distance); diff --git a/EU07.cpp b/EU07.cpp index 316d3772..f234d931 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -34,8 +34,10 @@ int main( int argc, char *argv[] ) auto result { Application.init( argc, argv ) }; if( result == 0 ) { result = Application.run(); + Application.exit(); } - Application.exit(); + std::_Exit(0); // skip destructors, there are ordering errors which causes segfaults + return result; } catch( std::bad_alloc const &Error ) { diff --git a/Globals.cpp b/Globals.cpp index 38d27f4b..b17f7444 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -650,6 +650,7 @@ global_settings::ConfigParse(cParser &Parser) { Parser.getTokens(1); Parser >> Global.screenshot_dir; } +#ifdef WITH_UART else if( token == "uart" ) { uart_conf.enable = true; Parser.getTokens( 3, false ); @@ -688,6 +689,7 @@ global_settings::ConfigParse(cParser &Parser) { Parser.getTokens( 1 ); Parser >> uart_conf.debug; } +#endif else if (token == "loadinglog") { Parser.getTokens( 1 ); Parser >> loading_log; diff --git a/Globals.h b/Globals.h index 167e5c55..d2985ca6 100644 --- a/Globals.h +++ b/Globals.h @@ -14,10 +14,12 @@ http://mozilla.org/MPL/2.0/. #include "dumb3d.h" #include "Float3d.h" #include "light.h" -#include "uart.h" #include "utilities.h" #include "motiontelemetry.h" #include "version.h" +#ifdef WITH_UART +#include "uart.h" +#endif struct global_settings { // members @@ -169,7 +171,9 @@ struct global_settings { 0, 0, 0, 0, 0, 0, 0 }; int iCalibrateOutDebugInfo { -1 }; // numer wyjścia kalibrowanego dla którego wyświetlać informacje podczas kalibracji int iPoKeysPWM[ 7 ] = { 0, 1, 2, 3, 4, 5, 6 }; // numery wejść dla PWM +#ifdef WITH_UART uart_input::conf_t uart_conf; +#endif // multiplayer int iMultiplayer{ 0 }; // blokada działania niektórych eventów na rzecz kominikacji // other diff --git a/Logs.cpp b/Logs.cpp index 3311fa4b..a4a87e71 100644 --- a/Logs.cpp +++ b/Logs.cpp @@ -27,7 +27,7 @@ std::deque log_scrollback; std::string filename_date() { ::SYSTEMTIME st; -#ifdef __linux__ +#ifdef __unix__ timespec ts; clock_gettime(CLOCK_REALTIME, &ts); tm *tms = localtime(&ts.tv_sec); diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index f76bd2da..060630d4 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1369,6 +1369,7 @@ public: std::string StLinSwitchType; bool Heating = false; //ogrzewanie 'Winger 020304 + bool HeatingAllow { false }; // heating switch // TODO: wrap heating in a basic device int DoubleTr = 1; //trakcja ukrotniona - przedni pojazd 'Winger 160304 bool PhysicActivation = true; @@ -1514,6 +1515,7 @@ public: /*-funkcje typowe dla lokomotywy elektrycznej*/ void ConverterCheck( double const Timestep ); // przetwornica + void HeatingCheck( double const Timestep ); void WaterPumpCheck( double const Timestep ); void WaterHeaterCheck( double const Timestep ); void FuelPumpCheck( double const Timestep ); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 75f66612..3aab7fea 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1186,11 +1186,13 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap for( int side = 0; side < 2; ++side ) { // przekazywanie napiec - auto const oppositeside = ( side == end::front ? end::rear : end::front ); + auto const oppositeside { ( side == end::front ? end::rear : end::front ) }; + auto const liveconnection{ + ( Couplers[ side ].CouplingFlag & ctrain_power ) + || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) + && ( Couplers[ side ].Connected->Heating ) ) }; - if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { + if( liveconnection ) { auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ Couplers[ side ].ConnectedNr ]; Couplers[ oppositeside ].power_high.voltage = std::max( @@ -1214,8 +1216,8 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap Couplers[ side ].power_high.local = false; // power, if any, will be from external source if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { + || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) + && ( Couplers[ side ].Connected->Heating ) ) ) { auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ ( Couplers[ side ].ConnectedNr == end::front ? @@ -1238,8 +1240,8 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap Couplers[ side ].power_high.local = true; // power is coming from local pantographs if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ side ].CouplingFlag & ctrain_heating ) ) ) { + || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) + && ( Couplers[ side ].Connected->Heating ) ) ) { auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ ( Couplers[ side ].ConnectedNr == end::front ? @@ -1520,6 +1522,8 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { // w rozrządczym nie (jest błąd w FIZ!) - Ra 2014-07: teraz we wszystkich UpdatePantVolume( Deltatime ); // Ra 2014-07: obsługa zbiornika rozrządu oraz pantografów } + // heating + HeatingCheck( Deltatime ); UpdateBrakePressure(Deltatime); UpdatePipePressure(Deltatime); @@ -1573,7 +1577,8 @@ void TMoverParameters::ConverterCheck( double const Timestep ) { if( ( ConverterAllow ) && ( ConverterAllowLocal ) && ( false == PantPressLockActive ) - && ( Mains ) ) { + && ( ( Mains ) + || ( GetTrainsetVoltage() > 0 ) ) ) { // delay timer can be optionally configured, and is set anew whenever converter goes off if( ConverterStartDelayTimer <= 0.0 ) { ConverterFlag = true; @@ -1588,6 +1593,17 @@ void TMoverParameters::ConverterCheck( double const Timestep ) { } }; +// heating system status check +void TMoverParameters::HeatingCheck( double const Timestep ) { + + Heating = ( + ( true == HeatingAllow ) + // powered vehicles are generally required to activate their power source to provide heating + // passive vehicles get a pass in this regard + && ( ( Power < 0.1 ) + || ( true == Mains ) ) ); +} + // water pump status check void TMoverParameters::WaterPumpCheck( double const Timestep ) { // NOTE: breaker override with start type is sm42 specific hack, replace with ability to define the presence of the breaker @@ -7271,14 +7287,14 @@ double TMoverParameters::GetTrainsetVoltage(void) return std::max( ( ( ( Couplers[end::front].Connected ) && ( ( Couplers[ end::front ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ end::front ].CouplingFlag & ctrain_heating ) ) ) ) ? + || ( ( Couplers[ end::front ].CouplingFlag & ctrain_heating ) + && ( Couplers[ end::front ].Connected->Heating ) ) ) ) ? Couplers[end::front].Connected->Couplers[ Couplers[end::front].ConnectedNr ].power_high.voltage : 0.0 ), ( ( ( Couplers[end::rear].Connected ) && ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power ) - || ( ( Heating ) - && ( Couplers[ end::rear ].CouplingFlag & ctrain_heating ) ) ) ) ? + || ( ( Couplers[ end::rear ].CouplingFlag & ctrain_heating ) + && ( Couplers[ end::rear ].Connected->Heating ) ) ) ) ? Couplers[ end::rear ].Connected->Couplers[ Couplers[ end::rear ].ConnectedNr ].power_high.voltage : 0.0 ) ); } diff --git a/Model3d.cpp b/Model3d.cpp index 8521102e..08290255 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -826,6 +826,27 @@ int TSubModel::count_children() { 1 + Child->count_siblings() ); } +// locates submodel mapped with replacable -4 +std::tuple +TSubModel::find_replacable4() { + + if( m_material == -4 ) { + return std::make_tuple( this, ( fLight != -1.0 ) ); + } + + if( Next != nullptr ) { + auto lookup { Next->find_replacable4() }; + if( std::get( lookup ) != nullptr ) { return lookup; } + } + + if( Child != nullptr ) { + auto lookup { Child->find_replacable4() }; + if( std::get( lookup ) != nullptr ) { return lookup; } + } + + return std::make_tuple( nullptr, false ); +} + uint32_t TSubModel::FlagsCheck() { // analiza koniecznych zmian pomiędzy submodelami // samo pomijanie glBindTexture() nie poprawi wydajności diff --git a/Model3d.h b/Model3d.h index ee248fea..132af42a 100644 --- a/Model3d.h +++ b/Model3d.h @@ -165,6 +165,8 @@ public: TSubModel * ChildGet() { return Child; }; int count_siblings(); int count_children(); + // locates submodel mapped with replacable -4 + std::tuple find_replacable4(); int TriangleAdd(TModel3d *m, material_handle tex, int tri); void SetRotate(float3 vNewRotateAxis, float fNewAngle); void SetRotateXYZ( Math3D::vector3 vNewAngles); diff --git a/PyInt.cpp b/PyInt.cpp index 00b743d5..6bfe0abe 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -10,10 +10,10 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "PyInt.h" -#include "Globals.h" +#include "dictionary.h" #include "application.h" -#include "renderer.h" #include "Logs.h" +#include "Globals.h" #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wwrite-strings" @@ -141,6 +141,11 @@ auto python_taskqueue::init() -> bool { Py_SetPythonHome("linuxpython64"); else Py_SetPythonHome("linuxpython"); +#elif __APPLE__ + if (sizeof(void*) == 8) + Py_SetPythonHome("macpython64"); + else + Py_SetPythonHome("macpython"); #endif Py_Initialize(); PyEval_InitThreads(); @@ -209,7 +214,8 @@ void python_taskqueue::exit() { m_condition.notify_all(); // let them free up their shit before we proceed for( auto &worker : m_workers ) { - worker.join(); + if (worker.joinable()) + worker.join(); } // get rid of the leftover tasks // with the workers dead we don't have to worry about concurrent access anymore diff --git a/PyInt.h b/PyInt.h index c3b2ebb3..d3e6bc29 100644 --- a/PyInt.h +++ b/PyInt.h @@ -50,24 +50,6 @@ http://mozilla.org/MPL/2.0/. #define PyGetBool(param) param ? Py_True : Py_False #define PyGetString(param) PyString_FromString(param) -// collection of keyword-value pairs -// NOTE: since our python dictionary operates on a few types, most of the class was hardcoded for simplicity -struct dictionary_source { -// types - template - using keyvaluepair_sequence = std::vector>; -// members - keyvaluepair_sequence floats; - keyvaluepair_sequence integers; - keyvaluepair_sequence bools; - keyvaluepair_sequence strings; -// methods - inline void insert( std::string const &Key, double const Value ) { floats.emplace_back( Key, Value ); } - inline void insert( std::string const &Key, int const Value ) { integers.emplace_back( Key, Value ); } - inline void insert( std::string const &Key, bool const Value ) { bools.emplace_back( Key, Value ); } - inline void insert( std::string const &Key, std::string const Value ) { strings.emplace_back( Key, Value ); } -}; - // python rendertarget struct python_rt { std::mutex mutex; diff --git a/Texture.cpp b/Texture.cpp index 9dd56b3f..254eb2c4 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -17,14 +17,16 @@ http://mozilla.org/MPL/2.0/. #include "Texture.h" #include "application.h" -#include "utilities.h" +#include "dictionary.h" #include "Globals.h" #include "Logs.h" +#include "utilities.h" #include "sn_utils.h" #include "utilities.h" #include "flip-s3tc.h" #include + #define EU07_DEFERRED_TEXTURE_UPLOAD texture_manager::texture_manager() { @@ -266,17 +268,9 @@ void opengl_texture::make_request() { auto const components { Split( name, '?' ) }; - auto const query { Split( components.back(), '&' ) }; - auto *dictionary { new dictionary_source }; - if( dictionary != nullptr ) { - for( auto const &querypair : query ) { - auto const valuepos { querypair.find( '=' ) }; - dictionary->insert( - ToLower( querypair.substr( 0, valuepos ) ), - querypair.substr( valuepos + 1 ) ); - } - } + auto *dictionary { new dictionary_source( components.back() ) }; + if( dictionary == nullptr ) { return; } auto rt = std::make_shared(); rt->shared_tex = id; diff --git a/Timer.cpp b/Timer.cpp index 278a879a..02fc558e 100644 --- a/Timer.cpp +++ b/Timer.cpp @@ -61,7 +61,7 @@ void UpdateTimers(bool pause) #ifdef _WIN32 QueryPerformanceFrequency((LARGE_INTEGER *)&fr); QueryPerformanceCounter((LARGE_INTEGER *)&count); -#elif __linux__ +#elif __unix__ timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); count = (uint64_t)ts.tv_sec * 1000000000 + (uint64_t)ts.tv_nsec; @@ -85,7 +85,7 @@ void UpdateTimers(bool pause) oldCount = count; // Keep track of the time lapse and frame count -#if __linux__ +#if __unix__ double fTime = (double)(count / 1000000000); #elif _WIN32_WINNT >= _WIN32_WINNT_VISTA double fTime = ::GetTickCount64() * 0.001f; // Get current time in seconds diff --git a/Train.cpp b/Train.cpp index bd82a6f3..2fe17ef3 100644 --- a/Train.cpp +++ b/Train.cpp @@ -30,6 +30,7 @@ http://mozilla.org/MPL/2.0/. #include "Console.h" #include "application.h" #include "renderer.h" +#include "dictionary.h" /* namespace input { @@ -552,31 +553,8 @@ dictionary_source *TTrain::GetTrainState() { dict->insert( "velnext", driver->VelNext ); dict->insert( "actualproximitydist", driver->ActualProximityDist ); // train data - auto const *timetable{ driver->TrainTimetable() }; - - dict->insert( "trainnumber", driver->TrainName() ); - dict->insert( "train_brakingmassratio", timetable->BrakeRatio ); - dict->insert( "train_enginetype", timetable->LocSeries ); - dict->insert( "train_engineload", timetable->LocLoad ); - - dict->insert( "train_stationindex", driver->iStationStart ); - auto const stationcount { driver->StationCount() }; - dict->insert( "train_stationcount", stationcount ); - if( stationcount > 0 ) { - // timetable stations data, if there's any - for( auto stationidx = 1; stationidx <= stationcount; ++stationidx ) { - auto const stationlabel { "train_station" + std::to_string( stationidx ) + "_" }; - auto const &timetableline { timetable->TimeTable[ stationidx ] }; - dict->insert( ( stationlabel + "name" ), Bezogonkow( timetableline.StationName ) ); - dict->insert( ( stationlabel + "fclt" ), Bezogonkow( timetableline.StationWare ) ); - dict->insert( ( stationlabel + "lctn" ), timetableline.km ); - dict->insert( ( stationlabel + "vmax" ), timetableline.vmax ); - dict->insert( ( stationlabel + "ah" ), timetableline.Ah ); - dict->insert( ( stationlabel + "am" ), timetableline.Am ); - dict->insert( ( stationlabel + "dh" ), timetableline.Dh ); - dict->insert( ( stationlabel + "dm" ), timetableline.Dm ); - } - } + driver->TrainTimetable()->serialize( dict ); + dict->insert( "train_stationstart", driver->iStationStart ); dict->insert( "train_atpassengerstop", driver->IsAtPassengerStop ); // world state data dict->insert( "scenario", Global.SceneryFile ); @@ -4148,7 +4126,7 @@ void TTrain::OnCommand_heatingtoggle( TTrain *Train, command_data const &Command if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( false == Train->mvControlled->Heating ) { + if( false == Train->mvControlled->HeatingAllow ) { // turn on OnCommand_heatingenable( Train, Command ); } @@ -4162,24 +4140,20 @@ void TTrain::OnCommand_heatingtoggle( TTrain *Train, command_data const &Command void TTrain::OnCommand_heatingenable( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { + + Train->mvControlled->HeatingAllow = true; // visual feedback Train->ggTrainHeatingButton.UpdateValue( 1.0, Train->dsbSwitch ); - - if( true == Train->mvControlled->Heating ) { return; } // already enabled - - Train->mvControlled->Heating = true; } } void TTrain::OnCommand_heatingdisable( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { + + Train->mvControlled->HeatingAllow = false; // visual feedback Train->ggTrainHeatingButton.UpdateValue( 0.0, Train->dsbSwitch ); - - if( false == Train->mvControlled->Heating ) { return; } // already disabled - - Train->mvControlled->Heating = false; } } @@ -6028,7 +6002,6 @@ bool TTrain::Update( double const Deltatime ) //--------- // hunter-080812: poprawka na ogrzewanie w elektrykach - usuniete uzaleznienie od przetwornicy if( ( mvControlled->Heating == true ) - && ( mvControlled->Mains == true ) && ( mvControlled->ConvOvldFlag == false ) ) btLampkaOgrzewanieSkladu.Turn( true ); else diff --git a/TrkFoll.cpp b/TrkFoll.cpp index ece011e5..20a5d677 100644 --- a/TrkFoll.cpp +++ b/TrkFoll.cpp @@ -31,8 +31,6 @@ bool TTrackFollower::Init(TTrack *pTrack, TDynamicObject *NewOwner, double fDir) fDirection = fDir; Owner = NewOwner; SetCurrentTrack(pTrack, 0); - fCurrentDistance = 0.0; - fDirection = 1.0; iEventFlag = 3; // na torze startowym również wykonać eventy 1/2 iEventallFlag = 3; if ((pCurrentSegment)) // && (pCurrentSegment->GetLength()ComputeLength(p1,cp1,cp2,p2); }; // inline double GetRadius(double L, double d); //McZapkie-150503 bool Init(TTrack *pTrack, TDynamicObject *NewOwner, double fDir); + void Reset(); void Render(float fNr); // members double fOffsetH = 0.0; // Ra: odległość środka osi od osi toru (dla samochodów) - użyć do wężykowania diff --git a/application.cpp b/application.cpp index 4a5437e8..052dd663 100644 --- a/application.cpp +++ b/application.cpp @@ -24,6 +24,7 @@ http://mozilla.org/MPL/2.0/. #include "translation.h" #include "Train.h" #include "Timer.h" +#include "dictionary.h" #pragma comment (lib, "glu32.lib") #pragma comment (lib, "dsound.lib") @@ -32,7 +33,7 @@ http://mozilla.org/MPL/2.0/. #pragma comment (lib, "dbghelp.lib") #pragma comment (lib, "version.lib") -#ifdef __linux__ +#ifdef __unix__ #include #include #endif @@ -560,7 +561,7 @@ eu07_application::init_files() { DeleteFile( "log.txt" ); DeleteFile( "errors.txt" ); CreateDirectory( "logs", NULL ); -#elif __linux__ +#elif __unix__ unlink("log.txt"); unlink("errors.txt"); mkdir("logs", 0755); diff --git a/audio.h b/audio.h index f8c2fe9c..d84d54a1 100644 --- a/audio.h +++ b/audio.h @@ -9,8 +9,13 @@ http://mozilla.org/MPL/2.0/. #pragma once +#ifdef __APPLE__ +#include +#include +#else #include #include +#endif namespace audio { diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c4c624ec..50ec575f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -26,6 +26,26 @@ jobs: artifactName: binaries displayName: 'Publish binaries' + - job: macos1013 + pool: + vmImage: 'macOS-10.13' + displayName: 'MacOS 10.13' + steps: + - script: | + HOMEBREW_NO_AUTO_UPDATE=1 brew install glew glfw python2 libpng glm luajit libserialport libsndfile asio pkg-config + displayName: 'Install dependencies' + - script: | + mkdir build + cd build + cmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_UART=OFF + cmake --build . + displayName: 'Build' + - task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: 'build/bin' + artifactName: binaries + displayName: 'Publish binaries' + - job: windows_x64 pool: vmImage: 'vs2017-win2016' diff --git a/dictionary.cpp b/dictionary.cpp new file mode 100644 index 00000000..f4e987ec --- /dev/null +++ b/dictionary.cpp @@ -0,0 +1,55 @@ +/* +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 "dictionary.h" + +#include "simulation.h" +#include "utilities.h" +#include "DynObj.h" +#include "Driver.h" +#include "mtable.h" + +dictionary_source::dictionary_source( std::string const &Input ) { + + auto const keyvaluepairs { Split( Input, '&' ) }; + + for( auto const &keyvaluepair : keyvaluepairs ) { + + auto const valuepos { keyvaluepair.find( '=' ) }; + if( keyvaluepair[ 0 ] != '$' ) { + // regular key, value pairs + insert( + ToLower( keyvaluepair.substr( 0, valuepos ) ), + keyvaluepair.substr( valuepos + 1 ) ); + } + else { + // special case, $key indicates request to include certain dataset clarified by value + auto const key { ToLower( keyvaluepair.substr( 0, valuepos ) ) }; + auto const value { keyvaluepair.substr( valuepos + 1 ) }; + + if( key == "$timetable" ) { + // timetable pulled from (preferably) the owner/direct controller of specified vehicle + auto const *vehicle { simulation::Vehicles.find( value ) }; + auto const *controller { ( + vehicle == nullptr ? nullptr : + vehicle->ctOwner == nullptr ? vehicle->Mechanik : + vehicle->ctOwner ) }; + auto const *timetable { ( + controller != nullptr ? + controller->TrainTimetable() : + nullptr ) }; + + if( timetable != nullptr ) { + timetable->serialize( this ); + } + } + } + } +} diff --git a/dictionary.h b/dictionary.h new file mode 100644 index 00000000..98828856 --- /dev/null +++ b/dictionary.h @@ -0,0 +1,31 @@ +/* +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 + +// collection of keyword-value pairs +// NOTE: since our python dictionary operates on a few types, most of the class was hardcoded for simplicity +struct dictionary_source { +// types + template + using keyvaluepair_sequence = std::vector>; +// members + keyvaluepair_sequence floats; + keyvaluepair_sequence integers; + keyvaluepair_sequence bools; + keyvaluepair_sequence strings; +// constructors + dictionary_source() = default; + dictionary_source( std::string const &Input ); +// methods + inline void insert( std::string const &Key, double const Value ) { floats.emplace_back( Key, Value ); } + inline void insert( std::string const &Key, int const Value ) { integers.emplace_back( Key, Value ); } + inline void insert( std::string const &Key, bool const Value ) { bools.emplace_back( Key, Value ); } + inline void insert( std::string const &Key, std::string const Value ) { strings.emplace_back( Key, Value ); } +}; diff --git a/drivermode.cpp b/drivermode.cpp index 47017431..02ad84f3 100644 --- a/drivermode.cpp +++ b/drivermode.cpp @@ -45,9 +45,11 @@ driver_mode::drivermode_input::poll() { if( true == Global.InputGamepad ) { gamepad.poll(); } +#ifdef WITH_UART if( uart != nullptr ) { uart->poll(); } +#endif /* // TBD, TODO: wrap current command in object, include other input sources? input::command = ( @@ -67,10 +69,12 @@ driver_mode::drivermode_input::init() { if( true == Global.InputGamepad ) { gamepad.init(); } +#ifdef WITH_UART if( true == Global.uart_conf.enable ) { uart = std::make_unique(); uart->init(); } +#endif if (Global.motiontelemetry_conf.enable) telemetry = std::make_unique(); diff --git a/drivermode.h b/drivermode.h index ec1756e6..86044961 100644 --- a/drivermode.h +++ b/drivermode.h @@ -14,10 +14,12 @@ http://mozilla.org/MPL/2.0/. #include "driverkeyboardinput.h" #include "drivermouseinput.h" #include "gamepadinput.h" -#include "uart.h" #include "Console.h" #include "Camera.h" #include "Classes.h" +#ifdef WITH_UART +#include "uart.h" +#endif class driver_mode : public application_mode { @@ -77,7 +79,9 @@ private: #ifdef _WIN32 Console console; #endif +#ifdef WITH_UART std::unique_ptr uart; +#endif std::unique_ptr telemetry; bool init(); diff --git a/mtable.cpp b/mtable.cpp index c1608f82..db6a0c35 100644 --- a/mtable.cpp +++ b/mtable.cpp @@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/. #include "mtable.h" #include "Globals.h" #include "simulationtime.h" +#include "dictionary.h" #include "utilities.h" double TTrainParameters::CheckTrainLatency() @@ -524,3 +525,29 @@ bool TTrainParameters::DirectionChange() return true; return false; } + +void TTrainParameters::serialize( dictionary_source *Output ) const { + + Output->insert( "trainnumber", TrainName ); + Output->insert( "train_brakingmassratio", BrakeRatio ); + Output->insert( "train_enginetype", LocSeries ); + Output->insert( "train_engineload", LocLoad ); + + Output->insert( "train_stationindex", StationIndex ); + Output->insert( "train_stationcount", StationCount ); + if( StationCount > 0 ) { + // timetable stations data, if there's any + for( auto stationidx = 1; stationidx <= StationCount; ++stationidx ) { + auto const stationlabel { "train_station" + std::to_string( stationidx ) + "_" }; + auto const &timetableline { TimeTable[ stationidx ] }; + Output->insert( ( stationlabel + "name" ), Bezogonkow( timetableline.StationName ) ); + Output->insert( ( stationlabel + "fclt" ), Bezogonkow( timetableline.StationWare ) ); + Output->insert( ( stationlabel + "lctn" ), timetableline.km ); + Output->insert( ( stationlabel + "vmax" ), timetableline.vmax ); + Output->insert( ( stationlabel + "ah" ), timetableline.Ah ); + Output->insert( ( stationlabel + "am" ), timetableline.Am ); + Output->insert( ( stationlabel + "dh" ), timetableline.Dh ); + Output->insert( ( stationlabel + "dm" ), timetableline.Dm ); + } + } +} diff --git a/mtable.h b/mtable.h index ea905e4c..78816aaa 100644 --- a/mtable.h +++ b/mtable.h @@ -83,6 +83,7 @@ class TTrainParameters bool LoadTTfile(std::string scnpath, int iPlus, double vmax); bool DirectionChange(); void StationIndexInc(); + void serialize( dictionary_source *Output ) const; }; class TMTableTime diff --git a/screenshot.cpp b/screenshot.cpp index 33b4bad9..765f37f7 100644 --- a/screenshot.cpp +++ b/screenshot.cpp @@ -38,7 +38,7 @@ void screenshot_manager::screenshot_save_thread( char *img, int w, int h ) uint64_t perf; #ifdef _WIN32 QueryPerformanceCounter((LARGE_INTEGER*)&perf); -#elif __linux__ +#elif __unix__ timespec ts; clock_gettime(CLOCK_REALTIME, &ts); perf = ts.tv_nsec; diff --git a/stdafx.h b/stdafx.h index 90301cc2..98fef00a 100644 --- a/stdafx.h +++ b/stdafx.h @@ -6,6 +6,12 @@ #ifndef STDAFX_H #define STDAFX_H +#ifdef __APPLE__ +#ifndef __unix__ +#define __unix__ 1 +#endif +#endif + #define _USE_MATH_DEFINES #include #ifdef _MSC_VER @@ -29,7 +35,7 @@ #include #undef NOMINMAX #endif -#ifdef __linux__ +#ifdef __unix__ #include #endif // stl @@ -71,18 +77,9 @@ #include #include -#ifdef EU07_BUILD_STATIC -#define GLEW_STATIC -#else -#ifdef _WIN32 -#define GLFW_DLL -#endif // _windows -#endif // build_static - #include "glad/glad.h" #define GLFW_INCLUDE_NONE -//#define GLFW_INCLUDE_GLU #include #ifndef GLFW_TRUE diff --git a/version.h b/version.h index 82a85aa3..492e4478 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION_INFO "M7 (GL3 NET) 02.04.2019" +#define VERSION_INFO "M7 (GL3 NET) 15.04.2019" diff --git a/widgets/popup.h b/widgets/popup.h index 27d2d047..da0d3b8f 100644 --- a/widgets/popup.h +++ b/widgets/popup.h @@ -12,7 +12,7 @@ class popup public: popup(ui_panel &panel); - ~popup(); + virtual ~popup(); bool render();