diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index c9c3bb04..d3e058d9 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -122,7 +122,18 @@ static int const dtrain_loaddestroyed = 32;/*dla wagonow*/ static int const dtrain_axle = 64; static int const dtrain_out = 128; /*wykolejenie*/ static int const dtrain_pantograph = 256; /*polamanie pantografu*/ - /*wagi prawdopodobienstwa dla funkcji FuzzyLogic*/ + +/*przyczyny wykolejenia*/ +enum DerailReason { + NONE = 0, + END_OF_TRACK = 1, // Ra: powód wykolejenia: brak szyn + TOO_HIGH_SPEED = 2, // Ra: powód wykolejenia: przewrócony na łuku + GAUGE_MISMATCH = 3, // Ra: powód wykolejenia: za szeroki tor + WRONG_TRACK_TYPE = 4, // Ra: powód wykolejenia: nieodpowiednia trajektoria + COLLISION = 5, // powód wykolejenia: zderzenie z innym pojazdem +}; + +/*wagi prawdopodobienstwa dla funkcji FuzzyLogic*/ #define p_elengproblem (1e-02) #define p_elengdamage (1e-01) #define p_coupldmg (2e-03) @@ -1045,7 +1056,7 @@ class TMoverParameters bool is_warm{false}; // fluid is too hot bool is_hot{false}; // fluid temperature crossed cooling threshold bool is_flowing{false}; // fluid is being pushed through the circuit - } water, water_aux, oil; + } water, water_aux, oil, engine; // output, state of affected devices bool PA{false}; // malfunction flag float rpmw{0.0}; // current main circuit fan revolutions @@ -1066,6 +1077,9 @@ class TMoverParameters float temperatura1{50.0}; float temperatura2{40.0}; float powerfactor{1.0}; // coefficient of heat generation for engines other than su45 + // engine overheat threshold + float engine_max_temp{-1}; // maximum acceptable engine temperature, triggers overheat lamp when exceeded + bool engine_is_hot{false}; // engine temperature crossed cooling threshold }; struct spring_brake @@ -1590,7 +1604,6 @@ class TMoverParameters int DamageFlag = 0; // kombinacja bitowa stalych dtrain_* } int EngDmgFlag = 0; // kombinacja bitowa stalych usterek} - int DerailReason = 0; // przyczyna wykolejenia // EndSignalsFlag: byte; {ABu 060205: zmiany - koncowki: 1/16 - swiatla prz/tyl, 2/31 - blachy prz/tyl} // HeadSignalsFlag: byte; {ABu 060205: zmiany - swiatla: 1/2/4 - przod, 16/32/63 - tyl} @@ -1832,7 +1845,7 @@ public: int DettachStatus(int ConnectNo); bool Dettach(int ConnectNo); void damage_coupler( int const End ); - void derail( int const Reason ); + void Derail(DerailReason Reason); bool DirectionForward(); bool DirectionBackward( void );/*! kierunek ruchu*/ bool EIMDirectionChangeAllow( void ) const; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index ca62fb72..4a98bb60 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1252,10 +1252,10 @@ void TMoverParameters::CollisionDetect(int const End, double const dt) } if( velocitydifference > safevelocitylimit * ( TotalMass / othervehicle->TotalMass ) ) { - derail( 5 ); + Derail(COLLISION); } if( velocitydifference > safevelocitylimit * ( othervehicle->TotalMass / TotalMass ) ) { - othervehicle->derail( 5 ); + othervehicle->Derail(COLLISION); } } } @@ -1318,24 +1318,31 @@ TMoverParameters::damage_coupler( int const End ) { WriteLog( "Bad driving: " + Name + " broke a coupler" ); } -void -TMoverParameters::derail( int const Reason ) { - +void TMoverParameters::Derail( DerailReason const Reason ) { if( SetFlag( DamageFlag, dtrain_out ) ) { - - DerailReason = Reason; // TODO: enum derail causes EventFlag = true; MainSwitch( false, range_t::local ); AccS *= 0.65; V *= 0.65; + RunningShape.R = 0; if( Vel < 5.0 ) { // HACK: prevent permanent axle spin in static vehicle after a collision nrot = 0.0; SlippingWheels = false; } - WriteLog( "Bad driving: " + Name + " derailed" ); + // Print a message in the log. + if (Reason == END_OF_TRACK) + ErrorLog("Bad driving: " + Name + " derailed due to end of track"); + else if (Reason == TOO_HIGH_SPEED) + ErrorLog("Bad driving: " + Name + " derailed due to too high speed"); + else if (Reason == GAUGE_MISMATCH) + ErrorLog("Bad dynamic: " + Name + " derailed due to track width"); // błąd w scenerii + else if (Reason == WRONG_TRACK_TYPE) + ErrorLog("Bad dynamic: " + Name + " derailed due to wrong track type"); // błąd w scenerii + else if (Reason == COLLISION) + WriteLog("Bad driving: " + Name + " derailed"); // This reason also generates its own message in `TMoverParameters::CollisionDetect()` } } @@ -1436,42 +1443,17 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap // wykolejanie na luku oraz z braku szyn if (TestFlag(CategoryFlag, 1)) { - if (FuzzyLogic((AccN / g) * (1.0 + 0.1 * (Track.DamageFlag && dtrack_freerail)), - TrackW / Dim.H, 1) || - TestFlag(Track.DamageFlag, dtrack_norail)) - if (SetFlag(DamageFlag, dtrain_out)) - { - EventFlag = true; - MainSwitch( false, range_t::local ); - RunningShape.R = 0; - if (TestFlag(Track.DamageFlag, dtrack_norail)) - DerailReason = 1; // Ra: powód wykolejenia: brak szyn - else - DerailReason = 2; // Ra: powód wykolejenia: przewrócony na łuku - } + if (TestFlag(Track.DamageFlag, dtrack_norail)) + Derail(END_OF_TRACK); + if (FuzzyLogic((AccN / g) * (1.0 + 0.1 * (Track.DamageFlag & dtrack_freerail)), TrackW / Dim.H, 1)) + Derail(TOO_HIGH_SPEED); // wykolejanie na poszerzeniu toru if (FuzzyLogic(abs(Track.Width - TrackW), TrackW / 10.0, 1)) - if (SetFlag(DamageFlag, dtrain_out)) - { - EventFlag = true; - MainSwitch( false, range_t::local ); - RunningShape.R = 0; - DerailReason = 3; // Ra: powód wykolejenia: za szeroki tor - } + Derail(GAUGE_MISMATCH); } // wykolejanie wkutek niezgodnosci kategorii toru i pojazdu if (!TestFlag(RunningTrack.CategoryFlag, CategoryFlag)) - if (SetFlag(DamageFlag, dtrain_out)) - { - EventFlag = true; - MainSwitch( false, range_t::local ); - DerailReason = 4; // Ra: powód wykolejenia: nieodpowiednia trajektoria - } - if( ( true == TestFlag( DamageFlag, dtrain_out ) ) - && ( Vel < 1.0 ) ) { - V = 0.0; - AccS = 0.0; - } + Derail(WRONG_TRACK_TYPE); // dL:=(V+AccS*dt/2)*dt; // przyrost dlugosci czyli przesuniecie @@ -8174,6 +8156,10 @@ void TMoverParameters::dizel_Heat( double const dt ) { dizel_heat.oil.is_hot = ( ( dizel_heat.oil.config.temp_max > 0 ) && ( dizel_heat.To > dizel_heat.oil.config.temp_max - ( dizel_heat.oil.is_hot ? 8 : 0 ) ) ); + // engine overheat check + dizel_heat.engine_is_hot = ( + ( dizel_heat.engine_max_temp > 0 ) + && ( dizel_heat.Ts > dizel_heat.engine_max_temp - ( dizel_heat.engine_is_hot ? 8 : 0 ) ) ); auto const PT = ( ( false == dizel_heat.water.is_cold ) @@ -8181,7 +8167,8 @@ void TMoverParameters::dizel_Heat( double const dt ) { && ( false == dizel_heat.water_aux.is_cold ) && ( false == dizel_heat.water_aux.is_hot ) && ( false == dizel_heat.oil.is_cold ) - && ( false == dizel_heat.oil.is_hot ) /* && ( false == awaria_termostatow ) */ ) /* || PTp */; + && ( false == dizel_heat.oil.is_hot ) + && ( false == dizel_heat.engine.is_hot ) /* && ( false == awaria_termostatow ) */ ) /* || PTp */; auto const PPT = ( false == PT ) /* && ( false == PPTp ) */; dizel_heat.PA = ( /* ( ( !zamkniecie or niedomkniecie ) and !WBD ) || */ PPT /* || nurnik || ( woda < 7 ) */ ) /* && ( !PAp ) */; @@ -11254,6 +11241,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { extract_value( dizel_heat.water_aux.config.shutters, "WaterAuxShutters", Input, "" ); extract_value( dizel_heat.oil.config.temp_min, "OilMinTemperature", Input, "" ); extract_value( dizel_heat.oil.config.temp_max, "OilMaxTemperature", Input, "" ); + extract_value( dizel_heat.engine_max_temp, "EngineMaxTemperature", Input, "" ); extract_value( dizel_heat.fan_speed, "WaterCoolingFanSpeed", Input, "" ); // water heater extract_value( WaterHeater.config.temp_min, "HeaterMinTemperature", Input, "" ); diff --git a/README.md b/README.md index 035f9e6b..78822a5a 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -![AppVeyor Build](https://img.shields.io/appveyor/build/Hirek193/maszyna?style=flat-square) -# MaSzyna Train Simulator +![enter image description here](https://raw.githubusercontent.com/MaSzyna-EU07/maszyna/refs/heads/master/readme_header.png) -_A free, **Polish** train simulator._ +# MaSzyna - Railway vehicle simulator -[Website](https://eu07.pl/?setlang=en) · [Report an issue](https://github.com/eu07/maszyna/issues) -![Discord](https://img.shields.io/discord/1019306167803056158?style=flat-square&logo=discord&label=Discord&labelColor=white) +[Website](https://eu07.pl/) · [Report an issue](https://github.com/MaSzyna-EU07/maszyna/issues) · [Community Discord](https://discord.gg/eu07) · [MaSzyna on Steam](https://store.steampowered.com/app/1033030/MaSzyna/) · [MaSzyna on Epic Games Store](https://store.epicgames.com/pl/p/maszyna-79052a) -MaSzyna executable source code is licensed under the [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/) and comes with a large pack of free assets under a [custom license](https://eu07.pl/theme/Maszyna/dokumentacja/inne/readme_pliki/en-licence.html). +![Discord](https://img.shields.io/discord/1019306167803056158?style=flat-square&logo=discord&label=Discord&labelColor=white) ![AppVeyor Build](https://img.shields.io/appveyor/build/Hirek193/maszyna?style=flat-square) + +MaSzyna executable source code is licensed under the [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/) and comes with a large pack of free assets under a [custom license](https://eu07.pl/docs/inne/readme_pliki/en-licence.html). ## Table of Contents diff --git a/application/application.cpp b/application/application.cpp index 5b256dd1..5a84596b 100644 --- a/application/application.cpp +++ b/application/application.cpp @@ -686,6 +686,7 @@ void eu07_application::exit() glfwDestroyWindow(window); } m_taskqueue.exit(); + glfwPollEvents(); // TODO: This fixes a segfault on Wayland when closing. Remove after updating glfw to 3.5. glfwTerminate(); if (!Global.exec_on_exit.empty()) diff --git a/application/driveruipanels.cpp b/application/driveruipanels.cpp index 16465d50..05e20b84 100644 --- a/application/driveruipanels.cpp +++ b/application/driveruipanels.cpp @@ -1138,9 +1138,12 @@ debug_panel::update_section_ai( std::vector &Output ) { "Current order: [" + std::to_string( mechanik.OrderPos ) + "] " + mechanik.Order2Str( mechanik.OrderCurrentGet() ); - if( mechanik.fStopTime < 0.0 ) { - textline += "\n stop time: " + to_string( std::abs( mechanik.fStopTime ), 1 ); - } + if( mechanik.fStopTime < 0.0 ) { + textline += "\n stop time: " + to_string( std::abs( mechanik.fStopTime ), 1 ); + } + if( mechanik.fActionTime < 0.0 ) { + textline += "\n action time: " + to_string( std::abs( mechanik.fActionTime ), 1 ); + } Output.emplace_back( textline, Global.UITextColor ); @@ -1231,7 +1234,8 @@ debug_panel::update_section_ai( std::vector &Output ) { std::vector const drivingflagnames { "StopCloser", "StopPoint", "Active", "Press", "Connect", "Primary", "Late", "StopHere", "StartHorn", "StartHornNow", "StartHornDone", "Oerlikons", "IncSpeed", "TrackEnd", "SwitchFound", "GuardSignal", - "Visibility", "DoorOpened", "PushPull", "SignalFound", "StopPointFound" /*"SemaphorWasElapsed", "TrainInsideStation", "SpeedLimitFound"*/ }; + "Visibility", "DoorOpened", "PushPull", "SignalFound", "StopPointFound", "GuardOpenDoor", "DepartureWarned" + /*"SemaphorWasElapsed", "TrainInsideStation", "SpeedLimitFound"*/ }; textline = "Driving flags:"; for( int idx = 0, flagbit = 1; idx < drivingflagnames.size(); ++idx, flagbit <<= 1 ) { diff --git a/application/editoruipanels.cpp b/application/editoruipanels.cpp index 11c1e4f0..966f14fb 100644 --- a/application/editoruipanels.cpp +++ b/application/editoruipanels.cpp @@ -97,17 +97,17 @@ void itemproperties_panel::update(scene::basic_node const *Node) // 3d shape auto modelfile{((subnode->pModel != nullptr) ? subnode->pModel->NameGet() : "(none)")}; - if (modelfile.find(szModelPath) == 0) + if (modelfile.find(paths::models) == 0) { // don't include 'models/' in the path - modelfile.erase(0, std::string{szModelPath}.size()); + modelfile.erase(0, std::string{paths::models}.size()); } // texture auto texturefile{((subnode->Material()->replacable_skins[1] != null_handle) ? GfxRenderer->Material(subnode->Material()->replacable_skins[1])->GetName() : "(none)")}; - if (texturefile.find(szTexturePath) == 0) + if (texturefile.find(paths::textures) == 0) { // don't include 'textures/' in the path - texturefile.erase(0, std::string{szTexturePath}.size()); + texturefile.erase(0, std::string{paths::textures}.size()); } text_lines.emplace_back("mesh: " + modelfile, Global.UITextColor); text_lines.emplace_back("skin: " + texturefile, Global.UITextColor); @@ -131,14 +131,14 @@ void itemproperties_panel::update(scene::basic_node const *Node) text_lines.emplace_back(textline, Global.UITextColor); // textures auto texturefile{((subnode->m_material1 != null_handle) ? GfxRenderer->Material(subnode->m_material1)->GetName() : "(none)")}; - if (texturefile.find(szTexturePath) == 0) + if (texturefile.find(paths::textures) == 0) { - texturefile.erase(0, std::string{szTexturePath}.size()); + texturefile.erase(0, std::string{paths::textures}.size()); } auto texturefile2{((subnode->m_material2 != null_handle) ? GfxRenderer->Material(subnode->m_material2)->GetName() : "(none)")}; - if (texturefile2.find(szTexturePath) == 0) + if (texturefile2.find(paths::textures) == 0) { - texturefile2.erase(0, std::string{szTexturePath}.size()); + texturefile2.erase(0, std::string{paths::textures}.size()); } textline = "skins:\n " + texturefile + "\n " + texturefile2; text_lines.emplace_back(textline, Global.UITextColor); diff --git a/application/scenarioloaderuilayer.cpp b/application/scenarioloaderuilayer.cpp index 1beb5de8..7ec9b0aa 100644 --- a/application/scenarioloaderuilayer.cpp +++ b/application/scenarioloaderuilayer.cpp @@ -91,6 +91,8 @@ std::vector scenarioloader_ui::get_random_trivia() int i = RandomInt(0, static_cast(triviaData.size()) - 1); std::string triviaStr = triviaData[i]["text"]; std::string background = triviaData[i]["background"]; + if (triviaData[i].contains("scenery")) + sceneryName = triviaData[i]["scenery"]; // divide trivia into multiple lines - UTF-8 safe implementation // Different languages need different character limits due to character width differences @@ -275,6 +277,15 @@ void scenarioloader_ui::render_() draw_list->AddText(text_pos, IM_COL32_WHITE, line.c_str()); } } + + // Scenery name + // Draw only if defined + if (!sceneryName.empty()) + { + ImVec2 text_size = ImGui::CalcTextSize(sceneryName.c_str()); + ImVec2 text_pos = ImVec2(screen_size.x - 16 - text_size.x, 16); + draw_list->AddText(text_pos, IM_COL32_WHITE, sceneryName.c_str()); + } // Progress bar at the bottom of the screen const auto p1 = ImVec2(0, Global.window_size.y - 2); diff --git a/application/scenarioloaderuilayer.h b/application/scenarioloaderuilayer.h index 3be9cba1..72d7c3df 100644 --- a/application/scenarioloaderuilayer.h +++ b/application/scenarioloaderuilayer.h @@ -30,4 +30,9 @@ private: std::vector get_random_trivia(); void generate_gradient_tex(); void load_wheel_frames(); + + /// + /// Scenery name for eligable trivias + /// + std::string sceneryName; }; diff --git a/audio/audio.cpp b/audio/audio.cpp index 65bd5ec9..8bdf1dd1 100644 --- a/audio/audio.cpp +++ b/audio/audio.cpp @@ -135,11 +135,11 @@ buffer_manager::create( std::string const &Filename ) { } } // if dynamic-specific and/or direct lookups find nothing, try the default sound folder - lookup = find_buffer( szSoundPath + filename ); + lookup = find_buffer( paths::sounds + filename ); if( lookup != null_handle ) { return lookup; } - filelookup = find_file( szSoundPath + filename ); + filelookup = find_file( paths::sounds + filename ); if( false == filelookup.empty() ) { return emplace( filelookup ); } diff --git a/audio/sound.cpp b/audio/sound.cpp index 8fcdd033..77e6c9c2 100644 --- a/audio/sound.cpp +++ b/audio/sound.cpp @@ -319,9 +319,9 @@ sound_source::export_as_text( std::ostream &Output ) const { << m_offset.z << ' '; // sound data auto soundfile { audio::renderer.buffer( sound( sound_id::main ).buffer ).name }; - if( soundfile.find( szSoundPath ) == 0 ) { + if( soundfile.find( paths::sounds ) == 0 ) { // don't include 'sounds/' in the path - soundfile.erase( 0, std::string{ szSoundPath }.size() ); + soundfile.erase( 0, std::string{ paths::sounds }.size() ); } Output << soundfile << ' '; diff --git a/betterRenderer/renderer/source/nvtexture.cpp b/betterRenderer/renderer/source/nvtexture.cpp index 47178111..adc3ff1d 100644 --- a/betterRenderer/renderer/source/nvtexture.cpp +++ b/betterRenderer/renderer/source/nvtexture.cpp @@ -314,7 +314,7 @@ size_t NvTextureManager::FetchTexture(std::string path, int format_hint, { const std::array filenames{ - Global.asCurrentTexturePath + path, path, szTexturePath + path}; + Global.asCurrentTexturePath + path, path, paths::textures + path}; for (const auto &filename : filenames) { if (auto found = m_texture_map.find(filename); found != m_texture_map.end()) diff --git a/model/AnimModel.cpp b/model/AnimModel.cpp index 20fdd628..0e4ae9d3 100644 --- a/model/AnimModel.cpp +++ b/model/AnimModel.cpp @@ -418,69 +418,50 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) { // case ls_Off: ustalenie czasu migotania, t<1s (f>1Hz), np. 0.1 => t=0.1 (f=10Hz) // case ls_On: ustalenie wypełnienia ułamkiem, np. 1.25 => zapalony przez 1/4 okresu // case ls_Blink: ustalenie częstotliwości migotania, f<1Hz (t>1s), np. 2.2 => f=0.2Hz (t=5s) - float modeintegral, modefractional; - for( int idx = 0; idx < iNumLights; ++idx ) { + for (int idx = 0; idx < iNumLights; ++idx) { + float modeintegral; + const float modefractional = std::modf(std::abs(lsLights[idx]), &modeintegral); + const auto mode = static_cast(modeintegral); + if (mode == ls_Dark || mode == ls_Home) + continue; // light threshold modes don't use timers - modefractional = std::modf( std::abs( lsLights[ idx ] ), &modeintegral ); - - if( modeintegral >= ls_Dark ) { - // light threshold modes don't use timers - continue; - } - auto const mode { static_cast( modeintegral ) }; - - auto &opacity { m_lightopacities[ idx ] }; - auto &timer { m_lighttimers[ idx ] }; - if( ( modeintegral < ls_Blink ) && ( modefractional < 0.01f ) ) { - // simple flip modes - auto const transitiontime { ( m_transition ? std::min( 0.65f, std::min( fOnTime, fOffTime ) * 0.45f ) : 0.01f ) }; - - switch( mode ) { - case ls_Off: { - // reduce to zero - timer = std::max( 0.f, timer - timedelta ); - break; - } - case ls_On: { - // increase to max value - timer = std::min( transitiontime, timer + timedelta ); - break; - } - default: { - break; - } - } - opacity = timer / transitiontime; - } - else { - // blink modes - auto const ontime { ( - ( mode == ls_Blink ) ? ( ( modefractional < 0.01f ) ? fOnTime : ( 1.f / modefractional ) * 0.5f ) : - ( mode == ls_Off ) ? modefractional * 0.5f : - ( mode == ls_On ) ? modefractional * ( fOnTime + fOffTime ) : - fOnTime ) }; // fallback - auto const offtime { ( - ( mode == ls_Blink ) ? ( ( modefractional < 0.01f ) ? fOffTime : ontime ) : - ( mode == ls_Off ) ? ontime : - ( mode == ls_On ) ? ( fOnTime + fOffTime ) - ontime : - fOffTime ) }; // fallback - auto const transitiontime { ( m_transition ? std::min(0.65f, std::min( ontime, offtime ) * 0.45f ) : 0.01f ) }; - - timer = fmod(timer + timedelta * ( lsLights[ idx ] > 0.f ? 1.f : -1.f ), ontime + offtime); - // set opacity depending on blink stage - if( timer < ontime ) { - // blink on - opacity = clamp( timer / transitiontime, 0.f, 1.f ); - } - else { - // blink off - opacity = 1.f - clamp( ( timer - ontime ) / transitiontime, 0.f, 1.f ); - } - } + float &opacity { m_lightopacities[ idx ] }; + float &timer { m_lighttimers[ idx ] }; + // Phase logic + float ontime = + modefractional < 0.01f ? fOnTime : + mode == ls_Off ? modefractional * 0.5f : + mode == ls_On ? modefractional * (fOnTime + fOffTime) : + mode == ls_Blink ? 0.5f / modefractional : + fOnTime; // fallback + float offtime = + modefractional < 0.01f ? fOffTime : + mode == ls_Off ? modefractional * 0.5f : + mode == ls_On ? (1 - modefractional) * (fOnTime + fOffTime) : + mode == ls_Blink ? 0.5f / modefractional : + fOffTime; // fallback + // Determine whether the light is currently on and update the timers + bool on = false; + if ((mode == ls_Off || mode == ls_On) && modefractional < 0.01f) + on = mode == ls_On; + else { + timer = fmod(timer + timedelta, ontime + offtime); // 0..(ontime+offtime) + const float time = fmod(timer + ( lsLights[ idx ] > 0.f ? 0.f : 0.5f ), ontime + offtime); // time with correction for phase shift for negative light values + on = time < ontime; + } + // Update the light brightness + const float transitionontime = std::min(0.25f, std::min(ontime, offtime) * 0.95f); + const float transitionofftime = std::min(0.45f, std::min(ontime, offtime) * 0.95f); + if (on) + opacity += m_transition ? timedelta / transitionontime : 1.f; // increase to max value + else + opacity -= m_transition ? timedelta / transitionofftime : 1.f; // reduce to zero + // Clamp the opacity + opacity = clamp(opacity, 0.f, 1.f); } // Ra 2F1I: to by można pomijać dla modeli bez animacji, których jest większość - for (auto entry : m_animlist) { + for (const auto entry : m_animlist) { if (!entry->evDone) // jeśli jest bez eventu entry->UpdateModel(); // przeliczenie animacji każdego submodelu } @@ -675,9 +656,9 @@ TAnimModel::export_as_text_( std::ostream &Output ) const { pModel ? pModel->NameGet() + ".t3d" : // rainsted requires model file names to include an extension "none" ) }; - if( modelfile.find( szModelPath ) == 0 ) { + if( modelfile.find( paths::models ) == 0 ) { // don't include 'models/' in the path - modelfile.erase( 0, std::string{ szModelPath }.size() ); + modelfile.erase( 0, std::string{ paths::models }.size() ); } Output << modelfile << ' '; // texture @@ -685,9 +666,9 @@ TAnimModel::export_as_text_( std::ostream &Output ) const { m_materialdata.replacable_skins[ 1 ] != null_handle ? GfxRenderer->Material( m_materialdata.replacable_skins[ 1 ] )->GetName() : "none" ) }; - if( texturefile.find( szTexturePath ) == 0 ) { + if( texturefile.find( paths::textures ) == 0 ) { // don't include 'textures/' in the path - texturefile.erase( 0, std::string{ szTexturePath }.size() ); + texturefile.erase( 0, std::string{ paths::textures }.size() ); } if( contains( texturefile, ' ' ) ) { Output << "\"" << texturefile << "\"" << ' '; diff --git a/model/MdlMngr.cpp b/model/MdlMngr.cpp index b0a7ba76..f70ba118 100644 --- a/model/MdlMngr.cpp +++ b/model/MdlMngr.cpp @@ -125,7 +125,7 @@ TModelsManager::find_in_databank( std::string const &Name ) { std::vector filenames { Name, - szModelPath + Name }; + paths::models + Name }; for( auto const &filename : filenames ) { auto const lookup { m_modelsmap.find( filename ) }; @@ -146,7 +146,7 @@ TModelsManager::find_on_disk( std::string const &Name ) { auto lookup = ( FileExists( Name + extension ) ? Name : - FileExists( szModelPath + Name + extension ) ? szModelPath + Name : + FileExists( paths::models + Name + extension ) ? paths::models + Name : "" ); if( false == lookup.empty() ) { return lookup; diff --git a/model/Texture.cpp b/model/Texture.cpp index f4085edf..c4dabd34 100644 --- a/model/Texture.cpp +++ b/model/Texture.cpp @@ -1467,7 +1467,7 @@ texture_manager::find_in_databank( std::string const &Texturename ) const { std::vector const filenames { Global.asCurrentTexturePath + Texturename, Texturename, - szTexturePath + Texturename }; + paths::textures + Texturename }; for( auto const &filename : filenames ) { auto const lookup { m_texturemappings.find( filename ) }; @@ -1486,7 +1486,7 @@ texture_manager::find_on_disk( std::string const &Texturename ) { std::vector const filenames { Global.asCurrentTexturePath + Texturename, Texturename, - szTexturePath + Texturename }; + paths::textures + Texturename }; auto lookup = FileExists( diff --git a/model/material.cpp b/model/material.cpp index 99261338..e0b04475 100644 --- a/model/material.cpp +++ b/model/material.cpp @@ -582,7 +582,7 @@ material_manager::find_in_databank( std::string const &Materialname ) const { std::vector const filenames { Global.asCurrentTexturePath + Materialname, Materialname, - szTexturePath + Materialname }; + paths::textures + Materialname }; for( auto const &filename : filenames ) { auto const lookup { m_materialmappings.find( filename ) }; @@ -602,7 +602,7 @@ material_manager::find_on_disk( std::string const &Materialname ) { auto const materialname { ToLower( Materialname ) }; return ( FileExists( - { Global.asCurrentTexturePath + materialname, materialname, szTexturePath + materialname }, + { Global.asCurrentTexturePath + materialname, materialname, paths::textures + materialname }, { ".mat" } ) ); } diff --git a/readme_header.png b/readme_header.png new file mode 100644 index 00000000..8d336fc4 Binary files /dev/null and b/readme_header.png differ diff --git a/scene/scenenode.h b/scene/scenenode.h index fb0959a7..eac74651 100644 --- a/scene/scenenode.h +++ b/scene/scenenode.h @@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/. #include "model/material.h" #include "model/vertex.h" #include "rendering/geometrybank.h" -#include "utils/uuid.hpp" +#include "../utilities/uuid.hpp" struct lighting_data { diff --git a/scripting/lua.cpp b/scripting/lua.cpp index c6585e76..58cdd41a 100644 --- a/scripting/lua.cpp +++ b/scripting/lua.cpp @@ -4,7 +4,6 @@ #include "utilities/Logs.h" #include "world/MemCell.h" #include "vehicle/Driver.h" -#include "scripting/lua_ffi.h" #include "simulation/simulation.h" lua::lua() @@ -15,12 +14,33 @@ lua::lua() lua_atpanic(state, atpanic); luaL_openlibs(state); - lua_getglobal(state, "package"); - lua_pushstring(state, "preload"); - lua_gettable(state, -2); - lua_pushcclosure(state, openffi, 0); - lua_setfield(state, -2, "eu07.events"); - lua_settop(state, 0); + static constexpr luaL_Reg api[] = + { + {"event_create", scriptapi_event_create}, + {"event_find", scriptapi_event_find}, + {"event_exists", scriptapi_event_exists}, + {"event_getname", scriptapi_event_getname}, + {"event_dispatch", scriptapi_event_dispatch}, + {"event_dispatch_n", scriptapi_event_dispatch_n}, + {"track_find", scriptapi_track_find}, + {"track_isoccupied", scriptapi_track_isoccupied}, + {"track_isoccupied_n", scriptapi_track_isoccupied_n}, + {"isolated_find", scriptapi_isolated_find}, + {"isolated_isoccupied", scriptapi_isolated_isoccupied}, + {"isolated_isoccupied_n", scriptapi_isolated_isoccupied_n}, + {"train_getname", scriptapi_train_getname}, + {"dynobj_putvalues", scriptapi_dynobj_putvalues}, + {"memcell_find", scriptapi_memcell_find}, + {"memcell_read", scriptapi_memcell_read}, + {"memcell_read_n", scriptapi_memcell_read_n}, + {"memcell_update", scriptapi_memcell_update}, + {"memcell_update_n", scriptapi_memcell_update_n}, + {"random", scriptapi_random}, + {"writelog", scriptapi_writelog}, + {"writeerrorlog", scriptapi_writeerrorlog}, + {nullptr, nullptr} + }; + luaL_register(state, "eu07.events", api); } lua::~lua() @@ -29,184 +49,324 @@ lua::~lua() state = nullptr; } -std::string lua::get_error() +std::string lua::get_error() const { - return std::string(lua_tostring(state, -1)); + return lua_tostring(state, -1); } -void lua::interpret(std::string file) +void lua::interpret(const std::string& file) const { if (luaL_dofile(state, file.c_str())) { - const char *str = lua_tostring(state, -1); - ErrorLog(std::string(str), logtype::lua); + std::string str = lua_tostring(state, -1); + ErrorLog("lua: Runtime error: " + str, logtype::lua); } } -// NOTE: we cannot throw exceptions in callbacks -// because it is not supported by LuaJIT on x86 windows - int lua::atpanic(lua_State *s) { - std::string err(lua_tostring(s, -1)); - ErrorLog(err, logtype::lua); + std::string err = lua_tostring(s, -1); + ErrorLog("lua: Runtime error: " + err, logtype::lua); return 0; } -int lua::openffi(lua_State *s) +/// Dereferences the function handler from the Lua registry. +void lua::unref(lua_State *L, const int ref) { - if (luaL_dostring(s, lua_ffi)) - { - ErrorLog(std::string(lua_tostring(s, -1)), logtype::lua); - return 0; - } - return 1; + luaL_unref(L, LUA_REGISTRYINDEX, ref); } -#if defined _WIN32 -# if defined __GNUC__ -# define EXPORT __attribute__ ((dllexport)) -# else -# define EXPORT __declspec(dllexport) -# endif -#elif defined __GNUC__ -# define EXPORT __attribute__ ((visibility ("default"))) -#else -# define EXPORT -#endif - -extern "C" +/// Executes the callback function of an event created with `scriptapi_event_create`. +void lua::dispatch_event(lua_State *L, const int handler, basic_event *event, const TDynamicObject *activator) { - EXPORT basic_event* scriptapi_event_create(const char* name, double delay, double randomdelay, lua::eventhandler_t handler) - { - basic_event *event = new lua_event(handler); - event->m_name = std::string(name); - event->m_delay = delay; - event->m_delayrandom = randomdelay; - - if (simulation::Events.insert(event)) - return event; - else - return nullptr; - } - - EXPORT basic_event* scriptapi_event_find(const char* name) - { - std::string str(name); - basic_event *e = simulation::Events.FindEvent(str); - if (e) - return e; - else - WriteLog("lua: missing event: " + str); - return nullptr; - } - - EXPORT TTrack* scriptapi_track_find(const char* name) - { - std::string str(name); - TTrack *track = simulation::Paths.find(str); - if (track) - return track; - else - WriteLog("lua: missing track: " + str); - return nullptr; - } - - EXPORT bool scriptapi_track_isoccupied(TTrack* track) - { - if (track) - return !track->IsEmpty(); - return false; - } - - EXPORT TIsolated* scriptapi_isolated_find(const char* name) - { - std::string str(name); - TIsolated *isolated = TIsolated::Find(name); - if (isolated) - return isolated; - else - WriteLog("lua: missing isolated: " + str); - return nullptr; - } - - EXPORT bool scriptapi_isolated_isoccupied(TIsolated* isolated) - { - if (isolated) - return isolated->Busy(); - return false; - } - - EXPORT const char* scriptapi_event_getname(basic_event *e) - { - if (e) - return e->m_name.c_str(); - return nullptr; - } - - EXPORT const char* scriptapi_train_getname(TDynamicObject *dyn) - { - if (dyn && dyn->Mechanik) - return dyn->Mechanik->TrainName().c_str(); - return nullptr; - } - - EXPORT void scriptapi_event_dispatch(basic_event *e, TDynamicObject *activator, double delay) - { - if (e) - simulation::Events.AddToQuery(e, activator, delay); - } - - EXPORT double scriptapi_random(double a, double b) - { - return Random(a, b); - } - - EXPORT void scriptapi_writelog(const char* txt) - { - WriteLog("lua: log: " + std::string(txt), logtype::lua); - } - - EXPORT void scriptapi_writeerrorlog(const char* txt) - { - ErrorLog("lua: log: " + std::string(txt), logtype::lua); - } - - struct memcell_values { const char *str; double num1; double num2; }; - - EXPORT TMemCell* scriptapi_memcell_find(const char *name) - { - std::string str(name); - TMemCell *mc = simulation::Memory.find(str); - if (mc) - return mc; - else - WriteLog("lua: missing memcell: " + str); - return nullptr; - } - - EXPORT memcell_values scriptapi_memcell_read(TMemCell *mc) - { - if (!mc) - return { nullptr, 0.0, 0.0 }; - return { mc->Text().c_str(), mc->Value1(), mc->Value2() }; - } - - EXPORT void scriptapi_memcell_update(TMemCell *mc, const char *str, double num1, double num2) - { - if (!mc) - return; - mc->UpdateValues(std::string(str), num1, num2, - basic_event::flags::text | basic_event::flags::value1 | basic_event::flags::value2); - } - - EXPORT void scriptapi_dynobj_putvalues(TDynamicObject *dyn, const char *str, double num1, double num2) - { - if (!dyn) - return; - TLocation loc; - if (dyn->Mechanik) - dyn->Mechanik->PutCommand(std::string(str), num1, num2, loc); - else - dyn->MoverParameters->PutCommand(std::string(str), num1, num2, loc); - } + lua_rawgeti(L, LUA_REGISTRYINDEX, handler); + lua_pushlightuserdata(L, event); + lua_pushlightuserdata(L, const_cast(activator)); + lua_call(L, 2, 0); +} + +void lua::push_memcell_values(lua_State *L, const TMemCell *mc) +{ + lua_createtable(L, 0, 3); + lua_pushstring(L, "str"); + lua_pushstring(L, mc ? mc->Text().c_str() : nullptr); + lua_settable(L, -3); + lua_pushstring(L, "num1"); + lua_pushnumber(L, mc ? mc->Value1() : 0.0); + lua_settable(L, -3); + lua_pushstring(L, "num2"); + lua_pushnumber(L, mc ? mc->Value2() : 0.0); + lua_settable(L, -3); +} + +memcell_values lua::get_memcell_values(lua_State *L, int idx) +{ + // For negative indices, we need to account for the extra item that is the key name/value extracted from the table. + if (idx < 0) + idx--; + memcell_values mc{nullptr, 0.0, 0.0}; + lua_pushstring(L, "str"); + lua_gettable(L, idx); + if (lua_isstring(L, -1)) + mc.str = lua_tostring(L, -1); + lua_pop(L, 1); + lua_pushstring(L, "num1"); + lua_gettable(L, idx); + if (lua_isnumber(L, -1)) + mc.num1 = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_pushstring(L, "num2"); + lua_gettable(L, idx); + if (lua_isnumber(L, -1)) + mc.num2 = lua_tonumber(L, -1); + lua_pop(L, 1); + return mc; +} + +int lua::scriptapi_event_create(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + double delay = lua_tonumber(L, 2); + double randomdelay = lua_tonumber(L, 3); + lua_pushvalue(L, 4); + int funcRef = luaL_ref(L, LUA_REGISTRYINDEX); + + basic_event *event = new lua_event(L, funcRef); + event->m_name = name; + event->m_delay = delay; + event->m_delayrandom = randomdelay; + if (simulation::Events.insert(event)) + { + lua_pushlightuserdata(L, event); + return 1; + } + return 0; +} + +int lua::scriptapi_event_find(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + basic_event *event = simulation::Events.FindEvent(name); + if (event) + { + lua_pushlightuserdata(L, event); + return 1; + } + ErrorLog("lua: missing event: " + name); + return 0; +} + +int lua::scriptapi_event_exists(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + basic_event *event = simulation::Events.FindEvent(name); + lua_pushboolean(L, event != nullptr); + return 1; +} + +int lua::scriptapi_event_getname(lua_State *L) +{ + auto *event = static_cast(lua_touserdata(L, 1)); + if (event) + { + lua_pushstring(L, event->m_name.c_str()); + return 1; + } + return 0; +} + +int lua::scriptapi_event_dispatch(lua_State *L) +{ + auto *event = static_cast(lua_touserdata(L, 1)); + auto *activator = static_cast(lua_touserdata(L, 2)); + double delay = lua_tonumber(L, 3); + if (event) + simulation::Events.AddToQuery(event, activator, delay); + return 0; +} + +int lua::scriptapi_event_dispatch_n(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + auto *activator = static_cast(lua_touserdata(L, 2)); + double delay = lua_tonumber(L, 3); + basic_event *event = simulation::Events.FindEvent(name); + if (event) + simulation::Events.AddToQuery(event, activator, delay); + else + ErrorLog("lua: missing event: " + name); + return 0; +} + +int lua::scriptapi_track_find(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + TTrack *track = simulation::Paths.find(name); + if (track) + { + lua_pushlightuserdata(L, track); + return 1; + } + ErrorLog("lua: missing track: " + name); + return 0; +} + +int lua::scriptapi_track_isoccupied(lua_State *L) +{ + auto *track = static_cast(lua_touserdata(L, 1)); + if (track) + { + lua_pushboolean(L, !track->IsEmpty()); + return 1; + } + return 0; +} + +int lua::scriptapi_track_isoccupied_n(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + TTrack *track = simulation::Paths.find(name); + if (track) + { + lua_pushboolean(L, !track->IsEmpty()); + return 1; + } + ErrorLog("lua: missing track: " + name); + return 0; +} + +int lua::scriptapi_isolated_find(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + TIsolated *isolated = TIsolated::Find(name); + if (isolated) + { + lua_pushlightuserdata(L, isolated); + return 1; + } + ErrorLog("lua: missing isolated: " + name); + return 0; +} + +int lua::scriptapi_isolated_isoccupied(lua_State *L) +{ + auto *isolated = static_cast(lua_touserdata(L, 1)); + if (isolated) + { + lua_pushboolean(L, isolated->Busy()); + return 1; + } + return 0; +} + +int lua::scriptapi_isolated_isoccupied_n(lua_State *L) +{ + std::string name = lua_tostring(L, 1); + TIsolated *isolated = TIsolated::Find(name); + if (isolated) + { + lua_pushboolean(L, isolated->Busy()); + return 1; + } + ErrorLog("lua: missing isolated: " + name); + return 0; +} + +int lua::scriptapi_train_getname(lua_State *L) +{ + auto *dyn = static_cast(lua_touserdata(L, 1)); + if (dyn && dyn->Mechanik) + { + lua_pushstring(L, dyn->Mechanik->TrainName().c_str()); + return 1; + } + return 0; +} + +int lua::scriptapi_dynobj_putvalues(lua_State *L) +{ + auto *dyn = static_cast(lua_touserdata(L, 1)); + auto [str, num1, num2] = get_memcell_values(L, 2); + if (!dyn) + return 0; + TLocation loc{}; + if (dyn->Mechanik) + dyn->Mechanik->PutCommand(str, num1, num2, loc); + else + dyn->MoverParameters->PutCommand(str, num1, num2, loc); + return 0; +} + +int lua::scriptapi_memcell_find(lua_State *L) +{ + std::string str = lua_tostring(L, 1); + TMemCell *mc = simulation::Memory.find(str); + if (mc) + { + lua_pushlightuserdata(L, mc); + return 1; + } + ErrorLog("lua: missing memcell: " + str); + return 0; +} + +int lua::scriptapi_memcell_read(lua_State *L) +{ + auto *mc = static_cast(lua_touserdata(L, 1)); + push_memcell_values(L, mc); + return 1; +} + +int lua::scriptapi_memcell_read_n(lua_State *L) +{ + std::string str = lua_tostring(L, 1); + TMemCell *mc = simulation::Memory.find(str); + push_memcell_values(L, mc); + if (!mc) + ErrorLog("lua: missing memcell: " + str); + return 1; +} + +int lua::scriptapi_memcell_update(lua_State *L) +{ + auto *mc = static_cast(lua_touserdata(L, 1)); + auto [str, num1, num2] = get_memcell_values(L, 2); + if (mc) + mc->UpdateValues(str, num1, num2, + basic_event::flags::text | basic_event::flags::value1 | basic_event::flags::value2); + return 0; +} + +int lua::scriptapi_memcell_update_n(lua_State *L) +{ + std::string mstr = lua_tostring(L, 1); + auto [str, num1, num2] = get_memcell_values(L, 2); + TMemCell *mc = simulation::Memory.find(mstr); + if (mc) + mc->UpdateValues(str, num1, num2, + basic_event::flags::text | basic_event::flags::value1 | basic_event::flags::value2); + else + ErrorLog("lua: missing memcell: " + mstr); + return 0; +} + +int lua::scriptapi_random(lua_State *L) +{ + double a = lua_tonumber(L, 1); + double b = lua_tonumber(L, 2); + lua_pushnumber(L, Random(a, b)); + return 1; +} + +int lua::scriptapi_writelog(lua_State *L) +{ + std::string txt = lua_tostring(L, 1); + WriteLog("lua: log: " + txt, logtype::lua); + return 0; +} + +int lua::scriptapi_writeerrorlog(lua_State *L) +{ + std::string txt = lua_tostring(L, 1); + ErrorLog("lua: log: " + txt, logtype::lua); + return 0; } diff --git a/scripting/lua.h b/scripting/lua.h index 0e781279..07d72926 100644 --- a/scripting/lua.h +++ b/scripting/lua.h @@ -1,22 +1,49 @@ #pragma once #include +#include class basic_event; +class TMemCell; class TDynamicObject; +struct memcell_values { const char *str; double num1; double num2; }; class lua { lua_State *state; static int atpanic(lua_State *s); - static int openffi(lua_State *s); + + static int scriptapi_event_create(lua_State *L); + static int scriptapi_event_find(lua_State *L); + static int scriptapi_event_exists(lua_State *L); + static int scriptapi_event_getname(lua_State *L); + static int scriptapi_event_dispatch(lua_State *L); + static int scriptapi_event_dispatch_n(lua_State *L); + static int scriptapi_track_find(lua_State *L); + static int scriptapi_track_isoccupied(lua_State *L); + static int scriptapi_track_isoccupied_n(lua_State *L); + static int scriptapi_isolated_find(lua_State *L); + static int scriptapi_isolated_isoccupied(lua_State *L); + static int scriptapi_isolated_isoccupied_n(lua_State *L); + static int scriptapi_train_getname(lua_State *L); + static int scriptapi_dynobj_putvalues(lua_State *L); + static int scriptapi_memcell_find(lua_State *L); + static int scriptapi_memcell_read(lua_State *L); + static int scriptapi_memcell_read_n(lua_State *L); + static int scriptapi_memcell_update(lua_State *L); + static int scriptapi_memcell_update_n(lua_State *L); + static int scriptapi_random(lua_State *L); + static int scriptapi_writelog(lua_State *L); + static int scriptapi_writeerrorlog(lua_State *L); public: lua(); ~lua(); - std::string get_error(); - void interpret(std::string file); - - typedef void (*eventhandler_t)(basic_event*, const TDynamicObject*); + std::string get_error() const; + void interpret(const std::string& file) const; + static void unref(lua_State *L, int ref); + static void dispatch_event(lua_State *L, int handler, basic_event *event, const TDynamicObject *activator); + static void push_memcell_values(lua_State *L, const TMemCell *mc); + static memcell_values get_memcell_values(lua_State *L, int idx); }; diff --git a/scripting/lua_ffi.h b/scripting/lua_ffi.h deleted file mode 100644 index 94105bd6..00000000 --- a/scripting/lua_ffi.h +++ /dev/null @@ -1,96 +0,0 @@ -const char lua_ffi[] = R"STRING( -local ffi = require("ffi") -ffi.cdef[[ -struct memcell_values { const char *str; double num1; double num2; }; - -typedef struct TEvent TEvent; -typedef struct TTrack TTrack; -typedef struct TIsolated TIsolated; -typedef struct TDynamicObject TDynamicObject; -typedef struct TMemCell TMemCell; -typedef struct memcell_values memcell_values; - -TEvent* scriptapi_event_create(const char* name, double delay, double randomdelay, void (*handler)(TEvent*, TDynamicObject*)); -TEvent* scriptapi_event_find(const char* name); -const char* scriptapi_event_getname(TEvent *e); -void scriptapi_event_dispatch(TEvent *e, TDynamicObject *activator, double delay); - -TTrack* scriptapi_track_find(const char* name); -bool scriptapi_track_isoccupied(TTrack *track); - -TIsolated* scriptapi_isolated_find(const char* name); -bool scriptapi_isolated_isoccupied(TIsolated *isolated); - -const char* scriptapi_train_getname(TDynamicObject *dyn); -void scriptapi_dynobj_putvalues(TDynamicObject *dyn, const char *str, double num1, double num2); - -TMemCell* scriptapi_memcell_find(const char *name); -memcell_values scriptapi_memcell_read(TMemCell *mc); -void scriptapi_memcell_update(TMemCell *mc, const char *str, double num1, double num2); - -double scriptapi_random(double a, double b); -void scriptapi_writelog(const char* txt); -void scriptapi_writeerrorlog(const char* txt); -]] - -local ns = ffi.C - -local module = {} - -module.event_create = ns.scriptapi_event_create -function module.event_preparefunc(f) - return ffi.cast("void (*)(TEvent*, TDynamicObject*)", f) -end -module.event_find = ns.scriptapi_event_find -function module.event_getname(a) - return ffi.string(ns.scriptapi_event_getname(a)) -end -module.event_dispatch = ns.scriptapi_event_dispatch -function module.event_dispatch_n(a, b, c) - ns.scriptapi_event_dispatch(ns.scriptapi_event_find(a), b, c) -end - -module.track_find = ns.scriptapi_track_find -module.track_isoccupied = ns.scriptapi_track_isoccupied -function module.track_isoccupied_n(a) - return ns.scriptapi_track_isoccupied(ns.scriptapi_track_find(a)) -end - -module.isolated_find = ns.scriptapi_isolated_find -module.isolated_isoccupied = ns.scriptapi_isolated_isoccupied -function module.isolated_isoccupied_n(a) - return ns.scriptapi_isolated_isoccupied(ns.scriptapi_isolated_find(a)) -end - -function module.train_getname(a) - return ffi.string(ns.scriptapi_train_getname(a)) -end -function module.dynobj_putvalues(a, b) - ns.scriptapi_dynobj_putvalues(a, b.str, b.num1, b.num2) -end - -module.memcell_find = ns.scriptapi_memcell_find -function module.memcell_read(a) - native = ns.scriptapi_memcell_read(a) - mc = { } - mc.str = ffi.string(native.str) - mc.num1 = native.num1 - mc.num2 = native.num2 - return mc -end -function module.memcell_read_n(a) - return module.memcell_read(ns.scriptapi_memcell_find(a)) -end -function module.memcell_update(a, b) - ns.scriptapi_memcell_update(a, b.str, b.num1, b.num2) -end -function module.memcell_update_n(a, b) - ns.scriptapi_memcell_update(ns.scriptapi_memcell_find(a), b.str, b.num1, b.num2) -end - -module.random = ns.scriptapi_random -module.writelog = ns.scriptapi_writelog -module.writeerrorlog = ns.scriptapi_writeerrorlog - -return module; -)STRING"; diff --git a/utilities/Globals.cpp b/utilities/Globals.cpp index 6e456e4d..6116db17 100644 --- a/utilities/Globals.cpp +++ b/utilities/Globals.cpp @@ -24,1039 +24,1276 @@ http://mozilla.org/MPL/2.0/. #include "vao.h" #include -void -global_settings::LoadIniFile(std::string asFileName) { +void global_settings::LoadIniFile(std::string asFileName) +{ + // initialize season data in case the main config file doesn't + std::time_t timenow = std::time(nullptr); - // initialize season data in case the main config file doesn't - std::time_t timenow = std::time( 0 ); - std::tm *localtime = std::localtime( &timenow ); - fMoveLight = localtime->tm_yday + 1; // numer bieżącego dnia w roku - simulation::Environment.compute_season( fMoveLight ); + std::tm tm{}; - cParser parser(asFileName, cParser::buffer_FILE); - ConfigParse(parser); -}; +#ifdef _WIN32 + localtime_s(&tm, &timenow); +#else + localtime_r(&timenow, &tm); +#endif -void -global_settings::ConfigParse(cParser &Parser) { + fMoveLight = tm.tm_yday + 1; // numer bieżącego dnia w roku + simulation::Environment.compute_season(fMoveLight); - std::string token; - do + cParser parser(asFileName, cParser::buffer_FILE); + ConfigParse(parser); +} + +template +static void ParseOne(cParser& parser, T& out, int tokenCount = 1, bool convert = false) +{ + parser.getTokens(tokenCount, convert); + parser >> out; +} + +template +static void ParseOneClamped(cParser& parser, T& out, T minValue, T maxValue, int tokenCount = 1, bool convert = false) +{ + parser.getTokens(tokenCount, convert); + parser >> out; + out = clamp(out, minValue, maxValue); +} + +void global_settings::FinalizeConfig() +{ + if (!bLoadTraction) { - token = ""; - Parser.getTokens(); - Parser >> token; - - if( ConfigParse_gfx( Parser, token ) ) { - continue; - } - else if (token == "sceneryfile") - { - Parser.getTokens(); - Parser >> SceneryFile; - } - else if (token == "humanctrlvehicle") - { - Parser.getTokens(); - Parser >> local_start_vehicle; - } - else if (token == "async.trainThreads") - { - Parser.getTokens(); - Parser >> trainThreads; - } - else if (token == "fieldofview") - { - Parser.getTokens(1, false); - Parser >> FieldOfView; - // guard against incorrect values - FieldOfView = clamp(FieldOfView, 10.0f, 75.0f); - } - else if (token == "width") - { - Parser.getTokens(1, false); - Parser >> window_size.x; - } - else if (token == "height") - { - Parser.getTokens(1, false); - Parser >> window_size.y; - } - else if (token == "heightbase") - { - Parser.getTokens(1, false); - Parser >> fDistanceFactor; - } - else if (token == "targetfps") - { - Parser.getTokens(1, false); - Parser >> targetfps; - } - else if (token == "basedrawrange") - { - Parser.getTokens(1); - Parser >> BaseDrawRange; - } - else if (token == "fullscreen") - { - Parser.getTokens(); - Parser >> bFullScreen; - } - else if (token == "fullscreenmonitor") - { - Parser.getTokens(1, false); - Parser >> fullscreen_monitor; - } - else if (token == "fullscreenwindowed") - { - Parser.getTokens(); - Parser >> fullscreen_windowed; - } - else if (token == "vsync") - { - Parser.getTokens(); - Parser >> VSync; - } - else if (token == "freefly") - { // Mczapkie-130302 - Parser.getTokens(); - Parser >> FreeFlyModeFlag; - Parser.getTokens(3, false); - Parser >> FreeCameraInit[0].x, FreeCameraInit[0].y, FreeCameraInit[0].z; - } - else if (token == "wireframe") - { - Parser.getTokens(); - Parser >> bWireFrame; - } - else if (token == "debugmode") - { // McZapkie! - DebugModeFlag uzywana w mover.pas, - // warto tez blokowac cheaty gdy false - Parser.getTokens(); - Parser >> DebugModeFlag; - } - else if (token == "soundenabled") - { // McZapkie-040302 - blokada dzwieku - przyda - // sie do debugowania oraz na komp. bez karty - // dzw. - Parser.getTokens(); - Parser >> bSoundEnabled; - } - else if (token == "sound.openal.renderer") - { - // selected device for audio renderer - AudioRenderer = Parser.getToken(false); // case-sensitive - } - else if (token == "sound.volume") - { - // selected device for audio renderer - Parser.getTokens(); - Parser >> AudioVolume; - AudioVolume = clamp(AudioVolume, 0.f, 2.f); - } - else if (token == "sound.volume.radio") - { - // selected device for audio renderer - Parser.getTokens(); - Parser >> DefaultRadioVolume; - DefaultRadioVolume = clamp(DefaultRadioVolume, 0.f, 1.f); - } - else if (token == "sound.maxsources") { - Parser.getTokens(); - Parser >> audio_max_sources; - } - else if( token == "sound.volume.vehicle" ) { - Parser.getTokens(); - Parser >> VehicleVolume; - VehicleVolume = clamp(VehicleVolume, 0.f, 1.f); - } - else if (token == "sound.volume.positional") - { - Parser.getTokens(); - Parser >> EnvironmentPositionalVolume; - EnvironmentPositionalVolume = clamp(EnvironmentPositionalVolume, 0.f, 1.f); - } - else if (token == "sound.volume.ambient") - { - Parser.getTokens(); - Parser >> EnvironmentAmbientVolume; - EnvironmentAmbientVolume = clamp(EnvironmentAmbientVolume, 0.f, 1.f); - } - // else if (str==AnsiString("renderalpha")) //McZapkie-1312302 - dwuprzebiegowe renderowanie - // bRenderAlpha=(GetNextSymbol().LowerCase()==AnsiString("yes")); - else if (token == "physicslog") - { // McZapkie-030402 - logowanie parametrow - // fizycznych dla kazdego pojazdu z maszynista - Parser.getTokens(); - Parser >> WriteLogFlag; - } - else if (token == "fullphysics") - { // McZapkie-291103 - usypianie fizyki - Parser.getTokens(); - Parser >> FullPhysics; - } - else if (token == "debuglog") - { - // McZapkie-300402 - wylaczanie log.txt - Parser.getTokens(); - Parser >> token; - if (token == "yes") - { - iWriteLogEnabled = 3; - } - else if (token == "no") - { - iWriteLogEnabled = 0; - } - else - { - iWriteLogEnabled = stol_def(token, 3); - } - } - else if (token == "multiplelogs") - { - Parser.getTokens(); - Parser >> MultipleLogs; - } - else if (token == "shakefactor") - { - Parser.getTokens(); - Parser >> ShakingMultiplierBF; - Parser.getTokens(); - Parser >> ShakingMultiplierRL; - Parser.getTokens(); - Parser >> ShakingMultiplierUD; - } - else if (token == "logs.filter") - { - Parser.getTokens(); - Parser >> DisabledLogTypes; - } - else if (token == "mousescale") - { - // McZapkie-060503 - czulosc ruchu myszy (krecenia glowa) - Parser.getTokens(2, false); - Parser >> fMouseXScale >> fMouseYScale; - } - else if (token == "mousecontrol") - { - // whether control pick mode can be activated - Parser.getTokens(); - Parser >> InputMouse; - } - else if (token == "enabletraction") - { - // Winger 040204 - 'zywe' patyki dostosowujace sie do trakcji; Ra 2014-03: teraz łamanie - Parser.getTokens(); - Parser >> bEnableTraction; - } - else if (token == "loadtraction") - { - // Winger 140404 - ladowanie sie trakcji - Parser.getTokens(); - Parser >> bLoadTraction; - } - else if (token == "friction") - { // mnożnik tarcia - KURS90 - Parser.getTokens(1, false); - Parser >> fFriction; - } - else if (token == "livetraction") - { - // Winger 160404 - zaleznosc napiecia loka od trakcji; - // Ra 2014-03: teraz prąd przy braku sieci - Parser.getTokens(); - Parser >> bLiveTraction; - } - else if (token == "skyenabled") - { - // youBy - niebo - Parser.getTokens(); - Parser >> token; - asSky = (token == "yes" ? "1" : "0"); - } - else if (token == "defaultext") - { - // ShaXbee - domyslne rozszerzenie tekstur - Parser.getTokens(); - Parser >> token; - if (token == "tga") - { - // domyślnie od TGA - szDefaultExt = szTexturesTGA; - } - else - { - szDefaultExt = (token[0] == '.' ? token : "." + token); - } - } - else if (token == "newaircouplers") - { - Parser.getTokens(); - Parser >> bnewAirCouplers; - } - else if (token == "anisotropicfiltering") - { - Parser.getTokens(1, false); - Parser >> AnisotropicFiltering; - if (AnisotropicFiltering < 1.0f) - AnisotropicFiltering = 1.0f; - } - else if (token == "usevbo") - { - Parser.getTokens(); - Parser >> bUseVBO; - } - else if (token == "feedbackmode") - { - Parser.getTokens(1, false); - Parser >> iFeedbackMode; - } - else if (token == "feedbackport") - { - Parser.getTokens(1, false); - Parser >> iFeedbackPort; - } - else if (token == "multiplayer") - { - Parser.getTokens(1, false); - Parser >> iMultiplayer; - } - else if (token == "isolatedtrainnumber") - { - Parser.getTokens(1, false); - Parser >> bIsolatedTrainName; - } - else if (token == "maxtexturesize") - { - // wymuszenie przeskalowania tekstur - Parser.getTokens(1, false); - int size; - Parser >> size; - iMaxTextureSize = clamp_power_of_two(size, 64, 8192); - } - else if (token == "maxcabtexturesize") - { - // wymuszenie przeskalowania tekstur - Parser.getTokens(1, false); - int size; - Parser >> size; - iMaxCabTextureSize = clamp_power_of_two(size, 512, 8192); - } - else if (token == "movelight") - { - // numer dnia w roku albo -1 - Parser.getTokens(1, false); - Parser >> fMoveLight; - if (fMoveLight == 0.f) - { // pobranie daty z systemu - std::time_t timenow = std::time(0); - std::tm *localtime = std::localtime(&timenow); - fMoveLight = localtime->tm_yday + 1; // numer bieżącego dnia w roku - } - simulation::Environment.compute_season(fMoveLight); - } - else if (token == "dynamiclights") - { - // number of dynamic lights in the scene - Parser.getTokens(1, false); - Parser >> DynamicLightCount; - // clamp the light number - // max 8 lights per opengl specs, minus one used for sun. at least one light for - // controlled vehicle - DynamicLightCount = clamp(DynamicLightCount, 0, 7); - } - else if (token == "scenario.time.override") - { - // shift (in hours) applied to train timetables - Parser.getTokens(1, false); - std::string token; - Parser >> token; - std::istringstream stream(token); - if (token.find(':') != -1) - { - float a, b; - char s; - stream >> a >> s >> b; - ScenarioTimeOverride = a + b / 60.0; - } - else - stream >> ScenarioTimeOverride; - ScenarioTimeOverride = clamp(ScenarioTimeOverride, 0.f, 24 * 1439 / 1440.f); - } - else if (token == "scenario.time.offset") - { - // shift (in hours) applied to train timetables - Parser.getTokens(1, false); - Parser >> ScenarioTimeOffset; - } - else if (token == "scenario.time.current") - { - // sync simulation time with local clock - Parser.getTokens(1, false); - Parser >> ScenarioTimeCurrent; - } - else if (token == "scenario.weather.temperature") - { - // selected device for audio renderer - Parser.getTokens(); - Parser >> AirTemperature; - AirTemperature = clamp(AirTemperature, -15.f, 45.f); - } - else if (token == "scalespeculars") - { - // whether strength of specular highlights should be adjusted (generally needed for - // legacy 3d models) - Parser.getTokens(); - Parser >> ScaleSpecularValues; - } - else if (token == "gfxrenderer") - { - // shadow render toggle - Parser.getTokens(); - Parser >> GfxRenderer; - if (GfxRenderer == "full") - { - GfxRenderer = "default"; - } - if (GfxRenderer == "experimental") - { - NvRenderer = true; - GfxRenderer = "experimental"; - } - BasicRenderer = (GfxRenderer == "simple"); - LegacyRenderer = !NvRenderer && (GfxRenderer != "default"); - } - else if (token == "shadows") - { - // shadow render toggle - Parser.getTokens(); - Parser >> RenderShadows; - } - else if (token == "shadowtune") - { - Parser.getTokens(4, false); - float discard; - Parser >> shadowtune.map_size >> discard >> shadowtune.range >> discard; - shadowtune.map_size = clamp_power_of_two(shadowtune.map_size, 512, 8192); - // make sure we actually make effective use of all csm stages - shadowtune.range = - std::max((shadowtune.map_size <= 2048 ? 75.f : 75.f * shadowtune.map_size / 2048), - shadowtune.range); - } - else if (token == "smoothtraction") - { - // podwójna jasność ambient - Parser.getTokens(); - Parser >> bSmoothTraction; - } - else if (token == "splinefidelity") - { - // segment size during spline->geometry conversion - float splinefidelity; - Parser.getTokens(); - Parser >> splinefidelity; - SplineFidelity = clamp(splinefidelity, 1.f, 4.f); - } - else if (token == "rendercab") - { - Parser.getTokens(); - Parser >> render_cab; - } - else if (token == "createswitchtrackbeds") - { - // podwójna jasność ambient - Parser.getTokens(); - Parser >> CreateSwitchTrackbeds; - } - else if (token == "timespeed") - { - // przyspieszenie czasu, zmienna do testów - Parser.getTokens(1, false); - Parser >> default_timespeed; - fTimeSpeed = default_timespeed; - } - else if (token == "deltaoverride") - { - // for debug - Parser.getTokens(1, false); - float deltaoverride; - Parser >> deltaoverride; - Timer::set_delta_override(1.0f / deltaoverride); - } - else if (token == "multisampling") - { - // tryb antyaliasingu: 0=brak,1=2px,2=4px - Parser.getTokens(1, false); - Parser >> iMultisampling; - iMultisampling = clamp(iMultisampling, 0, 4); // liczone 2 ^ n - } - else if (token == "latitude") - { - // szerokość geograficzna - Parser.getTokens(1, false); - Parser >> fLatitudeDeg; - } - else if (token == "convertmodels") - { - // tworzenie plików binarnych - Parser.getTokens(1, false); - Parser >> iConvertModels; - } - else if (token == "convertindexrange") - { - Parser.getTokens(1, false); - Parser >> iConvertIndexRange; - } - else if (token == "file.binary.terrain") - { - // binary terrain (de)serialization - Parser.getTokens(1, false); - Parser >> file_binary_terrain; - } - else if (token == "inactivepause") - { - // automatyczna pauza, gdy okno nieaktywne - Parser.getTokens(); - Parser >> bInactivePause; - } - else if (token == "slowmotion") - { - // tworzenie plików binarnych - Parser.getTokens(1, false); - Parser >> iSlowMotionMask; - } - else if (token == "hideconsole") - { - // hunter-271211: ukrywanie konsoli - Parser.getTokens(); - Parser >> bHideConsole; - } - else if (token == "rollfix") - { - // Ra: poprawianie przechyłki, aby wewnętrzna szyna była "pozioma" - Parser.getTokens(); - Parser >> bRollFix; - } - else if (token == "fpsaverage") - { - // oczekiwana wartość FPS - Parser.getTokens(1, false); - Parser >> fFpsAverage; - } - else if (token == "fpsdeviation") - { - // odchylenie standardowe FPS - Parser.getTokens(1, false); - Parser >> fFpsDeviation; - } - else if (token == "calibratein") - { - // parametry kalibracji wejść - Parser.getTokens(1, false); - int in; - Parser >> in; - if ((in < 0) || (in > 5)) - { - in = 5; // na ostatni, bo i tak trzeba pominąć wartości - } - Parser.getTokens(4, false); - Parser >> fCalibrateIn[in][0] // wyraz wolny - >> fCalibrateIn[in][1] // mnożnik - >> fCalibrateIn[in][2] // mnożnik dla kwadratu - >> fCalibrateIn[in][3]; // mnożnik dla sześcianu - fCalibrateIn[in][4] = 0.0; // mnożnik 4 potęgi - fCalibrateIn[in][5] = 0.0; // mnożnik 5 potęgi - } - else if (token == "calibrate5din") - { - // parametry kalibracji wejść - Parser.getTokens(1, false); - int in; - Parser >> in; - if ((in < 0) || (in > 5)) - { - in = 5; // na ostatni, bo i tak trzeba pominąć wartości - } - Parser.getTokens(6, false); - Parser >> fCalibrateIn[in][0] // wyraz wolny - >> fCalibrateIn[in][1] // mnożnik - >> fCalibrateIn[in][2] // mnożnik dla kwadratu - >> fCalibrateIn[in][3] // mnożnik dla sześcianu - >> fCalibrateIn[in][4] // mnożnik 4 potęgi - >> fCalibrateIn[in][5]; // mnożnik 5 potęgi - } - else if (token == "calibrateout") - { - // parametry kalibracji wyjść - Parser.getTokens(1, false); - int out; - Parser >> out; - if ((out < 0) || (out > 6)) - { - out = 6; // na ostatni, bo i tak trzeba pominąć wartości - } - Parser.getTokens(4, false); - Parser >> fCalibrateOut[out][0] // wyraz wolny - >> fCalibrateOut[out][1] // mnożnik liniowy - >> fCalibrateOut[out][2] // mnożnik dla kwadratu - >> fCalibrateOut[out][3]; // mnożnik dla sześcianu - fCalibrateOut[out][4] = 0.0; // mnożnik dla 4 potęgi - fCalibrateOut[out][5] = 0.0; // mnożnik dla 5 potęgi - } - else if (token == "calibrate5dout") - { - // parametry kalibracji wyjść - Parser.getTokens(1, false); - int out; - Parser >> out; - if ((out < 0) || (out > 6)) - { - out = 6; // na ostatni, bo i tak trzeba pominąć wartości - } - Parser.getTokens(6, false); - Parser >> fCalibrateOut[out][0] // wyraz wolny - >> fCalibrateOut[out][1] // mnożnik liniowy - >> fCalibrateOut[out][2] // mnożnik dla kwadratu - >> fCalibrateOut[out][3] // mnożnik dla sześcianu - >> fCalibrateOut[out][4] // mnożnik dla 4 potęgi - >> fCalibrateOut[out][5]; // mnożnik dla 5 potęgi - } - else if (token == "calibrateoutmaxvalues") - { - // maksymalne wartości jakie można wyświetlić na mierniku - Parser.getTokens(7, false); - Parser >> fCalibrateOutMax[0] >> fCalibrateOutMax[1] >> fCalibrateOutMax[2] >> - fCalibrateOutMax[3] >> fCalibrateOutMax[4] >> fCalibrateOutMax[5] >> - fCalibrateOutMax[6]; - } - else if (token == "calibrateoutdebuginfo") - { - // wyjście z info o przebiegu kalibracji - Parser.getTokens(1, false); - Parser >> iCalibrateOutDebugInfo; - } - else if (token == "pwm") - { - // zmiana numerów wyjść PWM - Parser.getTokens(2, false); - int pwm_out, pwm_no; - Parser >> pwm_out >> pwm_no; - iPoKeysPWM[pwm_out] = pwm_no; - } - else if (token == "brakestep") - { - Parser.getTokens(1, false); - Parser >> fBrakeStep; - } - else if (token == "brakespeed") - { - Parser.getTokens(1, false); - Parser >> brake_speed; - } - else if (token == "joinduplicatedevents") - { - // czy grupować eventy o tych samych nazwach - Parser.getTokens(); - Parser >> bJoinEvents; - } - else if (token == "hiddenevents") - { - // czy łączyć eventy z torami poprzez nazwę toru - Parser.getTokens(1, false); - Parser >> iHiddenEvents; - } - else if (token == "ai.trainman") - { - // czy łączyć eventy z torami poprzez nazwę toru - Parser.getTokens(1, false); - Parser >> AITrainman; - } - else if (token == "pause") - { - // czy po wczytaniu ma być pauza? - Parser.getTokens(); - Parser >> token; - iPause |= (token == "yes" ? 1 : 0); - } - else if (token == "priorityloadtext3d") - { - Parser.getTokens(1); - Parser >> token; - priorityLoadText3D = (token == "yes" ? true : false); - } - else if (token == "lang") - { - // domyślny język - http://tools.ietf.org/html/bcp47 - Parser.getTokens(1, false); - Parser >> asLang; - } - else if( token == "python.updatetime" ) - { - Parser.getTokens(); - Parser >> PythonScreenUpdateRate; - } - else if (token == "uitextcolor") - { - // color of the ui text. NOTE: will be obsolete once the real ui is in place - Parser.getTokens(3, false); - Parser >> UITextColor.r >> UITextColor.g >> UITextColor.b; - glm::clamp(UITextColor, 0.f, 255.f); - UITextColor = UITextColor / 255.f; - UITextColor.a = 1.f; - } - else if (token == "ui.bg.opacity") - { - // czy grupować eventy o tych samych nazwach - Parser.getTokens(); - Parser >> UIBgOpacity; - UIBgOpacity = clamp(UIBgOpacity, 0.f, 1.f); - } - else if (token == "input.gamepad") - { - // czy grupować eventy o tych samych nazwach - Parser.getTokens(); - Parser >> InputGamepad; - } - else if (token == "motiontelemetry") - { - Parser.getTokens(8); - Global.motiontelemetry_conf.enable = true; - Parser >> Global.motiontelemetry_conf.proto; - Parser >> Global.motiontelemetry_conf.address; - Parser >> Global.motiontelemetry_conf.port; - Parser >> Global.motiontelemetry_conf.updatetime; - Parser >> Global.motiontelemetry_conf.includegravity; - Parser >> Global.motiontelemetry_conf.fwdposbased; - Parser >> Global.motiontelemetry_conf.latposbased; - Parser >> Global.motiontelemetry_conf.axlebumpscale; - } - if (token == "screenshotsdir") - { - Parser.getTokens(1); - Parser >> Global.screenshot_dir; - } -#ifdef WITH_UART - else if (token == "uart") - { - uart_conf.enable = true; - Parser.getTokens(3, false); - Parser - >> uart_conf.port - >> uart_conf.baud - >> uart_conf.updatetime; - } - else if (token == "uarttune") - { - Parser.getTokens(18); - Parser - >> uart_conf.mainbrakemin >> uart_conf.mainbrakemax - >> uart_conf.localbrakemin >> uart_conf.localbrakemax - >> uart_conf.tankmax >> uart_conf.tankuart - >> uart_conf.pipemax >> uart_conf.pipeuart - >> uart_conf.brakemax >> uart_conf.brakeuart - >> uart_conf.pantographmax >> uart_conf.pantographuart - >> uart_conf.hvmax >> uart_conf.hvuart - >> uart_conf.currentmax >> uart_conf.currentuart - >> uart_conf.lvmax >> uart_conf.lvuart; - } - else if (token == "uarttachoscale") - { - Parser.getTokens(1); - Parser >> uart_conf.tachoscale; - } - else if (token == "uartfeature") - { - Parser.getTokens(1); - std::string firstToken = Parser.peek(); - - if(firstToken.find('|') != std::string::npos || firstToken == "none" || uartfeatures_map.count(firstToken)) { - // new format (features delimited by pipe) - - // all settings should be disabled initially - for(auto const &x : uartfeatures_map) { - *(x.second) = false; - } - - // and only defined should be enabled - std::string key; - std::stringstream firstTokenStream = std::stringstream(firstToken); - - while(!firstTokenStream.eof()) { - std::getline(firstTokenStream, key, '|'); - - if(uartfeatures_map.count(key)) { - *(uartfeatures_map[key]) = true; - } - } - } else { - // legacy format - - // first token is already parsed - Parser >> uart_conf.mainenable; - - // the rest tokens must be fetched - Parser.getTokens(3); - Parser - >> uart_conf.scndenable - >> uart_conf.trainenable - >> uart_conf.localenable; - } - } - else if( token == "uartdebug" ) { - Parser.getTokens( 1 ); - Parser >> uart_conf.debug; - } - else if (token == "uartmainpercentage") - { - Parser.getTokens(1); - Parser >> uart_conf.mainpercentage; - } -#endif -#ifdef WITH_ZMQ - else if( token == "zmq.address" ) { - Parser.getTokens( 1 ); - Parser >> zmq_address; - } -#endif -#ifdef USE_EXTCAM_CAMERA - else if( token == "extcam.cmd" ) { - Parser.getTokens( 1 ); - Parser >> extcam_cmd; - } - else if( token == "extcam.rec" ) { - Parser.getTokens( 1 ); - Parser >> extcam_rec; - } - else if( token == "extcam.res" ) { - Parser.getTokens( 2 ); - Parser >> extcam_res.x >> extcam_res.y; - } -#endif - else if (token == "loadinglog") { - Parser.getTokens( 1 ); - Parser >> loading_log; - } - else if (token == "ddsupperorigin") { - Parser.getTokens( 1 ); - Parser >> dds_upper_origin; - } - else if (token == "captureonstart") { - Parser.getTokens( 1 ); - Parser >> captureonstart; - } - else if (token == "gui.defaultwindows") { - Parser.getTokens(1); - Parser >> gui_defaultwindows; - } - else if (token == "gui.showtranscripts") { - Parser.getTokens(1); - Parser >> gui_showtranscripts; - } - else if (token == "gui.trainingdefault") { - Parser.getTokens(1); - Parser >> gui_trainingdefault; - } - else if (token == "gfx.angleplatform") - { - Parser.getTokens(1); - Parser >> gfx_angleplatform; - } - else if (token == "gfx.gldebug") - { - Parser.getTokens(1); - Parser >> gfx_gldebug; - } - else if (token == "ui.fontsize") - { - Parser.getTokens(1); - Parser >> ui_fontsize; - } - else if (token == "ui.scale") - { - Parser.getTokens(1); - Parser >> ui_scale; - } - else if (token == "python.enabled") - { - Parser.getTokens(1); - Parser >> python_enabled; - } - else if (token == "python.displaywindows") - { - Parser.getTokens(1); - Parser >> python_displaywindows; - } - else if (token == "python.threadedupload") - { - Parser.getTokens(1); - Parser >> python_threadedupload; - } - else if (token == "python.sharectx") - { - Parser.getTokens(1); - Parser >> python_sharectx; - } - else if (token == "python.uploadmain") - { - Parser.getTokens(1); - Parser >> python_uploadmain; - } - else if (token == "python.fpslimit") - { - Parser.getTokens(1); - float fpslimit; - Parser >> fpslimit; - python_minframetime = std::chrono::duration(1.0f / fpslimit); - } - else if (token == "python.vsync") - { - Parser.getTokens(1); - Parser >> python_vsync; - } - else if (token == "python.mipmaps") - { - Parser.getTokens(1); - Parser >> python_mipmaps; - } - else if (token == "python.viewport") - { - Parser.getTokens(8, false); - - pythonviewport_config conf; - Parser >> conf.surface >> conf.monitor; - Parser >> conf.size.x >> conf.size.y; - Parser >> conf.offset.x >> conf.offset.y; - Parser >> conf.scale.x >> conf.scale.y; - - python_viewports.push_back(conf); - } - else if (token == "extraviewport") - { - Parser.getTokens(3 + 12, false); - - extraviewport_config conf; - Parser >> conf.monitor >> conf.width >> conf.height; - Parser >> conf.draw_range; - - Parser >> conf.projection.pa.x >> conf.projection.pa.y >> conf.projection.pa.z; - Parser >> conf.projection.pb.x >> conf.projection.pb.y >> conf.projection.pb.z; - Parser >> conf.projection.pc.x >> conf.projection.pc.y >> conf.projection.pc.z; - Parser >> conf.projection.pe.x >> conf.projection.pe.y >> conf.projection.pe.z; - - extra_viewports.push_back(conf); - if (gl::vao::use_vao && conf.monitor != "MAIN") { - gl::vao::use_vao = false; - WriteLog("using multiple windows, disabling vao!"); - } - } - else if (token == "map.highlightdistance") { - Parser.getTokens(1); - Parser >> map_highlight_distance; - } - else if (token == "headtrack") { - Parser.getTokens(14); - Parser >> headtrack_conf.joy; - Parser >> headtrack_conf.magic_window; - Parser >> headtrack_conf.move_axes[0] >> headtrack_conf.move_axes[1] >> headtrack_conf.move_axes[2]; - Parser >> headtrack_conf.move_mul[0] >> headtrack_conf.move_mul[1] >> headtrack_conf.move_mul[2]; - Parser >> headtrack_conf.rot_axes[0] >> headtrack_conf.rot_axes[1] >> headtrack_conf.rot_axes[2]; - Parser >> headtrack_conf.rot_mul[0] >> headtrack_conf.rot_mul[1] >> headtrack_conf.rot_mul[2]; - } - else if (token == "vr.enabled") { - Parser.getTokens(1); - Parser >> vr; - } - else if (token == "vr.backend") { - Parser.getTokens(1); - Parser >> vr_backend; - } - else if (token == "compresstex") - { - Parser.getTokens(1); - if (false == gfx_usegles) - { - // ogl es use requires compression to be disabled - Parser >> compress_tex; - } - } - else if (token == "fpslimit") - { - Parser.getTokens(1); - float fpslimit; - Parser >> fpslimit; - minframetime = std::chrono::duration(1.0f / fpslimit); - } - else if (token == "randomseed") - { - Parser.getTokens(1); - Parser >> Global.random_seed; - } - else if (token == "network.server") - { - Parser.getTokens(2); - - std::string backend; - std::string conf; - Parser >> backend >> conf; - - network_servers.push_back(std::make_pair(backend, conf)); - } - else if (token == "network.client") - { - Parser.getTokens(2); - - network_client.emplace(); - Parser >> network_client->first; - Parser >> network_client->second; - } - else if (token == "execonexit") { - Parser.getTokens(1); - Parser >> exec_on_exit; - std::replace(std::begin(exec_on_exit), std::end(exec_on_exit), '_', ' '); - } - else if (token == "map.manualswitchcontrol") { - Parser.getTokens(1); - Parser >> map_manualswitchcontrol; - } - else if (token == "prepend_scn") { - Parser.getTokens(1); - Parser >> prepend_scn; - } - else if (token == "crashdamage") { - Parser.getTokens(1); - Parser >> crash_damage; - } - - } while ((token != "") && (token != "endconfig")); //(!Parser->EndOfFile) - // na koniec trochę zależności - if (!bLoadTraction) // wczytywanie drutów i słupów - { // tutaj wyłączenie, bo mogą nie być zdefiniowane w INI - bEnableTraction = false; // false = pantograf się nie połamie - bLiveTraction = false; // false = pantografy zawsze zbierają 95% MaxVoltage + bEnableTraction = false; + bLiveTraction = false; } - if (vr) { + + if (vr) + { gfx_skippipeline = false; VSync = false; } - /* - fFpsMin = fFpsAverage - - fFpsDeviation; // dolna granica FPS, przy której promień scenerii będzie - zmniejszany fFpsMax = fFpsAverage + fFpsDeviation; // górna granica FPS, przy której promień - scenerii będzie zwiększany - */ + if (iPause) - iTextMode = GLFW_KEY_F1; // jak pauza, to pokazać zegar + iTextMode = GLFW_KEY_F1; #ifndef WITH_PYTHON - python_enabled = false; + python_enabled = false; #endif #ifdef _WIN32 - Console::ModeSet(iFeedbackMode, iFeedbackPort); // tryb pracy konsoli sterowniczej + Console::ModeSet(iFeedbackMode, iFeedbackPort); #endif } +bool global_settings::ConfigParseGeneral(cParser& Parser, const std::string& token) +{ + if (token == "sceneryfile") + { + ParseOne(Parser, SceneryFile); + return true; + } + + if (token == "humanctrlvehicle") + { + ParseOne(Parser, local_start_vehicle); + return true; + } + + if (token == "width") + { + ParseOne(Parser, window_size.x, 1, false); + return true; + } + + if (token == "height") + { + ParseOne(Parser, window_size.y, 1, false); + return true; + } + + if (token == "fullscreen") + { + ParseOne(Parser, bFullScreen); + return true; + } + + if (token == "fullscreenmonitor") + { + ParseOne(Parser, fullscreen_monitor, 1, false); + return true; + } + + if (token == "fullscreenwindowed") + { + ParseOne(Parser, fullscreen_windowed); + return true; + } + + if (token == "hideconsole") + { + ParseOne(Parser, bHideConsole); + return true; + } + + if (token == "lang") + { + ParseOne(Parser, asLang, 1, false); + return true; + } + + if (token == "screenshotsdir") + { + ParseOne(Parser, Global.screenshot_dir, 1); + return true; + } + + if (token == "execonexit") + { + ParseOne(Parser, exec_on_exit, 1); + std::replace(std::begin(exec_on_exit), std::end(exec_on_exit), '_', ' '); + return true; + } + + if (token == "prepend_scn") + { + ParseOne(Parser, prepend_scn, 1); + return true; + } + + return false; +} + +bool global_settings::ConfigParseAudio(cParser& Parser, const std::string& token) +{ + if (token == "soundenabled") + { + ParseOne(Parser, bSoundEnabled); + return true; + } + + if (token == "sound.openal.renderer") + { + AudioRenderer = Parser.getToken(false); + return true; + } + + if (token == "sound.volume") + { + ParseOneClamped(Parser, AudioVolume, 0.f, 2.f); + return true; + } + + if (token == "sound.volume.radio") + { + ParseOneClamped(Parser, DefaultRadioVolume, 0.f, 1.f); + return true; + } + + if (token == "sound.maxsources") + { + ParseOne(Parser, audio_max_sources); + return true; + } + + if (token == "sound.volume.vehicle") + { + ParseOneClamped(Parser, VehicleVolume, 0.f, 1.f); + return true; + } + + if (token == "sound.volume.positional") + { + ParseOneClamped(Parser, EnvironmentPositionalVolume, 0.f, 1.f); + return true; + } + + if (token == "sound.volume.ambient") + { + ParseOneClamped(Parser, EnvironmentAmbientVolume, 0.f, 1.f); + return true; + } + + return false; +} + +bool global_settings::ConfigParseGraphics(cParser& Parser, const std::string& token) +{ + if (token == "fieldofview") + { + ParseOneClamped(Parser, FieldOfView, 10.0f, 75.0f, 1, false); + return true; + } + + if (token == "heightbase") + { + ParseOne(Parser, fDistanceFactor, 1, false); + return true; + } + + if (token == "basedrawrange") + { + ParseOne(Parser, BaseDrawRange, 1); + return true; + } + + if (token == "vsync") + { + ParseOne(Parser, VSync); + return true; + } + + if (token == "wireframe") + { + ParseOne(Parser, bWireFrame); + return true; + } + + if (token == "skyenabled") + { + std::string value; + ParseOne(Parser, value); + asSky = (value == "yes" ? "1" : "0"); + return true; + } + + if (token == "defaultext") + { + std::string value; + ParseOne(Parser, value); + + if (value == "tga") + szDefaultExt = szTexturesTGA; + else + szDefaultExt = (value[0] == '.' ? value : "." + value); + + return true; + } + + if (token == "anisotropicfiltering") + { + ParseOne(Parser, AnisotropicFiltering, 1, false); + if (AnisotropicFiltering < 1.0f) + AnisotropicFiltering = 1.0f; + return true; + } + + if (token == "usevbo") + { + ParseOne(Parser, bUseVBO); + return true; + } + + if (token == "maxtexturesize") + { + int size = 0; + ParseOne(Parser, size, 1, false); + iMaxTextureSize = clamp_power_of_two(size, 64, 8192); + return true; + } + + if (token == "maxcabtexturesize") + { + int size = 0; + ParseOne(Parser, size, 1, false); + iMaxCabTextureSize = clamp_power_of_two(size, 512, 8192); + return true; + } + + if (token == "dynamiclights") + { + ParseOne(Parser, DynamicLightCount, 1, false); + DynamicLightCount = clamp(DynamicLightCount, 0, 7); + return true; + } + + if (token == "gfxrenderer") + { + ParseOne(Parser, GfxRenderer); + + if (GfxRenderer == "full") + GfxRenderer = "default"; + + if (GfxRenderer == "experimental") + { + NvRenderer = true; + GfxRenderer = "experimental"; + } + + BasicRenderer = (GfxRenderer == "simple"); + LegacyRenderer = !NvRenderer && (GfxRenderer != "default"); + return true; + } + + if (token == "shadows") + { + ParseOne(Parser, RenderShadows); + return true; + } + + if (token == "shadowtune") + { + Parser.getTokens(4, false); + + float discard = 0.f; + Parser >> shadowtune.map_size >> discard >> shadowtune.range >> discard; + + shadowtune.map_size = clamp_power_of_two(shadowtune.map_size, 512, 8192); + shadowtune.range = + std::max((shadowtune.map_size <= 2048 ? 75.f : 75.f * shadowtune.map_size / 2048), + shadowtune.range); + return true; + } + + if (token == "scalespeculars") + { + ParseOne(Parser, ScaleSpecularValues); + return true; + } + + if (token == "rendercab") + { + ParseOne(Parser, render_cab); + return true; + } + + if (token == "multisampling") + { + ParseOne(Parser, iMultisampling, 1, false); + iMultisampling = clamp(iMultisampling, 0, 4); + return true; + } + + if (token == "compresstex") + { + Parser.getTokens(1); + if (!gfx_usegles) + Parser >> compress_tex; + return true; + } + + if (token == "gfx.angleplatform") + { + ParseOne(Parser, gfx_angleplatform, 1); + return true; + } + + if (token == "gfx.gldebug") + { + ParseOne(Parser, gfx_gldebug, 1); + return true; + } + + if (token == "extraviewport") + { + Parser.getTokens(15, false); + + extraviewport_config conf; + Parser >> conf.monitor >> conf.width >> conf.height + >> conf.draw_range + >> conf.projection.pa.x >> conf.projection.pa.y >> conf.projection.pa.z + >> conf.projection.pb.x >> conf.projection.pb.y >> conf.projection.pb.z + >> conf.projection.pc.x >> conf.projection.pc.y >> conf.projection.pc.z + >> conf.projection.pe.x >> conf.projection.pe.y >> conf.projection.pe.z; + + extra_viewports.push_back(conf); + + if (gl::vao::use_vao && conf.monitor != "MAIN") + { + gl::vao::use_vao = false; + WriteLog("using multiple windows, disabling vao!"); + } + return true; + } + + return false; +} + +bool global_settings::ConfigParseInput(cParser& Parser, const std::string& token) +{ + if (token == "freefly") + { + ParseOne(Parser, FreeFlyModeFlag); + Parser.getTokens(3, false); + Parser >> FreeCameraInit[0].x >> FreeCameraInit[0].y >> FreeCameraInit[0].z; + return true; + } + + if (token == "mousescale") + { + Parser.getTokens(2, false); + Parser >> fMouseXScale >> fMouseYScale; + return true; + } + + if (token == "mousecontrol") + { + ParseOne(Parser, InputMouse); + return true; + } + + if (token == "input.gamepad") + { + ParseOne(Parser, InputGamepad); + return true; + } + + if (token == "headtrack") + { + Parser.getTokens(14); + Parser >> headtrack_conf.joy + >> headtrack_conf.magic_window + >> headtrack_conf.move_axes[0] >> headtrack_conf.move_axes[1] >> headtrack_conf.move_axes[2] + >> headtrack_conf.move_mul[0] >> headtrack_conf.move_mul[1] >> headtrack_conf.move_mul[2] + >> headtrack_conf.rot_axes[0] >> headtrack_conf.rot_axes[1] >> headtrack_conf.rot_axes[2] + >> headtrack_conf.rot_mul[0] >> headtrack_conf.rot_mul[1] >> headtrack_conf.rot_mul[2]; + return true; + } + + return false; +} + +bool global_settings::ConfigParseSimulation(cParser& Parser, const std::string& token) +{ + if (token == "async.trainThreads") + { + ParseOne(Parser, trainThreads); + return true; + } + + if (token == "physicslog") + { + ParseOne(Parser, WriteLogFlag); + return true; + } + + if (token == "fullphysics") + { + ParseOne(Parser, FullPhysics); + return true; + } + + if (token == "enabletraction") + { + ParseOne(Parser, bEnableTraction); + return true; + } + + if (token == "loadtraction") + { + ParseOne(Parser, bLoadTraction); + return true; + } + + if (token == "friction") + { + ParseOne(Parser, fFriction, 1, false); + return true; + } + + if (token == "livetraction") + { + ParseOne(Parser, bLiveTraction); + return true; + } + + if (token == "newaircouplers") + { + ParseOne(Parser, bnewAirCouplers); + return true; + } + + if (token == "movelight") + { + ParseOne(Parser, fMoveLight, 1, false); + + if (fMoveLight == 0.f) + { + std::time_t timenow = std::time(nullptr); + std::tm tm{}; + +#ifdef _WIN32 + localtime_s(&tm, &timenow); +#else + localtime_r(&timenow, &tm); +#endif + + fMoveLight = tm.tm_yday + 1; + } + + simulation::Environment.compute_season(fMoveLight); + return true; + } + + if (token == "scenario.time.override") + { + Parser.getTokens(1, false); + + std::string value; + Parser >> value; + + std::istringstream stream(value); + + if (value.find(':') != std::string::npos) + { + float hours = 0.f; + float minutes = 0.f; + char sep = '\0'; + + stream >> hours >> sep >> minutes; + ScenarioTimeOverride = hours + minutes / 60.0f; + } + else + { + stream >> ScenarioTimeOverride; + } + + ScenarioTimeOverride = clamp(ScenarioTimeOverride, 0.f, 24 * 1439 / 1440.f); + return true; + } + + if (token == "scenario.time.offset") + { + ParseOne(Parser, ScenarioTimeOffset, 1, false); + return true; + } + + if (token == "scenario.time.current") + { + ParseOne(Parser, ScenarioTimeCurrent, 1, false); + return true; + } + + if (token == "scenario.weather.temperature") + { + ParseOneClamped(Parser, AirTemperature, -15.f, 45.f); + return true; + } + + if (token == "smoothtraction") + { + ParseOne(Parser, bSmoothTraction); + return true; + } + + if (token == "splinefidelity") + { + float splinefidelity = 0.f; + ParseOne(Parser, splinefidelity); + SplineFidelity = clamp(splinefidelity, 1.f, 4.f); + return true; + } + + if (token == "createswitchtrackbeds") + { + ParseOne(Parser, CreateSwitchTrackbeds); + return true; + } + + if (token == "timespeed") + { + ParseOne(Parser, default_timespeed, 1, false); + fTimeSpeed = default_timespeed; + return true; + } + + if (token == "deltaoverride") + { + float deltaoverride = 0.f; + ParseOne(Parser, deltaoverride, 1, false); + + if (deltaoverride > 0.f) + Timer::set_delta_override(1.0f / deltaoverride); + return true; + } + + if (token == "latitude") + { + ParseOne(Parser, fLatitudeDeg, 1, false); + return true; + } + + if (token == "convertmodels") + { + ParseOne(Parser, iConvertModels, 1, false); + return true; + } + + if (token == "convertindexrange") + { + ParseOne(Parser, iConvertIndexRange, 1, false); + return true; + } + + if (token == "file.binary.terrain") + { + ParseOne(Parser, file_binary_terrain, 1, false); + return true; + } + + if (token == "inactivepause") + { + ParseOne(Parser, bInactivePause); + return true; + } + + if (token == "slowmotion") + { + ParseOne(Parser, iSlowMotionMask, 1, false); + return true; + } + + if (token == "fpsaverage") + { + ParseOne(Parser, fFpsAverage, 1, false); + return true; + } + + if (token == "fpsdeviation") + { + ParseOne(Parser, fFpsDeviation, 1, false); + return true; + } + + if (token == "brakestep") + { + ParseOne(Parser, fBrakeStep, 1, false); + return true; + } + + if (token == "brakespeed") + { + ParseOne(Parser, brake_speed, 1, false); + return true; + } + + if (token == "joinduplicatedevents") + { + ParseOne(Parser, bJoinEvents); + return true; + } + + if (token == "hiddenevents") + { + ParseOne(Parser, iHiddenEvents, 1, false); + return true; + } + + if (token == "ai.trainman") + { + ParseOne(Parser, AITrainman, 1, false); + return true; + } + + if (token == "pause") + { + std::string value; + ParseOne(Parser, value); + iPause |= (value == "yes" ? 1 : 0); + return true; + } + + if (token == "priorityloadtext3d") + { + std::string value; + ParseOne(Parser, value, 1); + priorityLoadText3D = (value == "yes"); + return true; + } + + if (token == "fpslimit") + { + float fpslimit = 0.f; + ParseOne(Parser, fpslimit, 1); + + if (fpslimit > 0.f) + minframetime = std::chrono::duration(1.0f / fpslimit); + return true; + } + + if (token == "randomseed") + { + ParseOne(Parser, Global.random_seed, 1); + return true; + } + + if (token == "map.manualswitchcontrol") + { + ParseOne(Parser, map_manualswitchcontrol, 1); + return true; + } + + if (token == "map.highlightdistance") + { + ParseOne(Parser, map_highlight_distance, 1); + return true; + } + + if (token == "crashdamage") + { + ParseOne(Parser, crash_damage, 1); + return true; + } + + return false; +} + +bool global_settings::ConfigParseUI(cParser& Parser, const std::string& token) +{ + if (token == "uitextcolor") + { + Parser.getTokens(3, false); + Parser >> UITextColor.r >> UITextColor.g >> UITextColor.b; + UITextColor = glm::clamp(UITextColor, 0.f, 255.f); + UITextColor = UITextColor / 255.f; + UITextColor.a = 1.f; + return true; + } + + if (token == "ui.bg.opacity") + { + ParseOneClamped(Parser, UIBgOpacity, 0.f, 1.f); + return true; + } + + if (token == "ui.fontsize") + { + ParseOne(Parser, ui_fontsize, 1); + return true; + } + + if (token == "ui.scale") + { + ParseOne(Parser, ui_scale, 1); + return true; + } + + if (token == "gui.defaultwindows") + { + ParseOne(Parser, gui_defaultwindows, 1); + return true; + } + + if (token == "gui.showtranscripts") + { + ParseOne(Parser, gui_showtranscripts, 1); + return true; + } + + if (token == "gui.trainingdefault") + { + ParseOne(Parser, gui_trainingdefault, 1); + return true; + } + + return false; +} + +bool global_settings::ConfigParsePython(cParser& Parser, const std::string& token) +{ + if (token == "python.enabled") + { + ParseOne(Parser, python_enabled, 1); + return true; + } + + if (token == "python.updatetime") + { + ParseOne(Parser, PythonScreenUpdateRate); + return true; + } + + if (token == "python.displaywindows") + { + ParseOne(Parser, python_displaywindows, 1); + return true; + } + + if (token == "python.threadedupload") + { + ParseOne(Parser, python_threadedupload, 1); + return true; + } + + if (token == "python.sharectx") + { + ParseOne(Parser, python_sharectx, 1); + return true; + } + + if (token == "python.uploadmain") + { + ParseOne(Parser, python_uploadmain, 1); + return true; + } + + if (token == "python.fpslimit") + { + float fpslimit = 0.f; + ParseOne(Parser, fpslimit, 1); + + if (fpslimit > 0.f) + python_minframetime = std::chrono::duration(1.0f / fpslimit); + return true; + } + + if (token == "python.vsync") + { + ParseOne(Parser, python_vsync, 1); + return true; + } + + if (token == "python.mipmaps") + { + ParseOne(Parser, python_mipmaps, 1); + return true; + } + + if (token == "python.viewport") + { + Parser.getTokens(8, false); + + pythonviewport_config conf; + Parser >> conf.surface >> conf.monitor + >> conf.size.x >> conf.size.y + >> conf.offset.x >> conf.offset.y + >> conf.scale.x >> conf.scale.y; + + python_viewports.push_back(conf); + return true; + } + + return false; +} + +bool global_settings::ConfigParseNetwork(cParser& Parser, const std::string& token) +{ + if (token == "network.server") + { + Parser.getTokens(2); + + std::string backend; + std::string conf; + Parser >> backend >> conf; + + network_servers.emplace_back(backend, conf); + return true; + } + + if (token == "network.client") + { + Parser.getTokens(2); + + network_client.emplace(); + Parser >> network_client->first >> network_client->second; + return true; + } + + return false; +} + +bool global_settings::ConfigParseHardware(cParser& Parser, const std::string& token) +{ + if (token == "feedbackmode") + { + ParseOne(Parser, iFeedbackMode, 1, false); + return true; + } + + if (token == "feedbackport") + { + ParseOne(Parser, iFeedbackPort, 1, false); + return true; + } + + if (token == "multiplayer") + { + ParseOne(Parser, iMultiplayer, 1, false); + return true; + } + + if (token == "isolatedtrainnumber") + { + ParseOne(Parser, bIsolatedTrainName, 1, false); + return true; + } + + if (token == "motiontelemetry") + { + Parser.getTokens(8); + Global.motiontelemetry_conf.enable = true; + Parser >> Global.motiontelemetry_conf.proto + >> Global.motiontelemetry_conf.address + >> Global.motiontelemetry_conf.port + >> Global.motiontelemetry_conf.updatetime + >> Global.motiontelemetry_conf.includegravity + >> Global.motiontelemetry_conf.fwdposbased + >> Global.motiontelemetry_conf.latposbased + >> Global.motiontelemetry_conf.axlebumpscale; + return true; + } + + if (token == "vr.enabled") + { + ParseOne(Parser, vr, 1); + return true; + } + + if (token == "vr.backend") + { + ParseOne(Parser, vr_backend, 1); + return true; + } + +#ifdef WITH_UART + if (token == "uart") + { + uart_conf.enable = true; + Parser.getTokens(3, false); + Parser >> uart_conf.port + >> uart_conf.baud + >> uart_conf.updatetime; + return true; + } + + if (token == "uarttune") + { + Parser.getTokens(18); + Parser >> uart_conf.mainbrakemin >> uart_conf.mainbrakemax + >> uart_conf.localbrakemin >> uart_conf.localbrakemax + >> uart_conf.tankmax >> uart_conf.tankuart + >> uart_conf.pipemax >> uart_conf.pipeuart + >> uart_conf.brakemax >> uart_conf.brakeuart + >> uart_conf.pantographmax >> uart_conf.pantographuart + >> uart_conf.hvmax >> uart_conf.hvuart + >> uart_conf.currentmax >> uart_conf.currentuart + >> uart_conf.lvmax >> uart_conf.lvuart; + return true; + } + + if (token == "uarttachoscale") + { + ParseOne(Parser, uart_conf.tachoscale, 1); + return true; + } + + if (token == "uartfeature") + { + Parser.getTokens(1); + std::string firstToken = Parser.peek(); + + if (firstToken.find('|') != std::string::npos || firstToken == "none" || uartfeatures_map.count(firstToken)) + { + for (auto const& x : uartfeatures_map) + { + *(x.second) = false; + } + + std::string key; + std::stringstream firstTokenStream(firstToken); + + while (!firstTokenStream.eof()) + { + std::getline(firstTokenStream, key, '|'); + + if (uartfeatures_map.count(key)) + { + *(uartfeatures_map[key]) = true; + } + } + } + else + { + Parser >> uart_conf.mainenable; + Parser.getTokens(3); + Parser >> uart_conf.scndenable + >> uart_conf.trainenable + >> uart_conf.localenable; + } + + return true; + } + + if (token == "uartdebug") + { + ParseOne(Parser, uart_conf.debug, 1); + return true; + } + + if (token == "uartmainpercentage") + { + ParseOne(Parser, uart_conf.mainpercentage, 1); + return true; + } +#endif + +#ifdef WITH_ZMQ + if (token == "zmq.address") + { + ParseOne(Parser, zmq_address, 1); + return true; + } +#endif + +#ifdef USE_EXTCAM_CAMERA + if (token == "extcam.cmd") + { + ParseOne(Parser, extcam_cmd, 1); + return true; + } + + if (token == "extcam.rec") + { + ParseOne(Parser, extcam_rec, 1); + return true; + } + + if (token == "extcam.res") + { + Parser.getTokens(2); + Parser >> extcam_res.x >> extcam_res.y; + return true; + } +#endif + + return false; +} + +bool global_settings::ConfigParseDebug(cParser& Parser, const std::string& token) +{ + if (token == "debugmode") + { + ParseOne(Parser, DebugModeFlag); + return true; + } + + if (token == "debuglog") + { + std::string value; + ParseOne(Parser, value); + + if (value == "yes") + iWriteLogEnabled = 3; + else if (value == "no") + iWriteLogEnabled = 0; + else + iWriteLogEnabled = stol_def(value, 3); + + return true; + } + + if (token == "multiplelogs") + { + ParseOne(Parser, MultipleLogs); + return true; + } + + if (token == "shakefactor") + { + ParseOne(Parser, ShakingMultiplierBF); + ParseOne(Parser, ShakingMultiplierRL); + ParseOne(Parser, ShakingMultiplierUD); + return true; + } + + if (token == "logs.filter") + { + ParseOne(Parser, DisabledLogTypes); + return true; + } + + if (token == "rollfix") + { + ParseOne(Parser, bRollFix); + return true; + } + + if (token == "loadinglog") + { + ParseOne(Parser, loading_log, 1); + return true; + } + + if (token == "captureonstart") + { + ParseOne(Parser, captureonstart, 1); + return true; + } + + if (token == "ddsupperorigin") + { + ParseOne(Parser, dds_upper_origin, 1); + return true; + } + + if (token == "calibratein") + { + Parser.getTokens(1, false); + + int in = 0; + Parser >> in; + + if ((in < 0) || (in > 5)) + in = 5; + + Parser.getTokens(4, false); + Parser >> fCalibrateIn[in][0] + >> fCalibrateIn[in][1] + >> fCalibrateIn[in][2] + >> fCalibrateIn[in][3]; + + fCalibrateIn[in][4] = 0.0; + fCalibrateIn[in][5] = 0.0; + return true; + } + + if (token == "calibrate5din") + { + Parser.getTokens(1, false); + + int in = 0; + Parser >> in; + + if ((in < 0) || (in > 5)) + in = 5; + + Parser.getTokens(6, false); + Parser >> fCalibrateIn[in][0] + >> fCalibrateIn[in][1] + >> fCalibrateIn[in][2] + >> fCalibrateIn[in][3] + >> fCalibrateIn[in][4] + >> fCalibrateIn[in][5]; + return true; + } + + if (token == "calibrateout") + { + Parser.getTokens(1, false); + + int out = 0; + Parser >> out; + + if ((out < 0) || (out > 6)) + out = 6; + + Parser.getTokens(4, false); + Parser >> fCalibrateOut[out][0] + >> fCalibrateOut[out][1] + >> fCalibrateOut[out][2] + >> fCalibrateOut[out][3]; + + fCalibrateOut[out][4] = 0.0; + fCalibrateOut[out][5] = 0.0; + return true; + } + + if (token == "calibrate5dout") + { + Parser.getTokens(1, false); + + int out = 0; + Parser >> out; + + if ((out < 0) || (out > 6)) + out = 6; + + Parser.getTokens(6, false); + Parser >> fCalibrateOut[out][0] + >> fCalibrateOut[out][1] + >> fCalibrateOut[out][2] + >> fCalibrateOut[out][3] + >> fCalibrateOut[out][4] + >> fCalibrateOut[out][5]; + return true; + } + + if (token == "calibrateoutmaxvalues") + { + Parser.getTokens(7, false); + Parser >> fCalibrateOutMax[0] + >> fCalibrateOutMax[1] + >> fCalibrateOutMax[2] + >> fCalibrateOutMax[3] + >> fCalibrateOutMax[4] + >> fCalibrateOutMax[5] + >> fCalibrateOutMax[6]; + return true; + } + + if (token == "calibrateoutdebuginfo") + { + ParseOne(Parser, iCalibrateOutDebugInfo, 1, false); + return true; + } + + if (token == "pwm") + { + Parser.getTokens(2, false); + + int pwm_out = 0; + int pwm_no = 0; + Parser >> pwm_out >> pwm_no; + + iPoKeysPWM[pwm_out] = pwm_no; + return true; + } + + return false; +} + +void global_settings::ConfigParse(cParser& Parser) +{ + std::string token; + + do + { + token.clear(); + Parser.getTokens(); + Parser >> token; + + if (token.empty() || token == "endconfig") + break; + + if (ConfigParse_gfx(Parser, token)) + continue; + + if (ConfigParseGeneral(Parser, token)) + continue; + + if (ConfigParseAudio(Parser, token)) + continue; + + if (ConfigParseGraphics(Parser, token)) + continue; + + if (ConfigParseInput(Parser, token)) + continue; + + if (ConfigParseSimulation(Parser, token)) + continue; + + if (ConfigParseUI(Parser, token)) + continue; + + if (ConfigParsePython(Parser, token)) + continue; + + if (ConfigParseNetwork(Parser, token)) + continue; + + if (ConfigParseHardware(Parser, token)) + continue; + + if (ConfigParseDebug(Parser, token)) + continue; + + // WriteLog(std::format("Unknown config token: {}", token), logtype::warning, false); + + } while (true); + + FinalizeConfig(); +} + bool global_settings::ConfigParse_gfx( cParser &Parser, std::string_view const Token ) { diff --git a/utilities/Globals.h b/utilities/Globals.h index 21308af8..6e6e9197 100644 --- a/utilities/Globals.h +++ b/utilities/Globals.h @@ -67,7 +67,7 @@ struct global_settings { int iPause{ 0 }; // globalna pauza ruchu: b0=start,b1=klawisz,b2=tło,b3=lagi,b4=wczytywanie float AirTemperature{ 15.f }; std::string asCurrentSceneryPath{ "scenery/" }; - std::string asCurrentTexturePath{ szTexturePath }; + std::string asCurrentTexturePath{ paths::textures }; std::string asCurrentDynamicPath; int CurrentMaxTextureSize{ 4096 }; bool UpdateMaterials{ true }; @@ -345,10 +345,21 @@ struct global_settings { float m_skysaturationcorrection{ 1.65f }; float m_skyhuecorrection{ 0.5f }; -// methods - void LoadIniFile( std::string asFileName ); - void ConfigParse( cParser &parser ); - bool ConfigParse_gfx( cParser &parser, std::string_view const Token ); + // methods + void LoadIniFile( std::string asFileName ); + void FinalizeConfig(); + void ConfigParse(cParser &parser); + bool ConfigParseGeneral(cParser& Parser, const std::string& token); + bool ConfigParseAudio(cParser& Parser, const std::string& token); + bool ConfigParseGraphics(cParser& Parser, const std::string& token); + bool ConfigParseInput(cParser& Parser, const std::string& token); + bool ConfigParseSimulation(cParser& Parser, const std::string& token); + bool ConfigParseUI(cParser& Parser, const std::string& token); + bool ConfigParsePython(cParser& Parser, const std::string& token); + bool ConfigParseNetwork(cParser& Parser, const std::string& token); + bool ConfigParseHardware(cParser& Parser, const std::string& token); + bool ConfigParseDebug(cParser& Parser, const std::string& token); + bool ConfigParse_gfx( cParser &parser, std::string_view const Token ); // sends basic content of the class in legacy (text) format to provided stream void export_as_text( std::ostream &Output ) const; diff --git a/utilities/Logs.cpp b/utilities/Logs.cpp index b6a09a8d..48d74317 100644 --- a/utilities/Logs.cpp +++ b/utilities/Logs.cpp @@ -158,55 +158,57 @@ void LogService() } -void WriteLog(const char *str, logtype const Type, bool isError) +bool ShouldSkipLog(std::string_view str, logtype type) { - if (!str || *str == '\0') - return; - if (TestFlag(Global.DisabledLogTypes, static_cast(Type))) - return; - - // time calculation - auto now = std::chrono::steady_clock::now(); - auto elapsed = now - Global.startTimestamp; - double seconds = std::chrono::duration_cast>(elapsed).count(); - - // time format - std::ostringstream oss; - oss << "[ " << std::fixed << std::setprecision(3) << seconds << " ] "; - - // wyrownanie do np. 10 znaków długości + dwie tabulacje - std::ostringstream final; - final << std::setw(10) << oss.str() << "\t\t" << str; - - - logMutex.lock(); - InfoStack.emplace_back(final.str(), isError); - logMutex.unlock(); + return str.empty() || + TestFlag(Global.DisabledLogTypes, static_cast(type)); } -void ErrorLog(const char *str, logtype const Type) +std::string FormatLogMessage(std::string_view str) { - if (!str || *str == '\0') - return; - if (TestFlag(Global.DisabledLogTypes, static_cast(Type))) + const auto now = std::chrono::steady_clock::now(); + const auto elapsed = now - Global.startTimestamp; + const double seconds = std::chrono::duration(elapsed).count(); + + return std::format("[ {:8.3f} ]\t\t{}", seconds, str); +} + +void WriteLog(std::string_view str, logtype type, bool isError) +{ + if (ShouldSkipLog(str, type)) return; - // time calculation - auto now = std::chrono::steady_clock::now(); - auto elapsed = now - Global.startTimestamp; - double seconds = std::chrono::duration_cast>(elapsed).count(); + const auto message = FormatLogMessage(str); - // time format - std::ostringstream oss; - oss << "[ " << std::fixed << std::setprecision(3) << seconds << " ] "; + std::lock_guard lock(logMutex); + InfoStack.push_back({message, isError}); +} - // wyrownanie do np. 10 znaków długości + dwie tabulacje - std::ostringstream final; - final << std::setw(10) << oss.str() << "\t\t" << str; +void ErrorLog(std::string_view str, logtype type) +{ + if (ShouldSkipLog(str, type)) + return; - logMutex.lock(); - ErrorStack.emplace_back(final.str()); - logMutex.unlock(); + const auto message = FormatLogMessage(str); + + std::lock_guard lock(logMutex); + ErrorStack.push_back(message); +} + +void WriteLog(const char* str, logtype type, bool isError) +{ + if (str == nullptr || *str == '\0') + return; + + WriteLog(std::string_view{str}, type, isError); +} + +void ErrorLog(const char* str, logtype type) +{ + if (str == nullptr || *str == '\0') + return; + + ErrorLog(std::string_view{str}, type); } diff --git a/utilities/utilities.h b/utilities/utilities.h index 0844b085..058220ca 100644 --- a/utilities/utilities.h +++ b/utilities/utilities.h @@ -29,12 +29,15 @@ template T sign(T x) #define DegToRad(a) ((M_PI / 180.0) * (a)) //(a) w nawiasie, bo może być dodawaniem #define RadToDeg(r) ((180.0 / M_PI) * (r)) -#define szSceneryPath "scenery/" -#define szTexturePath "textures/" -#define szModelPath "models/" -#define szDynamicPath "dynamic/" -#define szSoundPath "sounds/" -#define szDataPath "data/" +namespace paths +{ +inline constexpr const char *scenery = "scenery/"; +inline constexpr const char *textures = "textures/"; +inline constexpr const char *models = "models/"; +inline constexpr const char *dynamic = "dynamic/"; +inline constexpr const char *sounds = "sounds/"; +inline constexpr const char *data = "data/"; +} #define MAKE_ID4(a, b, c, d) (((std::uint32_t)(d) << 24) | ((std::uint32_t)(c) << 16) | ((std::uint32_t)(b) << 8) | (std::uint32_t)(a)) diff --git a/utils/uuid.hpp b/utilities/uuid.hpp similarity index 100% rename from utils/uuid.hpp rename to utilities/uuid.hpp diff --git a/vehicle/Driver.cpp b/vehicle/Driver.cpp index ae6b256d..9a1f5d84 100644 --- a/vehicle/Driver.cpp +++ b/vehicle/Driver.cpp @@ -4359,62 +4359,61 @@ void TController::Doors( bool const Open, int const Side ) { if( ( false == doors_permit_active() ) && ( false == doors_open() ) ) { iDrivigFlags &= ~moveDoorOpened; + iDrivigFlags &= ~moveDepartureWarned; return; } + if (mvOccupied->Doors.has_warning && !TestFlag(iDrivigFlags, moveDepartureWarned)) + { + if (!mvOccupied->DepartureSignal) + { + // Not warned yet, not warning yet - start the warning signal. + cue_action( driver_hint::departuresignalon ); // załącenie bzyczka + fActionTime = Random( -0.3, -0.8 ); // 0.3-0.8 second buzzer + return; + } + // Not warned yet, warning now - stop the warning signal and mark it as done. + cue_action( driver_hint::departuresignaloff ); + fActionTime = Random( 0.0, -0.2 ); // Wait just a bit more before departing + iDrivigFlags |= moveDepartureWarned; + return; + } + if( true == doors_open() ) { - if( ( true == mvOccupied->Doors.has_warning ) - && ( false == mvOccupied->DepartureSignal ) - && ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) { - cue_action( driver_hint::departuresignalon ); // załącenie bzyczka - fActionTime = -1.5 - 0.1 * Random( 10 ); // 1.5-2.5 second wait + // consist-wide remote signals to close doors + if( ( pVehicle->MoverParameters->Doors.close_control == control_t::conductor ) + || ( pVehicle->MoverParameters->Doors.close_control == control_t::driver ) + || ( pVehicle->MoverParameters->Doors.close_control == control_t::mixed ) ) { + cue_action( driver_hint::doorrightclose ); + cue_action( driver_hint::doorleftclose ); } } + if( true == doors_permit_active() ) { + cue_action( driver_hint::doorrightpermitoff ); + cue_action( driver_hint::doorleftpermitoff ); + } + // if applicable close manually-operated doors in vehicles which may ignore remote signals + { + const auto *vehicle = pVehicles[ end::front ]; // pojazd na czole składu + while( vehicle != nullptr ) { + // zamykanie drzwi w pojazdach - flaga zezwolenia była by lepsza + auto const ismanualdoor { + ( vehicle->MoverParameters->Doors.auto_velocity == -1.f ) + && ( ( vehicle->MoverParameters->Doors.close_control == control_t::passenger ) + || ( vehicle->MoverParameters->Doors.close_control == control_t::mixed ) ) }; - if( ( false == AIControllFlag ) || ( fActionTime > -0.5 ) ) { - // ai doesn't close the door until it's free to depart, but human driver has free reign to do stupid things - if( true == doors_open() ) { - // consist-wide remote signals to close doors - if( ( pVehicle->MoverParameters->Doors.close_control == control_t::conductor ) - || ( pVehicle->MoverParameters->Doors.close_control == control_t::driver ) - || ( pVehicle->MoverParameters->Doors.close_control == control_t::mixed ) ) { - cue_action( driver_hint::doorrightclose ); - cue_action( driver_hint::doorleftclose ); + if( ( true == ismanualdoor ) + && ( ( vehicle->LoadExchangeTime() == 0.f ) + || ( vehicle->MoverParameters->Vel > EU07_AI_MOVEMENT ) ) ) { + vehicle->MoverParameters->OperateDoors( side::right, false, range_t::local ); + vehicle->MoverParameters->OperateDoors( side::left, false, range_t::local ); } - } - if( true == doors_permit_active() ) { - cue_action( driver_hint::doorrightpermitoff ); - cue_action( driver_hint::doorleftpermitoff ); - } - // if applicable close manually-operated doors in vehicles which may ignore remote signals - { - auto *vehicle = pVehicles[ end::front ]; // pojazd na czole składu - while( vehicle != nullptr ) { - // zamykanie drzwi w pojazdach - flaga zezwolenia była by lepsza - auto const ismanualdoor { - ( vehicle->MoverParameters->Doors.auto_velocity == -1.f ) - && ( ( vehicle->MoverParameters->Doors.close_control == control_t::passenger ) - || ( vehicle->MoverParameters->Doors.close_control == control_t::mixed ) ) }; - - if( ( true == ismanualdoor ) - && ( ( vehicle->LoadExchangeTime() == 0.f ) - || ( vehicle->MoverParameters->Vel > EU07_AI_MOVEMENT ) ) ) { - vehicle->MoverParameters->OperateDoors( side::right, false, range_t::local ); - vehicle->MoverParameters->OperateDoors( side::left, false, range_t::local ); - } - vehicle = vehicle->Next(); // pojazd podłączony z tyłu (patrząc od czoła) - } - } - fActionTime = -2.0 - 0.1 * Random( 15 ); // 2.0-3.5 sec wait, +potentially 0.5 remaining - iDrivigFlags &= ~moveDoorOpened; // zostały zamknięte - nie wykonywać drugi raz - - if( ( true == mvOccupied->DepartureSignal ) - && ( Random( 100 ) < Random( 100 ) ) ) { - // potentially turn off the warning before actual departure - // TBD, TODO: dedicated buzzer duration counter? - cue_action( driver_hint::departuresignaloff ); + vehicle = vehicle->Next(); // pojazd podłączony z tyłu (patrząc od czoła) } } + fActionTime = Random( -1.0, -3.5 ); // 1.0-3.5 sec wait + iDrivigFlags &= ~moveDoorOpened; // zostały zamknięte - nie wykonywać drugi raz + iDrivigFlags &= ~moveDepartureWarned; } } @@ -4541,7 +4540,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N // NewCommand = Global.asCurrentSceneryPath + NewCommand; auto lookup = FileExists( - { Global.asCurrentSceneryPath + NewCommand, szSoundPath + NewCommand }, + { Global.asCurrentSceneryPath + NewCommand, paths::sounds + NewCommand }, { ".ogg", ".flac", ".wav" } ); if( false == lookup.first.empty() ) { // wczytanie dźwięku odjazdu podawanego bezpośrenido @@ -4552,7 +4551,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N NewCommand += "radio"; lookup = FileExists( - { Global.asCurrentSceneryPath + NewCommand, szSoundPath + NewCommand }, + { Global.asCurrentSceneryPath + NewCommand, paths::sounds + NewCommand }, { ".ogg", ".flac", ".wav" } ); if( false == lookup.first.empty() ) { // wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie) diff --git a/vehicle/Driver.h b/vehicle/Driver.h index 554ffc87..e897ba91 100644 --- a/vehicle/Driver.h +++ b/vehicle/Driver.h @@ -75,7 +75,8 @@ enum TMovementStatus movePushPull = 0x40000, // zmiana czoła przez zmianę kabiny - nie odczepiać przy zmianie kierunku moveSignalFound = 0x80000, // na drodze skanowania został znaleziony semafor moveStopPointFound = 0x100000, // stop point detected ahead - moveGuardOpenDoor = 0x200000 // time for opening a door before departure + moveGuardOpenDoor = 0x200000, // time for opening a door before departure + moveDepartureWarned = 0x400000 // driver has already warned about the doors being closed (only for manual warning - i.e. EN57-ish) /* moveSemaphorWasElapsed = 0x100000, // minięty został semafor moveTrainInsideStation = 0x200000, // pociąg między semaforem a rozjazdami lub następnym semaforem diff --git a/vehicle/DynObj.cpp b/vehicle/DynObj.cpp index 63e6940d..8f4a3db5 100644 --- a/vehicle/DynObj.cpp +++ b/vehicle/DynObj.cpp @@ -63,7 +63,7 @@ TextureTest( std::string const &Name ) { auto const lookup { FileExists( - { Global.asCurrentTexturePath + Name, Name, szTexturePath + Name }, + { Global.asCurrentTexturePath + Name, Name, paths::textures + Name }, { ".mat", ".dds", ".tga", ".ktx", ".png", ".bmp", ".jpg", ".tex" } ) }; return ( lookup.first + lookup.second ); @@ -1982,7 +1982,7 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" ) { // Ustawienie początkowe pojazdu iDirection = (Reversed ? 0 : 1); // Ra: 0, jeśli ma być wstawiony jako obrócony tyłem - asBaseDir = szDynamicPath + BaseDir + "/"; // McZapkie-310302 + asBaseDir = paths::dynamic + BaseDir + "/"; // McZapkie-310302 asName = Name; std::string asAnimName; // zmienna robocza do wyszukiwania osi i wózków // Ra: zmieniamy znaczenie obsady na jednoliterowe, żeby dosadzić kierownika @@ -3011,7 +3011,7 @@ void TDynamicObject::LoadUpdate() { // update bindings between lowpoly sections and potential load chunks placed inside them update_load_sections(); // z powrotem defaultowa sciezka do tekstur - Global.asCurrentTexturePath = std::string( szTexturePath ); + Global.asCurrentTexturePath = std::string( paths::textures ); } } @@ -4265,27 +4265,6 @@ bool TDynamicObject::Update(double dt, double dt1) MoverParameters->InactiveCabPantsCheck = false; } - - if (MoverParameters->DerailReason > 0) - { - switch (MoverParameters->DerailReason) - { - case 1: - ErrorLog("Bad driving: " + asName + " derailed due to end of track"); - break; - case 2: - ErrorLog("Bad driving: " + asName + " derailed due to too high speed"); - break; - case 3: - ErrorLog("Bad dynamic: " + asName + " derailed due to track width"); - break; // błąd w scenerii - case 4: - ErrorLog("Bad dynamic: " + asName + " derailed due to wrong track type"); - break; // błąd w scenerii - } - MoverParameters->DerailReason = 0; //żeby tylko raz - } - if( MoverParameters->LoadStatus ) { LoadUpdate(); // zmiana modelu ładunku } @@ -5337,7 +5316,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co if (ReplacableSkin != "none") { m_materialdata.assign( ReplacableSkin ); } - Global.asCurrentTexturePath = szTexturePath; // z powrotem defaultowa sciezka do tekstur + Global.asCurrentTexturePath = paths::textures; // z powrotem defaultowa sciezka do tekstur do { token = ""; parser.getTokens(); parser >> token; @@ -6011,7 +5990,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co mdLoad = LoadMMediaFile_mdload( MoverParameters->LoadType.name ); // z powrotem defaultowa sciezka do tekstur - Global.asCurrentTexturePath = std::string( szTexturePath ); + Global.asCurrentTexturePath = std::string( paths::textures ); } } // models @@ -7148,7 +7127,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co attachment->Init(); } - Global.asCurrentTexturePath = szTexturePath; // kiedyś uproszczone wnętrze mieszało tekstury nieba + Global.asCurrentTexturePath = paths::textures; // kiedyś uproszczone wnętrze mieszało tekstury nieba Global.asCurrentDynamicPath = ""; // position sound emitters which weren't defined in the config file diff --git a/vehicle/Train.cpp b/vehicle/Train.cpp index 50fdb489..de2e8e5e 100644 --- a/vehicle/Train.cpp +++ b/vehicle/Train.cpp @@ -7926,6 +7926,11 @@ bool TTrain::Update( double const Deltatime ) btLampkaRearRightEndLight.Turn( ( mvOccupied->iLights[ end::rear ] & light::redmarker_right ) != 0 ); // others btLampkaMalfunction.Turn( mvControlled->dizel_heat.PA ); + // overheat indicator lamps + btLampkaOilOverheat.Turn( mvControlled->dizel_heat.oil.is_hot ); + btLampkaWaterOverheat.Turn( mvControlled->dizel_heat.water.is_hot ); + btLampkaWaterAuxOverheat.Turn( mvControlled->dizel_heat.water_aux.is_hot ); + btLampkaEngineOverheat.Turn( mvControlled->dizel_heat.engine_is_hot ); btLampkaMotorBlowers.Turn( ( mvControlled->MotorBlowers[ end::front ].is_active ) && ( mvControlled->MotorBlowers[ end::rear ].is_active ) ); btLampkaCoolingFans.Turn( mvControlled->RventRot > 1.0 ); btLampkaTempomat.Turn( mvOccupied->SpeedCtrlUnit.IsActive ); @@ -7966,6 +7971,11 @@ bool TTrain::Update( double const Deltatime ) btLampkaBrakeProfileR.Turn( false ); btLampkaSpringBrakeActive.Turn( false ); btLampkaSpringBrakeInactive.Turn( false ); + // overheat indicator lamps off + btLampkaOilOverheat.Turn( false ); + btLampkaWaterOverheat.Turn( false ); + btLampkaWaterAuxOverheat.Turn( false ); + btLampkaEngineOverheat.Turn( false ); btLampkaMaxSila.Turn( false ); btLampkaPrzekrMaxSila.Turn( false ); btLampkaRadio.Turn( false ); @@ -9311,7 +9321,7 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) TModel3d *kabina = TModelsManager::GetModel(DynamicObject->asBaseDir + token, true, true, (Global.network_servers.empty() && !Global.network_client) ? 0 : id()); // z powrotem defaultowa sciezka do tekstur - Global.asCurrentTexturePath = szTexturePath; + Global.asCurrentTexturePath = paths::textures; // if (DynamicObject->mdKabina!=k) if (kabina != nullptr) { @@ -9756,6 +9766,11 @@ void TTrain::clear_cab_controls() btLampkaNadmWent.Clear(9); btLampkaWysRozr.Clear(((mvControlled->TrainType & dt_ET22) != 0) ? -1 : 10); // ET22 nie ma tej lampki btLampkaOgrzewanieSkladu.Clear(11); + // overheat indicator lamps + btLampkaOilOverheat.Clear(-1); + btLampkaWaterOverheat.Clear(-1); + btLampkaWaterAuxOverheat.Clear(-1); + btLampkaEngineOverheat.Clear(-1); btHaslerBrakes.Clear(12); // ciśnienie w cylindrach do odbijania na haslerze btHaslerCurrent.Clear(13); // prąd na silnikach do odbijania na haslerze // Numer 14 jest używany dla buczka SHP w update_sounds() @@ -10425,6 +10440,11 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-mainbreakerblinking:", btLampkaMainBreakerBlinkingIfReady }, { "i-vent_ovld:", btLampkaNadmWent }, { "i-comp_ovld:", btLampkaNadmSpr }, + // overheat indicator lamps + { "i-oil_overheat:", btLampkaOilOverheat }, + { "i-water_overheat:", btLampkaWaterOverheat }, + { "i-wateraux_overheat:", btLampkaWaterAuxOverheat }, + { "i-engine_overheat:", btLampkaEngineOverheat }, { "i-resistors:", btLampkaOpory }, { "i-no_resistors:", btLampkaBezoporowa }, { "i-no_resistors_b:", btLampkaBezoporowaB }, diff --git a/vehicle/Train.h b/vehicle/Train.h index 2831e825..96a41463 100644 --- a/vehicle/Train.h +++ b/vehicle/Train.h @@ -761,6 +761,11 @@ public: // reszta może by?publiczna TButton btLampkaBrakeProfileR; // rapid brake acting speed TButton btLampkaSpringBrakeActive; TButton btLampkaSpringBrakeInactive; + // overheat indicator lamps + TButton btLampkaOilOverheat; + TButton btLampkaWaterOverheat; + TButton btLampkaWaterAuxOverheat; + TButton btLampkaEngineOverheat; // KURS90 TButton btLampkaBoczniki; TButton btLampkaMaxSila; diff --git a/world/Event.cpp b/world/Event.cpp index 015ad7b2..10a71bac 100644 --- a/world/Event.cpp +++ b/world/Event.cpp @@ -1654,7 +1654,7 @@ animation_event::deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) { // animacja z pliku VMD { m_animationfilename = token; - std::ifstream file( szModelPath + m_animationfilename, std::ios::binary | std::ios::ate ); file.unsetf( std::ios::skipws ); + std::ifstream file( paths::models + m_animationfilename, std::ios::binary | std::ios::ate ); file.unsetf( std::ios::skipws ); auto size = file.tellg(); // ios::ate already positioned us at the end of the file file.seekg( 0, std::ios::beg ); // rewind the caret afterwards // animation size, previously held in param 7 @@ -2110,44 +2110,44 @@ friction_event::export_as_text_( std::ostream &Output ) const { } #ifdef WITH_LUA -lua_event::lua_event(lua::eventhandler_t func) { - lua_func = func; +lua_event::lua_event(lua_State *L, const int ref) { + lua_state = L; + lua_func = ref; +} + +lua_event::~lua_event() { + lua::unref(lua_state, lua_func); } // prepares event for use -void -lua_event::init() { - // nothing to do here +void lua_event::init() { + // nothing to do here } // event type string -std::string -lua_event::type() const { - return "lua"; +std::string lua_event::type() const { + return "lua"; } // deserialize() subclass details -void -lua_event::deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) { - // preload next token - Input.getTokens(); +void lua_event::deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) { + // preload next token + Input.getTokens(); } // run() subclass details -void -lua_event::run_() { +void lua_event::run_() { try { - if (lua_func) - lua_func(this, m_activator); + if (lua_func != LUA_NOREF) + lua::dispatch_event(lua_state, lua_func, this, m_activator); } catch (...) { - ErrorLog(simulation::Lua.get_error()); + ErrorLog("lua: Runtime error: " + simulation::Lua.get_error()); } } // export_as_text() subclass details -void -lua_event::export_as_text_( std::ostream &Output ) const { - // nothing to do here +void lua_event::export_as_text_( std::ostream &Output ) const { + // nothing to do here } bool lua_event::is_instant() const { diff --git a/world/Event.h b/world/Event.h index 2f67d8bb..d04b4c87 100644 --- a/world/Event.h +++ b/world/Event.h @@ -591,17 +591,19 @@ private: #ifdef WITH_LUA class lua_event : public basic_event { public: - lua_event(lua::eventhandler_t func); - void init() override; + lua_event(lua_State *L, int ref); + ~lua_event() override; + void init() override; private: - std::string type() const override; - void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override; - void run_() override; - void export_as_text_( std::ostream &Output ) const override; - bool is_instant() const override; + std::string type() const override; + void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override; + void run_() override; + void export_as_text_( std::ostream &Output ) const override; + bool is_instant() const override; - lua::eventhandler_t lua_func = nullptr; + lua_State *lua_state = nullptr; + int lua_func = LUA_NOREF; }; #endif diff --git a/world/Track.cpp b/world/Track.cpp index f1fb356b..22387323 100644 --- a/world/Track.cpp +++ b/world/Track.cpp @@ -2266,9 +2266,9 @@ TTrack::export_as_text_( std::ostream &Output ) const { m_material1 != null_handle ? GfxRenderer->Material( m_material1 )->GetName() : "none" ) }; - if( texturefile.find( szTexturePath ) == 0 ) { + if( texturefile.find( paths::textures ) == 0 ) { // don't include 'textures/' in the path - texturefile.erase( 0, std::string{ szTexturePath }.size() ); + texturefile.erase( 0, std::string{ paths::textures }.size() ); } Output << texturefile << ' ' @@ -2278,9 +2278,9 @@ TTrack::export_as_text_( std::ostream &Output ) const { m_material2 != null_handle ? GfxRenderer->Material( m_material2 )->GetName() : "none" ); - if( texturefile.find( szTexturePath ) == 0 ) { + if( texturefile.find( paths::textures ) == 0 ) { // don't include 'textures/' in the path - texturefile.erase( 0, std::string{ szTexturePath }.size() ); + texturefile.erase( 0, std::string{ paths::textures }.size() ); } Output << texturefile << ' '; @@ -2351,9 +2351,9 @@ TTrack::export_as_text_( std::ostream &Output ) const { if( ( eType == tt_Switch ) && ( SwitchExtension->m_material3 != null_handle ) ) { auto texturefile { GfxRenderer->Material( m_material2 )->GetName() }; - if( texturefile.find( szTexturePath ) == 0 ) { + if( texturefile.find( paths::textures ) == 0 ) { // don't include 'textures/' in the path - texturefile.erase( 0, std::string{ szTexturePath }.size() ); + texturefile.erase( 0, std::string{ paths::textures }.size() ); } Output << "trackbed " << texturefile << ' '; } @@ -2384,7 +2384,7 @@ std::string TTrack::tooltip() const std::pair TTrack::fetch_track_rail_profile( std::string const &Profile ) { - auto const railprofilepath { std::string( szModelPath ) + "tory/railprofile_" }; + auto const railprofilepath { std::string( paths::models ) + "tory/railprofile_" }; auto const railkeyprefix { std::string( "rail_" ) }; if( m_profiles.empty() ) { @@ -2414,7 +2414,7 @@ TTrack::fetch_default_profiles() { if( false == m_profiles.empty() ) { return; } - auto const railprofilepath { std::string( szModelPath ) + "tory/railprofile_" }; + auto const railprofilepath { std::string( paths::models ) + "tory/railprofile_" }; auto const railkeyprefix { std::string( "rail_" ) }; m_profiles.emplace_back( deserialize_profile( railprofilepath + "default" ) ); diff --git a/world/mtable.cpp b/world/mtable.cpp index c6460248..ba560ff1 100644 --- a/world/mtable.cpp +++ b/world/mtable.cpp @@ -617,7 +617,7 @@ TTrainParameters::load_sounds() { auto const lookup { FileExists( - { Global.asCurrentSceneryPath + stationname, std::string{ szSoundPath } + "sip/" + stationname }, + { Global.asCurrentSceneryPath + stationname, std::string{ paths::sounds } + "sip/" + stationname }, { ".ogg", ".flac", ".wav" } ) }; if( lookup.first.empty() ) { continue;