+ free_noncritical_blocks();
+
+ BlockRef &last = (rsv_blocks.empty() ? cur_blocks.back() : rsv_blocks.back());
+ BlockRef next = last.next();
+ const Block::Endpoint &ep = next.block->get_endpoints()[next.entry];
+
+ set_route(Route::find(*ep.track, ep.track_ep, to));
+}
+
+bool Train::divert(Track &from)
+{
+ if(!from.get_turnout_id())
+ throw InvalidParameterValue("Can't divert from a non-turnout");
+ if(routes.empty())
+ return false;
+
+ int path = -1;
+ unsigned from_ep = 0;
+ list<RouteRef>::iterator route = routes.begin();
+ Block *block = cur_blocks.back().block;
+ unsigned entry = cur_blocks.back().entry;
+ set<const Track *> visited;
+
+ // Follow our routes to find out where we're entering the turnout
+ while(1)
+ {
+ Block *link = block->get_link(block->traverse(entry, route->route));
+ entry = link->get_endpoint_by_link(*block);
+ block = link;
+
+ const Block::Endpoint &entry_ep = block->get_endpoints()[entry];
+
+ if(visited.count(entry_ep.track))
+ return false;
+ visited.insert(entry_ep.track);
+
+ if(!advance_route(route, *entry_ep.track))
+ return false;
+
+ if(entry_ep.track==&from)
+ {
+ if(block->get_train()==this && !free_block(*block))
+ return false;
+
+ from_ep = entry_ep.track_ep;
+ path = route->route->get_turnout(from.get_turnout_id());
+ break;
+ }
+ }
+
+ // Check that more than one path is available
+ unsigned ep_paths = from.get_type().get_endpoints()[from_ep].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!=path)
+ {
+ path = i;
+ break;
+ }
+
+ Track *track = from.get_link(from.traverse(from_ep, path));
+ if(!track)
+ return false;
+
+ unsigned ep = track->get_endpoint_by_link(from);
+
+ set<const 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());
+ Route *diversion = 0;
+ try
+ {
+ diversion = Route::find(*track, ep, tracks);
+ }
+ catch(const Msp::Exception &)
+ {
+ return false;
+ }
+
+ diversion->set_name("Diversion");
+ diversion->add_track(from);
+ diversion->set_turnout(from.get_turnout_id(), path);
+
+ if(!is_valid_diversion(*diversion, from, from_ep))
+ {
+ delete diversion;
+ return false;
+ }
+
+ // Follow the diversion route until we get back to the original route
+ list<RouteRef>::iterator end = routes.end();
+ while(1)
+ {
+ path = 0;
+ if(track->get_turnout_id())
+ path = diversion->get_turnout(track->get_turnout_id());
+ Track *next = track->get_link(track->traverse(ep, path));
+
+ for(list<RouteRef>::iterator i=route; (end==routes.end() && i!=routes.end()); ++i)
+ if(i->route->get_tracks().count(next))
+ end = i;
+
+ if(end!=routes.end())
+ break;
+ else if(!diversion->get_tracks().count(next))
+ throw Exception("Pathfinder returned a bad route");
+
+ ep = next->get_endpoint_by_link(*track);
+ track = next;
+ }
+
+ if(route==end)
+ // We are rejoining the same route we diverted from, duplicate it
+ routes.insert(end, *route);