-bool Train::divert(Track &from)
-{
- if(!from.get_turnout_id())
- throw InvalidParameterValue("Can't divert from a non-turnout");
- if(routes.empty())
- return false;
-
- unsigned path = 0;
- unsigned entry = 0;
- list<RouteRef>::iterator route = routes.begin();
-
- // Follow our routes to find out where we're entering the turnout
- for(TrackLoopIter track = blocks.front().track_iter();;)
- {
- if(!advance_route(route, *track))
- return false;
-
- if(&*track==&from)
- {
- Block &block = track->get_block();
- if(block.get_train()==this && !free_block(block))
- return false;
-
- int route_path = route->route->get_turnout(from.get_turnout_id());
-
- // Check that more than one path is available
- unsigned ep_paths = track.endpoint().paths;
- if(!(ep_paths&(ep_paths-1)))
- return false;
-
- // Choose some other path
- for(int i=0; ep_paths>>i; ++i)
- if((ep_paths&(1<<i)) && i!=route_path)
- {
- path = i;
- break;
- }
-
- entry = track.entry();
- break;
- }
-
- track = track.next(route->route->get_path(*track));
-
- if(!track || track.looped())
- return false;
- }
-
- TrackIter track = TrackIter(&from, entry).next(path);
- if(!track)
- return false;
-
- set<Track *> tracks;
- for(list<RouteRef>::iterator i=routes.begin(); i!=routes.end(); ++i)
- tracks.insert(i->route->get_tracks().begin(), i->route->get_tracks().end());
- RefPtr<Route> diversion = Route::find(track, tracks);
- if(!diversion)
- return false;
-
- diversion->set_name("Diversion");
- diversion->add_track(from);
- diversion->set_turnout(from.get_turnout_id(), path);
-
- if(!is_valid_diversion(*diversion, TrackIter(&from, entry)))
- return false;
-
- // Follow the diversion route until we get back to the original route
- list<RouteRef>::iterator end = routes.end();
- while(1)
- {
- for(list<RouteRef>::iterator i=route; (end==routes.end() && i!=routes.end()); ++i)
- if(i->route->has_track(*track))
- end = i;
-
- if(end!=routes.end())
- break;
- else if(!diversion->has_track(*track))
- throw LogicError("Pathfinder returned a bad route");
-
- track = track.next(diversion->get_path(*track));
- }
-
- if(route==end)
- // We are rejoining the same route we diverted from, duplicate it
- routes.insert(end, *route);
- else
- {
- ++route;
- routes.erase(route, end);
- }
- routes.insert(end, RouteRef(diversion.release(), from.get_turnout_id()));
-
- return true;
-}
-