From 010663e021ec90e93d945713b2c4b69927946158 Mon Sep 17 00:00:00 2001 From: Hirek Date: Sat, 1 Mar 2025 20:58:29 +0100 Subject: [PATCH 1/9] Note for compiling (there are submodules now) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e15e1bd..bab04fe7 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ MaSzyna should work and compile natively under **Linux** and **Windows**. Other Commands will be written in [`Bash`](https://www.gnu.org/software/bash/). No-Linux users must do in corresponding technology. 0. Clone source code. - You may download source code [as ZIP archive](https://github.com/eu07/maszyna/archive/master.zip), or clone it by [`Git`](https://git-scm.com/). We won't provide tutorial to second one, the only note worth mention is that, repository doesn't contain submodules, so `--recursive` is not needed. From now, it is assumed that your working directory is inside directory with unpacked source code. + You may download source code [as ZIP archive](https://github.com/eu07/maszyna/archive/master.zip), or clone it by [`Git`](https://git-scm.com/). We won't provide tutorial to second one, the only note worth mention is that, repository contain submodules, so `--recursive` is needed. From now, it is assumed that your working directory is inside directory with unpacked source code. 1. Make directory, where build files will be stored, then enter inside it. $ mkdir build From 13e6dc0f879df9ee3faf5beb4de9344cb976b2ce Mon Sep 17 00:00:00 2001 From: Hirek Date: Sun, 2 Mar 2025 19:49:32 +0100 Subject: [PATCH 2/9] Exterior model external_only submodel to hide when player is inside cab --- DynObj.cpp | 7 ++++++- DynObj.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/DynObj.cpp b/DynObj.cpp index ecd017cc..3972c2ad 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -1164,12 +1164,15 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist) btnOn = true; } + // only external models ( external_only_on / external_only_off ) + btExteriorOnly.Turn(!bDisplayCab); // display only when cab is not rendered + if( ( false == bDisplayCab ) // edge case, lowpoly may act as a stand-in for the hi-fi cab, so make sure not to show the driver when inside && ( Mechanik != nullptr ) && ( ( Mechanik->action() != TAction::actSleep ) /* || ( MoverParameters->Battery ) */ ) ) { // rysowanie figurki mechanika - btMechanik1.Turn( MoverParameters->CabOccupied > 0 ); + btMechanik1.Turn(MoverParameters->CabOccupied > 0); btMechanik2.Turn( MoverParameters->CabOccupied < 0 ); if( MoverParameters->CabOccupied != 0 ) { btnOn = true; @@ -2373,9 +2376,11 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" iInventory[end::rear] |= m_highbeam22.Active() ? light::highbeamlight_right : 0; iInventory[end::rear] |= m_highbeam23.Active() ? light::highbeamlight_left : 0; + btExteriorOnly.Init("external_only", mdModel, false); btMechanik1.Init( "mechanik1", mdLowPolyInt, false); btMechanik2.Init( "mechanik2", mdLowPolyInt, false); + if( MoverParameters->dizel_heat.water.config.shutters ) { btShutters1.Init( "shutters1", mdModel, false ); } diff --git a/DynObj.h b/DynObj.h index 97ca1a90..b3cf7575 100644 --- a/DynObj.h +++ b/DynObj.h @@ -494,6 +494,7 @@ private: AirCoupler m_headsignal13; AirCoupler m_headsignal22; AirCoupler m_headsignal23; + TButton btExteriorOnly; TButton btMechanik1; TButton btMechanik2; TButton btShutters1; // cooling shutters for primary water circuit From f8d6bf5d0cd2ff6530791f31f053e54c1b117015 Mon Sep 17 00:00:00 2001 From: Hirek Date: Thu, 6 Mar 2025 22:51:24 +0100 Subject: [PATCH 3/9] Add wiper animation --- DynObj.cpp | 182 ++++++++++++++++++++++++++++++++++++++++++- DynObj.h | 18 ++++- McZapkie/MOVER.h | 19 +++++ McZapkie/Mover.cpp | 54 ++++++++++++- Train.cpp | 41 +++++++++- Train.h | 4 + command.cpp | 4 + command.h | 2 + drivermouseinput.cpp | 4 + utilities.h | 8 ++ 10 files changed, 330 insertions(+), 6 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 3972c2ad..15aea0ba 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -254,6 +254,9 @@ int TAnim::TypeSet(int i, TMoverParameters currentMover, int fl) case 8: iFlags = 0x080; break; // mirror + case 9: + iFlags = 0x023; // 2: Rotating/Motion-based, 8: Uses 3 submodels + break; default: iFlags = 0; } @@ -761,6 +764,24 @@ void TDynamicObject::UpdateMirror( TAnim *pAnim ) { interpolate( 0.0, MoverParameters->MirrorMaxShift, dMirrorMoveL * isactive ) ); } +// wipers +void TDynamicObject::UpdateWiper(TAnim* pAnim) +{ + if (!pAnim || !pAnim->smElement) + return; + + int i = pAnim->iNumber; + // odwaramy animacje dla parzystych indexow + const double rotateAngle = (i + 1) % 2 == 0 ? -MoverParameters->WiperAngle : MoverParameters->WiperAngle; + + if (pAnim->smElement[0]) // ramie 1 + pAnim->smElement[0]->SetRotate(float3(0, 1, 0), smoothInterpolate(0.0, rotateAngle, dWiperPos[i])); + if (pAnim->smElement[1]) // ramie 2 + pAnim->smElement[1]->SetRotate(float3(0, 1, 0), smoothInterpolate(0.0, rotateAngle, dWiperPos[i])); + if (pAnim->smElement[2]) // pioro + pAnim->smElement[2]->SetRotate(float3(0, 1, 0), smoothInterpolate(0.0, -rotateAngle, dWiperPos[i])); +} + /* void TDynamicObject::UpdateLeverDouble(TAnim *pAnim) { // animacja gałki zależna od double @@ -4105,6 +4126,99 @@ bool TDynamicObject::Update(double dt, double dt1) MoverParameters->PantFrontVolt = 0.95 * MoverParameters->EnginePowerSource.MaxVoltage; } + // wipers + if (dWiperPos.size() > 0) // tylko dla wozow ze zdefiniowanymi wycierakami + { + for (int i = 0; i < dWiperPos.size(); i++) // iteracja po kazdej wycieraczce + { + bool wipersActive; + auto const bytesum = MoverParameters->WiperList[MoverParameters->wiperSwitchPos].byteSum; + // rozroznienie kabinowe + if (MoverParameters->CabActive == 1) + { + wipersActive = ((bytesum & (1 << i)) != 0) && MoverParameters->Battery; + } + else if (MoverParameters->CabActive == -1) + { + // odwroconie indexow wycieraczek + wipersActive = ((bytesum & (1 << dWiperPos.size() - 1 - i)) != 0) && MoverParameters->Battery; + } + else { + wipersActive = false; + } + + auto const currentWiperParams = MoverParameters->WiperList[workingSwitchPos[i]]; + + wiperOutTimer[i] += dt1; // aktualizujemy zegarek + wiperParkTimer[i] += dt1; // aktualizujemy zegarek + + if (wipersActive || dWiperPos[i] > 0.0) // zeby wrocily do trybu park + { + if (dWiperPos[i] > 0.0 && !wipersActive) // bezwzgledny powrot do zera + { + dWiperPos[i] = std::max(0.0, dWiperPos[i] - (1.f / currentWiperParams.WiperSpeed) * dt1); + } + else { + + if (dWiperPos[i] < 1.0 && !wiperDirection[i] && wiperParkTimer[i] > currentWiperParams.interval) + // go out + { + if (!sWiperToPark.is_playing()) + { + wiper_playSoundFromStart[i] = true; + } + dWiperPos[i] = std::min(1.0, dWiperPos[i] + (1.f / currentWiperParams.WiperSpeed) * dt1); + } + if (dWiperPos[i] > 0.0 && wiperDirection[i] && wiperOutTimer[i] > currentWiperParams.outBackDelay) + // return back + { + if (!sWiperToPark.is_playing()) + { + wiper_playSoundToStart[i] = true; + } + dWiperPos[i] = std::max(0.0, dWiperPos[i] - (1.f / currentWiperParams.WiperSpeed) * dt1); + } + if (dWiperPos[i] == 1.0) // we reached end + { + wiperParkTimer[i] = 0.0; + wiperDirection[i] = true; // switch direction + } + if (dWiperPos[i] == 0.0) + { + // when in park position + wiperOutTimer[i] = 0.0; + wiperDirection[i] = false; + workingSwitchPos[i] = MoverParameters->wiperSwitchPos; // update configuration + } + + } + } + + // sound + if (wiper_playSoundFromStart[i] && dWiperPos[i] == 1.0) + { + sWiperFromPark.play(sound_flags::exclusive); + wiper_playSoundFromStart[i] = false; + } + else + { + sWiperToPark.stop(); + } + if (wiper_playSoundToStart[i] && dWiperPos[i] == 0.0) + { + sWiperToPark.play(sound_flags::exclusive); + wiper_playSoundToStart[i] = false; + } + else + { + sWiperToPark.stop(); + } + + } + } + + + // mirrors if( (MoverParameters->Vel > MoverParameters->MirrorVelClose) || (MoverParameters->CabActive == 0) && (activation::mirrors) @@ -5169,6 +5283,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co std::string asAnimName; bool Stop_InternalData = false; pants = NULL; // wskaźnik pierwszego obiektu animującego dla pantografów + wipers = NULL; // wskaznik pierwszego obiektu animujacego dla wycieraczek { // preliminary check whether the file exists cParser parser( TypeName + ".mmd", cParser::buffer_FILE, asBaseDir ); @@ -5256,13 +5371,17 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co pAnimations.resize( iAnimations ); int i, j, k = 0, sm = 0; - for (j = 0; j < ANIM_TYPES; ++j) - for (i = 0; i < iAnimType[j]; ++i) + for (j = 0; j < ANIM_TYPES; ++j) // petla po wszystkich wpisach w animations + for (i = 0; i < iAnimType[j]; ++i) // petla iteruje sie tyle razy ile mamy wpisane w animations { if (j == ANIM_PANTS) // zliczamy poprzednie animacje if (!pants) if (iAnimType[ANIM_PANTS]) // o ile jakieś pantografy są (a domyślnie są) pants = &pAnimations[k]; // zapamiętanie na potrzeby wyszukania submodeli + if (j == ANIM_WIPERS) + if (!wipers) + if (iAnimType[ANIM_WIPERS]) + wipers = &pAnimations[k]; pAnimations[k].iShift = sm; // przesunięcie do przydzielenia wskaźnika sm += pAnimations[k++].TypeSet(j, *MoverParameters); // ustawienie typu animacji i zliczanie tablicowanych submodeli } @@ -5835,6 +5954,52 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } } + else if (token == "animwiperprefix:") + { + parser.getTokens(1, false); + parser >> token; + TSubModel *sm; + if (wipers) + { + + for (int i = 0; i < iAnimType[ANIM_WIPERS]; i++) // zebranie wszystkich submodeli wycieraczek + { + dWiperPos.emplace_back(0.0); // dodajemy na koniec zeby sie miejsce tam zrobilo i nie bylo invalid addressow potem + + asAnimName = token + std::to_string(i + 1); + // element wycieraczki nr 1 + sm = GetSubmodelFromName(mdModel, asAnimName + "_p1"); + wipers[i].smElement[0] = sm; + if (sm) + { + wipers[i].smElement[0]->WillBeAnimated(); + // auto const offset{wipers[i].smElement[0]->offset()}; + } + + // element wycieraczki nr 2 + sm = GetSubmodelFromName(mdModel, asAnimName + "_p2"); + wipers[i].smElement[1] = sm; + if (sm) + { + wipers[i].smElement[1]->WillBeAnimated(); + // auto const offset{wipers[i].smElement[0]->offset()}; + } + + // element wycieraczki nr 3 + sm = GetSubmodelFromName(mdModel, asAnimName + "_p3"); + wipers[i].smElement[2] = sm; + if (sm) + { + wipers[i].smElement[2]->WillBeAnimated(); + // auto const offset{wipers[i].smElement[0]->offset()}; + } + wipers[i].yUpdate = std::bind(&TDynamicObject::UpdateWiper, this, std::placeholders::_1); + wipers[i].fMaxDist = 150 * 150; + wipers[i].iNumber = i; + } + } + } + } while( ( token != "" ) && ( token != "endmodels" ) ); @@ -6586,6 +6751,19 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co m_powertrainsounds.rsEngageSlippery.m_frequencyfactor /= ( 1 + MoverParameters->nmax ); } + + // dzwieki wycieraczkuf + else if (token == "wiperFromPark:") + { + sWiperFromPark.deserialize(parser, sound_type::single); + sWiperFromPark.owner(this); + } + else if (token == "wiperToPark:") + { + sWiperToPark.deserialize(parser, sound_type::single); + sWiperToPark.owner(this); + } + else if (token == "retarder:") { m_powertrainsounds.retarder.deserialize(parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency); m_powertrainsounds.retarder.owner(this); diff --git a/DynObj.h b/DynObj.h index b3cf7575..9af851c0 100644 --- a/DynObj.h +++ b/DynObj.h @@ -22,6 +22,8 @@ http://mozilla.org/MPL/2.0/. #include "sound.h" #include "Spring.h" +#include + #define EU07_SOUND_BOGIESOUNDS //--------------------------------------------------------------------------- @@ -36,7 +38,8 @@ int const ANIM_PANTS = 5; // pantografy int const ANIM_STEAMS = 6; // napęd parowozu int const ANIM_DOORSTEPS = 7; int const ANIM_MIRRORS = 8; -int const ANIM_TYPES = 9; // Ra: ilość typów animacji +int const ANIM_WIPERS = 9; +int const ANIM_TYPES = 10; // Ra: ilość typów animacji class TAnim; //typedef void(__closure *TUpdate)(TAnim *pAnim); // typ funkcji aktualizującej położenie submodeli @@ -291,7 +294,8 @@ private: void UpdatePlatformTranslate(TAnim *pAnim); // doorstep animation, shift void UpdatePlatformRotate(TAnim *pAnim); // doorstep animation, rotate void UpdateMirror(TAnim *pAnim); // mirror animation -/* + void UpdateWiper(TAnim *pAnim); // wiper animation + /* void UpdateLeverDouble(TAnim *pAnim); // animacja gałki zależna od double void UpdateLeverFloat(TAnim *pAnim); // animacja gałki zależna od float void UpdateLeverInt(TAnim *pAnim); // animacja gałki zależna od int (wartość) @@ -315,6 +319,14 @@ private: Math3D::vector3 vFloor; // podłoga dla ładunku public: TAnim *pants; // indeks obiektu animującego dla pantografu 0 + TAnim *wipers; // wycieraczki + std::vector wiperOutTimer = std::vector(8, 0.0); + std::vector wiperParkTimer = std::vector(8, 0.0); + std::vector workingSwitchPos = std::vector(8, 0); // working switch position (to not break wipers when switching modes) + std::vector dWiperPos; // timing na osi czasu animacji wycieraczki + std::vector wiperDirection = std::vector(8, false); // false - return direction; true - out direction + std::vector wiper_playSoundFromStart = std::vector(8, false); + std::vector wiper_playSoundToStart = std::vector(8, false); double NoVoltTime; // czas od utraty zasilania double dMirrorMoveL{ 0.0 }; double dMirrorMoveR{ 0.0 }; @@ -534,6 +546,8 @@ private: springbrake_sounds m_springbrakesounds; sound_source rsSlippery { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // moved from cab sound_source sSand { sound_placement::external }; + sound_source sWiperToPark { sound_placement::internal }; + sound_source sWiperFromPark { sound_placement::internal }; // moving part and other external sounds sound_source m_startjolt { sound_placement::general }; // movement start jolt, played once on initial acceleration at slow enough speed bool m_startjoltplayed { false }; diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 10eae432..d0c2b331 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -609,6 +609,17 @@ struct TDEScheme double Umax = 0.0; /*napiecie maksymalne*/ double Imax = 0.0; /*prad maksymalny*/ }; + +struct TWiperScheme +{ + uint8_t byteSum = 0; // suma bitowa pracujacych wycieraczek + double WiperSpeed = 0.0; // predkosc wycieraczki + double interval = 0.0; // interwal pracy wycieraczki + double outBackDelay = 0.0; // czas po jakim wycieraczka zacznie wracac z konca do poczatku +}; +typedef TWiperScheme TWiperSchemeTable[16]; + + typedef TDEScheme TDESchemeTable[33]; /*tablica rezystorow rozr.*/ struct TShuntScheme { @@ -1134,6 +1145,9 @@ class TMoverParameters bool LocHandleTimeTraxx = false; /*hamulec dodatkowy typu traxx*/ double MBPM = 1.0; /*masa najwiekszego cisnienia*/ + int wiperSwitchPos = 0; // pozycja przelacznika wycieraczek + double WiperAngle = {45.0}; // kat pracy wycieraczek + std::shared_ptr Hamulec; std::shared_ptr Handle; std::shared_ptr LocHandle; @@ -1359,6 +1373,9 @@ class TMoverParameters bool Flat = false; double Vhyp = 1.0; TDESchemeTable DElist; + TWiperSchemeTable WiperList; + int WiperListSize; + double Vadd = 1.0; TMPTRelayTable MPTRelay; int RelayType = 0; @@ -2046,6 +2063,7 @@ private: void LoadFIZ_UCList(std::string const &Input); void LoadFIZ_DList( std::string const &Input ); void LoadFIZ_FFList( std::string const &Input ); + void LoadFIZ_WiperList(std::string const &Input); void LoadFIZ_LightsList( std::string const &Input ); void LoadFIZ_CompressorList(std::string const &Input); void LoadFIZ_PowerParamsDecode( TPowerParameters &Powerparameters, std::string const Prefix, std::string const &Input ); @@ -2067,6 +2085,7 @@ private: bool readPmaxList(std::string const &line); bool readFFList( std::string const &line ); bool readWWList( std::string const &line ); + bool readWiperList( std::string const &line ); bool readLightsList( std::string const &Input ); bool readCompressorList(std::string const &Input); void BrakeValveDecode( std::string const &s ); //Q 20160719 diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index deb8492e..1d894dd0 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -8928,7 +8928,7 @@ bool startBPT; bool startMPT, startMPT0; bool startRLIST, startUCLIST; bool startDIZELMOMENTUMLIST, startDIZELV2NMAXLIST, startHYDROTCLIST, startPMAXLIST; -bool startDLIST, startFFLIST, startWWLIST; +bool startDLIST, startFFLIST, startWWLIST, startWiperList; bool startLIGHTSLIST; bool startCOMPRESSORLIST; int LISTLINE; @@ -9280,6 +9280,25 @@ bool TMoverParameters::readFFList( std::string const &line ) { return true; } +// parsowanie wiperList +bool TMoverParameters::readWiperList(std::string const& line) +{ + cParser parser(line); + if (false == parser.getTokens(4, false)) + { + WriteLog("Read WiperList: arguments missing in line " + std::to_string(LISTLINE + 1)); + return false; + } + int idx = LISTLINE++; + if (idx >= sizeof(WiperList) / sizeof(TWiperScheme)) + { + WriteLog("Read WiperList: number of entries exceeded capacity of the data table"); + return false; + } + parser >> WiperList[idx].byteSum >> WiperList[idx].WiperSpeed >> WiperList[idx].interval >> WiperList[idx].outBackDelay; + return true; +} + // parsowanie WWList bool TMoverParameters::readWWList( std::string const &line ) { @@ -9463,6 +9482,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) startPMAXLIST = false; startFFLIST = false; startWWLIST = false; + startWiperList = false; startLIGHTSLIST = false; startCOMPRESSORLIST = false; std::string file = TypeName + ".fiz"; @@ -9563,6 +9583,13 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) startFFLIST = false; continue; } + if (issection("endwl", inputline)) + { + // skonczylismy czytac liste konfiguracji wycieraczek + startBPT = false; + startWiperList = false; + continue; + } if( issection( "END-WWL", inputline ) ) { startBPT = false; startWWLIST = false; @@ -9854,9 +9881,22 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) { startBPT = false; startWWLIST = true; LISTLINE = 0; + continue; } + if (issection("WiperList:", inputline)) + { + startBPT = false; + fizlines.emplace("WiperList", inputline); + startWiperList = true; + LISTLINE = 0; + LoadFIZ_WiperList(inputline); + + continue; + } + + if( issection( "LightsList:", inputline ) ) { startBPT = false; fizlines.emplace( "LightsList", inputline ); @@ -9923,6 +9963,11 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) readWWList( inputline ); continue; } + if (true == startWiperList) + { + readWiperList(inputline); + continue; + } if( true == startLIGHTSLIST ) { readLightsList( inputline ); continue; @@ -11274,6 +11319,13 @@ void TMoverParameters::LoadFIZ_FFList( std::string const &Input ) { extract_value( RlistSize, "Size", Input, "" ); } + +void TMoverParameters::LoadFIZ_WiperList(std::string const &Input) +{ + extract_value(WiperListSize, "Size", Input, ""); + extract_value(WiperAngle, "Angle", Input, ""); +} + void TMoverParameters::LoadFIZ_LightsList( std::string const &Input ) { extract_value( LightsPosNo, "Size", Input, "" ); diff --git a/Train.cpp b/Train.cpp index b2a76c05..bfc817c0 100644 --- a/Train.cpp +++ b/Train.cpp @@ -290,6 +290,10 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::pantographraiserear, &TTrain::OnCommand_pantographraiserear }, { user_command::pantographlowerfront, &TTrain::OnCommand_pantographlowerfront }, { user_command::pantographlowerrear, &TTrain::OnCommand_pantographlowerrear }, + + {user_command::wiperswitchincrease, &TTrain::OnCommand_wiperswitchincrease}, + {user_command::wiperswitchdecrease, &TTrain::OnCommand_wiperswitchdecrease}, + { user_command::pantographlowerall, &TTrain::OnCommand_pantographlowerall }, { user_command::pantographselectnext, &TTrain::OnCommand_pantographselectnext }, { user_command::pantographselectprevious, &TTrain::OnCommand_pantographselectprevious }, @@ -2213,6 +2217,31 @@ void TTrain::OnCommand_mubrakingindicatortoggle( TTrain *Train, command_data con } } +void TTrain::OnCommand_wiperswitchincrease(TTrain *Train, command_data const &Command) +{ + if (Command.action == GLFW_PRESS) + { + Train->mvOccupied->wiperSwitchPos++; + if (Train->mvOccupied->wiperSwitchPos > Train->mvOccupied->WiperListSize) + Train->mvOccupied->wiperSwitchPos = Train->mvOccupied->WiperListSize - 1; + + // Visual feedback + Train->ggWiperSw.UpdateValue(Train->mvOccupied->wiperSwitchPos, Train->dsbSwitch); + } +} +void TTrain::OnCommand_wiperswitchdecrease(TTrain *Train, command_data const &Command) +{ + if (Command.action == GLFW_PRESS) + { + Train->mvOccupied->wiperSwitchPos--; + if (Train->mvOccupied->wiperSwitchPos < 0) + Train->mvOccupied->wiperSwitchPos = 0; + + // visual feedback + Train->ggWiperSw.UpdateValue(Train->mvOccupied->wiperSwitchPos, Train->dsbSwitch); + } +} + void TTrain::OnCommand_reverserincrease( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { @@ -6993,6 +7022,7 @@ void TTrain::OnCommand_vehicleboost(TTrain *Train, const command_data &Command) } } + // cab movement update, fixed step part void TTrain::UpdateCab() { @@ -8158,6 +8188,7 @@ bool TTrain::Update( double const Deltatime ) ggBrakeProfileG.Update(); ggBrakeProfileR.Update(); ggBrakeOperationModeCtrl.Update(); + ggWiperSw.Update(); ggMaxCurrentCtrl.UpdateValue( ( true == mvControlled->ShuntModeAllow ? ( true == mvControlled->ShuntMode ? @@ -9648,6 +9679,7 @@ void TTrain::clear_cab_controls() ggBrakeProfileG.Clear(); ggBrakeProfileR.Clear(); ggBrakeOperationModeCtrl.Clear(); + ggWiperSw.Clear(); ggMaxCurrentCtrl.Clear(); ggMainOffButton.Clear(); ggMainOnButton.Clear(); @@ -10117,6 +10149,12 @@ void TTrain::set_cab_controls( int const Cab ) { 1.f : 0.f ); } + + if (ggWiperSw.SubModel != nullptr) + { + ggWiperSw.PutValue(mvOccupied->wiperSwitchPos); + } + if (ggBrakeOperationModeCtrl.SubModel != nullptr) { ggBrakeOperationModeCtrl.PutValue( (mvOccupied->BrakeOpModeFlag > 0 ? @@ -10591,7 +10629,8 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "invertertoggle11_bt:", ggInverterToggleButtons[10] }, { "invertertoggle12_bt:", ggInverterToggleButtons[11] }, {"pantvalvesupdate_bt:", ggPantValvesUpdate}, - {"pantvalvesoff_bt:", ggPantValvesOff} + {"pantvalvesoff_bt:", ggPantValvesOff}, + {"wipers_sw:", ggWiperSw} }; { auto const lookup { gauges.find( Label ) }; diff --git a/Train.h b/Train.h index a6267bb2..d569cf27 100644 --- a/Train.h +++ b/Train.h @@ -226,6 +226,8 @@ class TTrain { // command handlers // NOTE: we're currently using universal handlers and static handler map but it may be beneficial to have these implemented on individual class instance basis // TBD, TODO: consider this approach if we ever want to have customized consist behaviour to received commands, based on the consist/vehicle type or whatever + static void OnCommand_wiperswitchincrease(TTrain *Train, command_data const &Command); + static void OnCommand_wiperswitchdecrease(TTrain *Train, command_data const &Command); static void OnCommand_aidriverenable( TTrain *Train, command_data const &Command ); static void OnCommand_aidriverdisable( TTrain *Train, command_data const &Command ); static void OnCommand_jointcontrollerset( TTrain *Train, command_data const &Command ); @@ -546,6 +548,8 @@ public: // reszta może by?publiczna TGauge ggBrakeProfileR; // nastawiacz PR - hamowanie dwustopniowe TGauge ggBrakeOperationModeCtrl; //przełącznik trybu pracy PS/PN/EP/MED + TGauge ggWiperSw; // przelacznik wycieraczek + TGauge ggMaxCurrentCtrl; TGauge ggMainOffButton; diff --git a/command.cpp b/command.cpp index e7b6b64a..9a0defe7 100644 --- a/command.cpp +++ b/command.cpp @@ -375,6 +375,8 @@ commanddescription_sequence Commands_descriptions = { { "spawntrainset", command_target::simulation, command_mode::oneoff }, { "destroytrainset", command_target::simulation, command_mode::oneoff }, { "quitsimulation", command_target::simulation, command_mode::oneoff }, + {"wiperswitchincrease", command_target::vehicle, command_mode::oneoff}, + {"wiperswitchdecrease", command_target::vehicle, command_mode::oneoff}, }; // Maps of command and coresponding strings @@ -729,6 +731,8 @@ std::unordered_map commandMap = { {"spawntrainset", user_command::spawntrainset}, {"destroytrainset", user_command::destroytrainset}, {"quitsimulation", user_command::quitsimulation}, + {"wiperswitchincrease", user_command::wiperswitchincrease}, + {"wiperswitchdecrease", user_command::wiperswitchdecrease}, {"none", user_command::none}}; } // simulation diff --git a/command.h b/command.h index a6201daf..96a9b9c8 100644 --- a/command.h +++ b/command.h @@ -371,6 +371,8 @@ enum class user_command { spawntrainset, destroytrainset, quitsimulation, + wiperswitchincrease, + wiperswitchdecrease, none = -1 }; diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index 31da0943..a6f99e56 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -1115,6 +1115,10 @@ drivermouse_input::default_bindings() { { "invertertoggle12_bt:",{ user_command::invertertoggle12, user_command::none } }, + { "wipers_sw:",{ + user_command::wiperswitchincrease, + user_command::wiperswitchdecrease + } }, }; } diff --git a/utilities.h b/utilities.h index 2d479627..385e5480 100644 --- a/utilities.h +++ b/utilities.h @@ -332,6 +332,14 @@ interpolate( Type_ const &First, Type_ const &Second, double const Factor ) { return static_cast( ( First * ( 1.0 - Factor ) ) + ( Second * Factor ) ); } +template Type_ smoothInterpolate(Type_ const &First, Type_ const &Second, double Factor) +{ + // Apply smoothing (ease-in-out quadratic) + Factor = Factor * Factor * (3 - 2 * Factor); + + return static_cast((First * (1.0 - Factor)) + (Second * Factor)); +} + // tests whether provided points form a degenerate triangle template bool From 87973d64fa1f24f8b40afa755654ce01694405f2 Mon Sep 17 00:00:00 2001 From: Hirek Date: Thu, 6 Mar 2025 23:26:09 +0100 Subject: [PATCH 4/9] Wiper logic cleanup --- DynObj.cpp | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/DynObj.cpp b/DynObj.cpp index 15aea0ba..278dafa7 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -4163,19 +4163,11 @@ bool TDynamicObject::Update(double dt, double dt1) if (dWiperPos[i] < 1.0 && !wiperDirection[i] && wiperParkTimer[i] > currentWiperParams.interval) // go out { - if (!sWiperToPark.is_playing()) - { - wiper_playSoundFromStart[i] = true; - } dWiperPos[i] = std::min(1.0, dWiperPos[i] + (1.f / currentWiperParams.WiperSpeed) * dt1); } if (dWiperPos[i] > 0.0 && wiperDirection[i] && wiperOutTimer[i] > currentWiperParams.outBackDelay) // return back { - if (!sWiperToPark.is_playing()) - { - wiper_playSoundToStart[i] = true; - } dWiperPos[i] = std::max(0.0, dWiperPos[i] - (1.f / currentWiperParams.WiperSpeed) * dt1); } if (dWiperPos[i] == 1.0) // we reached end @@ -4193,27 +4185,6 @@ bool TDynamicObject::Update(double dt, double dt1) } } - - // sound - if (wiper_playSoundFromStart[i] && dWiperPos[i] == 1.0) - { - sWiperFromPark.play(sound_flags::exclusive); - wiper_playSoundFromStart[i] = false; - } - else - { - sWiperToPark.stop(); - } - if (wiper_playSoundToStart[i] && dWiperPos[i] == 0.0) - { - sWiperToPark.play(sound_flags::exclusive); - wiper_playSoundToStart[i] = false; - } - else - { - sWiperToPark.stop(); - } - } } From fa9eef8b69fb460a4ef97da0d11964462859fbb2 Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 7 Mar 2025 02:07:08 +0100 Subject: [PATCH 5/9] Necessary fixes for wpiers switcher --- Train.cpp | 2 +- drivermouseinput.cpp | 2 +- translation.cpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Train.cpp b/Train.cpp index bfc817c0..bb7b20ad 100644 --- a/Train.cpp +++ b/Train.cpp @@ -2222,7 +2222,7 @@ void TTrain::OnCommand_wiperswitchincrease(TTrain *Train, command_data const &Co if (Command.action == GLFW_PRESS) { Train->mvOccupied->wiperSwitchPos++; - if (Train->mvOccupied->wiperSwitchPos > Train->mvOccupied->WiperListSize) + if (Train->mvOccupied->wiperSwitchPos > Train->mvOccupied->WiperListSize - 1) Train->mvOccupied->wiperSwitchPos = Train->mvOccupied->WiperListSize - 1; // Visual feedback diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index a6f99e56..b57542cd 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -1115,7 +1115,7 @@ drivermouse_input::default_bindings() { { "invertertoggle12_bt:",{ user_command::invertertoggle12, user_command::none } }, - { "wipers_sw:",{ + { "wieprs_sw:",{ user_command::wiperswitchincrease, user_command::wiperswitchdecrease } }, diff --git a/translation.cpp b/translation.cpp index 65638988..c6de1099 100644 --- a/translation.cpp +++ b/translation.cpp @@ -323,6 +323,7 @@ std::string locale::label_cab_control(std::string const &Label) { "universal7:", STRN("interactive part") }, { "universal8:", STRN("interactive part") }, { "universal9:", STRN("interactive part") }, + { "wieprs_sw:", STRN("wipers mode selector") }, }; auto const it = cabcontrols_labels.find( Label ); From 7cfb811f7da52c388bdbbb77a577f6d682d205ea Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 7 Mar 2025 02:11:27 +0100 Subject: [PATCH 6/9] Typo fix --- drivermouseinput.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index b57542cd..a6f99e56 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -1115,7 +1115,7 @@ drivermouse_input::default_bindings() { { "invertertoggle12_bt:",{ user_command::invertertoggle12, user_command::none } }, - { "wieprs_sw:",{ + { "wipers_sw:",{ user_command::wiperswitchincrease, user_command::wiperswitchdecrease } }, From 1b346b1aeee7121f8b9d50f15671a53c3c1b56d3 Mon Sep 17 00:00:00 2001 From: Hirek Date: Fri, 7 Mar 2025 02:12:47 +0100 Subject: [PATCH 7/9] And one more typo... --- translation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translation.cpp b/translation.cpp index c6de1099..d5f85e63 100644 --- a/translation.cpp +++ b/translation.cpp @@ -323,7 +323,7 @@ std::string locale::label_cab_control(std::string const &Label) { "universal7:", STRN("interactive part") }, { "universal8:", STRN("interactive part") }, { "universal9:", STRN("interactive part") }, - { "wieprs_sw:", STRN("wipers mode selector") }, + { "wipers_sw:", STRN("wipers mode selector") }, }; auto const it = cabcontrols_labels.find( Label ); From 583dc8c1ec8bf04bfa75dbd3a7370c4cc29c259f Mon Sep 17 00:00:00 2001 From: Anonim17PL Date: Mon, 24 Mar 2025 13:25:51 +0100 Subject: [PATCH 8/9] =?UTF-8?q?Czy=20kto=C5=9B=20w=20og=C3=B3le=20kompiluj?= =?UTF-8?q?e=20ten=20kod=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ```/home/debian/autobuilder/output/build/maszyna/application.h:41:10: error: extra qualification ‘eu07_application::’ on member ‘DiscordRPCService’ [-fpermissive] 41 | void eu07_application::DiscordRPCService(); // discord rich presence service function (runs as separate thread) | ^~~~~~~~~~~~~~~~``` Czy C++20 jest nam potrzebne? ```/home/debian/autobuilder/output/build/maszyna/McZapkie/Mover.cpp:11346:34: error: ‘std::string’ {aka ‘class std::__cxx11::basic_string’} has no member named ‘_Starts_with’ 11346 | if (PantType._Starts_with("DSA")) // zakladam ze wszystkie pantografy DSA sa takie same | ^~~~~~~~~~~~``` Kompletny absurd z 7cac46d: ```/home/debian/autobuilder/output/build/maszyna/particles.cpp:477:29: error: cannot bind non-const lvalue reference of type ‘cParser&’ to an rvalue of type ‘cParser’ 477 | if( source.deserialize( cParser( templatepath + templatename + ".txt", cParser::buffer_FILE ) ) ) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~``` --- McZapkie/Mover.cpp | 2 +- application.h | 2 +- particles.cpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index deb8492e..9d5e152d 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -11343,7 +11343,7 @@ void TMoverParameters::LoadFIZ_PowerParamsDecode( TPowerParameters &Powerparamet extract_value(PantType, "PantType", Line, ""); if (PantType == "AKP_4E") collectorparameters.PantographType = TPantType::AKP_4E; - if (PantType._Starts_with("DSA")) // zakladam ze wszystkie pantografy DSA sa takie same + if (PantType.rfind("DSA",0) == 0) // zakladam ze wszystkie pantografy DSA sa takie same collectorparameters.PantographType = TPantType::DSAx; if (PantType == "EC160" || PantType == "EC200") collectorparameters.PantographType = TPantType::EC160_200; diff --git a/application.h b/application.h index 3cc73ff0..97068afc 100644 --- a/application.h +++ b/application.h @@ -38,7 +38,7 @@ public: run(); // issues request for a worker thread to perform specified task. returns: true if task was scheduled - void eu07_application::DiscordRPCService(); // discord rich presence service function (runs as separate thread) + void DiscordRPCService(); // discord rich presence service function (runs as separate thread) bool request( python_taskqueue::task_request const &Task ); // ensures the main thread holds the python gil and can safely execute python calls diff --git a/particles.cpp b/particles.cpp index 361f2b8c..a1a9ef8d 100644 --- a/particles.cpp +++ b/particles.cpp @@ -474,7 +474,8 @@ particle_manager::find( std::string const &Template ) { } // ... and if it fails try to add the template to the database from a data file smoke_source source; - if( source.deserialize( cParser( templatepath + templatename + ".txt", cParser::buffer_FILE ) ) ) { + cParser sound_parser( templatepath + templatename + ".txt", cParser::buffer_FILE ); + if( source.deserialize( sound_parser ) ) { // if deserialization didn't fail finish source setup... source.m_opacitymodifier.bind( &Global.SmokeFidelity ); // ...then cache the source as template for future instances From d1415cf2a18685de0c9bc47d7abfff1183b8fbc5 Mon Sep 17 00:00:00 2001 From: Hirek Date: Tue, 1 Apr 2025 02:24:25 +0200 Subject: [PATCH 9/9] priorityLoadText3D feature in eu07.ini Allows to make priority load t3d models instead of e3d. Takes yes or no. --- Globals.cpp | 7 +++++++ Globals.h | 1 + Model3d.cpp | 31 ++++++++++++++++++------------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Globals.cpp b/Globals.cpp index fa75cfe3..1e76e9ba 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -670,6 +670,12 @@ global_settings::ConfigParse(cParser &Parser) { 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 @@ -1358,6 +1364,7 @@ global_settings::export_as_text( std::ostream &Output ) const { export_as_text( Output, "joinduplicatedevents", bJoinEvents ); export_as_text( Output, "hiddenevents", iHiddenEvents ); export_as_text( Output, "pause", ( iPause & 1 ) != 0 ); + export_as_text(Output, "priorityLoadText3D", priorityLoadText3D); export_as_text( Output, "lang", asLang ); export_as_text( Output, "python.updatetime", PythonScreenUpdateRate ); Output diff --git a/Globals.h b/Globals.h index d54b9f4a..b9e4e964 100644 --- a/Globals.h +++ b/Globals.h @@ -82,6 +82,7 @@ struct global_settings { bool file_binary_terrain{ true }; // enable binary terrain (de)serialization bool file_binary_terrain_state{true}; // logs + bool priorityLoadText3D{false}; // ladowanie T3D priorytetowo int iWriteLogEnabled{ 3 }; // maska bitowa: 1-zapis do pliku, 2-okienko, 4-nazwy torów bool MultipleLogs{ false }; unsigned int DisabledLogTypes{ 0 }; diff --git a/Model3d.cpp b/Model3d.cpp index 13ad449d..52fd3959 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1570,23 +1570,28 @@ bool TModel3d::LoadFromFile(std::string const &FileName, bool dynamic) m_filename = name; asBinary = name + ".e3d"; - if (FileExists(asBinary)) + + // Hirek: Jesli mamy ustawione priorityLoadText3D na yes to wpierw ladujemy t3d + if (Global.priorityLoadText3D && FileExists(name + ".t3d")) + { + WriteLog("Forced loading text model \"" + name + ".t3d\""); + LoadFromTextFile(name + ".t3d", dynamic); + if (!dynamic) + Init(); + } + else if (FileExists(asBinary)) { LoadFromBinFile(asBinary, dynamic); - asBinary = ""; // wyłączenie zapisu + asBinary = ""; Init(); - } - else - { - if (FileExists(name + ".t3d")) - { - LoadFromTextFile(name + ".t3d", dynamic); // wczytanie tekstowego - if( !dynamic ) { - // pojazdy dopiero po ustawieniu animacji - Init(); // generowanie siatek i zapis E3D - } - } } + else if (FileExists(name + ".t3d")) + { + LoadFromTextFile(name + ".t3d", dynamic); + if (!dynamic) + Init(); + } + bool const result = Root ? (iSubModelsCount > 0) : false; // brak pliku albo problem z wczytaniem if (false == result)