diff --git a/application/editormodeselftestswitch.cpp b/application/editormodeselftestswitch.cpp index 0f2d5c99..31f4ce77 100644 --- a/application/editormodeselftestswitch.cpp +++ b/application/editormodeselftestswitch.cpp @@ -337,6 +337,46 @@ editor_mode::selftest_result editor_mode::selftest_switch(std::string const &com WriteLog("SELFTEST conecheck " + name + ":" + text); return selftest_result::handled; } + // a walk along the tracks from the plain one nearest the point as an axle runs: along it (1) or against it (-1), on the path of a switch + // it's set to, or the one come onto from its end; each track, the path of a switch and the way, END where no track goes on + if (command == "pathwalk") + { + glm::dvec3 point{0.0}; + double direction{1.0}; + int steps{8}; + words >> point.x >> point.z >> direction >> steps; + TTrack *track{nullptr}; + double nearest{1e9}; + for (auto *candidate : simulation::Paths.sequence()) + { + if (candidate == nullptr || candidate->m_editorremoved || candidate->eType != tt_Normal || candidate->m_paths.empty()) + continue; + if (auto const d{geometry::plan_distance(editor_track::point_at(*candidate, editor_track::nearest_parameter(*candidate, point)), point)}; d < nearest) + { + nearest = d; + track = candidate; + } + } + std::string text{"SELFTEST pathwalk:"}; + int path{-1}; + for (int step = 0; step < steps && track != nullptr; ++step) + { + text += " " + track->name() + (path >= 0 ? "[" + std::to_string(path) + "]" : std::string{}) + (direction > 0 ? ">" : "<"); + auto const side{direction > 0 ? 1 : -1}; + auto const switched{track->eType == tt_Switch && path >= 0}; + auto const joined{switched ? (side > 0 ? track->SwitchExtension->iNextDirection[path] : track->SwitchExtension->iPrevDirection[path]) : (side > 0 ? track->iNextDirection : track->iPrevDirection)}; + auto *next{track->Connected(side, direction, switched ? path : -1)}; + if (next == nullptr) + { + text += " END"; + break; + } + path = next->eType == tt_Switch ? ((joined & 1) ? joined >> 1 : next->SwitchExtension->CurrentIndex) : -1; + track = next; + } + WriteLog(text); + return selftest_result::handled; + } if (command == "throw") { std::string name; diff --git a/editor/editorTrack.cpp b/editor/editorTrack.cpp index 0e1a44c2..11150cb6 100644 --- a/editor/editorTrack.cpp +++ b/editor/editorTrack.cpp @@ -1143,6 +1143,8 @@ void editor_track::join(TTrack &Track) join(*other); } } + // and a switch right after another one + Track.join_switches(); break; } default: diff --git a/world/Track.cpp b/world/Track.cpp index f13e6f97..a54dcd9f 100644 --- a/world/Track.cpp +++ b/world/Track.cpp @@ -2016,6 +2016,59 @@ bool TTrack::SetConnections(int i) return false; } +void TTrack::join_switches() +{ + if( eType != tt_Switch || SwitchExtension == nullptr || simulation::Region == nullptr ) { + return; + } + // the ends of the paths as the codes of the track joined to them have them: 0 the start of a switch, where both paths begin, 1 the end of + // its first path, 3 the end of its second one; a vehicle going on onto a start keeps its direction, onto an end it turns + auto const code = []( int const Endpoint ) { return Endpoint == 3 ? 1 : Endpoint == 5 ? 3 : 0; }; + // the paths of the switch joined to its neighbours as it's set, as Switch() takes them + auto const bind = []( TTrack &Switch ) { + auto const &extension { *Switch.SwitchExtension }; + auto const state { extension.CurrentIndex & 1 }; + Switch.trNext = extension.pNexts[ state ]; + Switch.trPrev = extension.pPrevs[ state ]; + Switch.iNextDirection = extension.iNextDirection[ state ]; + Switch.iPrevDirection = extension.iPrevDirection[ state ]; + }; + auto &own { *SwitchExtension }; + for( int path = 0; path < 2; ++path ) { + for( int end = 0; end < 2; ++end ) { + if( ( end ? own.pNexts[ path ] : own.pPrevs[ path ] ) != nullptr || own.Segments[ path ] == nullptr ) { + continue; + } + auto const point { end ? own.Segments[ path ]->FastGetPoint_1() : own.Segments[ path ]->FastGetPoint_0() }; + auto const [ other, endpoint ] { simulation::Region->find_path( point, this ) }; + if( other == nullptr || other == this || other->eType != tt_Switch || other->SwitchExtension == nullptr || endpoint < 2 || endpoint > 5 ) { + continue; + } + auto &theirs { *other->SwitchExtension }; + if( end == 0 ) { + own.pPrevs[ 0 ] = own.pPrevs[ 1 ] = other; + own.iPrevDirection[ 0 ] = own.iPrevDirection[ 1 ] = code( endpoint ); + } + else { + own.pNexts[ path ] = other; + own.iNextDirection[ path ] = code( endpoint ); + } + auto const ours { end == 0 ? 0 : path ? 3 : 1 }; + if( endpoint == 2 || endpoint == 4 ) { + theirs.pPrevs[ 0 ] = theirs.pPrevs[ 1 ] = this; + theirs.iPrevDirection[ 0 ] = theirs.iPrevDirection[ 1 ] = ours; + } + else { + auto const theirpath { endpoint == 5 ? 1 : 0 }; + theirs.pNexts[ theirpath ] = this; + theirs.iNextDirection[ theirpath ] = ours; + } + bind( *other ); + } + } + bind( *this ); +} + bool TTrack::Switch(int i, float const t, float const d) { // przełączenie torów z uruchomieniem animacji if (SwitchExtension) // tory przełączalne mają doklejkę @@ -3947,6 +4000,13 @@ path_table::InitTracks() { } } + // the plain tracks are joined to the switches above, a switch right after another one only here, once they are + for( auto *track : m_items ) { + if( track->eType == tt_Switch ) { + track->join_switches(); + } + } + if( Global.CreateSwitchTrackbeds ) { // do this after all connections are established, otherwise missing switch connections // may prevent us from obtaining texture data for basic track from 'across' a switch diff --git a/world/Track.h b/world/Track.h index ed1225c5..bfd32136 100644 --- a/world/Track.h +++ b/world/Track.h @@ -315,6 +315,9 @@ public: material_handle rail_material( int const Path ) const; glm::vec2 tread_band() const; bool SetConnections(int i); + // joins the free ends of the switch to the free ends of other switches in the same points, which the plain tracks joined to switches + // leave out: a switch right after another one, at the end of either path of it, two facing at their starts or meeting at their ends + void join_switches(); bool Switch(int i, float const t = -1.f, float const d = -1.f); bool SwitchForced(int i, TDynamicObject *o); int CrossSegment(int from, int into);