+}
+
+unsigned Train::reserve_more()
+{
+ if(!active || blocks.empty())
+ return 0;
+
+ BlockIter start = blocks.back();
+
+ pending_block = 0;
+
+ // See how many sensor blocks and how much track we already have
+ unsigned nsens = 0;
+ float dist = 0;
+ for(BlockList::const_iterator i=cur_blocks_end; i!=blocks.end(); ++i)
+ {
+ if((*i)->get_sensor_id())
+ ++nsens;
+ if(nsens>0)
+ dist += (*i)->get_path_length(i->entry());
+ }
+
+ if(end_of_route)
+ return nsens;
+
+ list<RouteRef>::iterator cur_route = routes.begin();
+ advance_route(cur_route, *start.track_iter());
+
+ float approach_margin = 50*layout.get_catalogue().get_scale();
+ float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
+
+ BlockIter block = start;
+ BlockIter good = start;
+ Track *divert_track = 0;
+ bool try_divert = false;
+ unsigned good_sens = nsens;
+ float good_dist = dist;
+ Train *blocking_train = 0;
+ BlockList contested_blocks;
+
+ SetFlag setf(reserving);
+
+ while(good_sens<3 || good_dist<min_dist || !contested_blocks.empty())
+ {
+ BlockIter last = block;
+ block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
+ if(!block)
+ break;
+
+ TrackIter track = block.track_iter();
+
+ if(cur_route!=routes.end())
+ {
+ if(!advance_route(cur_route, *track))
+ {
+ // Keep the blocks if we arrived at the end of the route
+ if(!blocking_train)
+ {
+ good = last;
+ good_sens = nsens;
+ good_dist = dist;
+ end_of_route = true;
+ }
+ break;
+ }
+ }
+ else if(!routes.empty() && routes.front().route->has_track(*track))
+ cur_route = routes.begin();
+
+ if(block->get_endpoints().size()<2)
+ {
+ if(!blocking_train)
+ {
+ good = last;
+ good_sens = nsens;
+ good_dist = dist;
+ }
+ break;
+ }
+
+ if(blocking_train)
+ {
+ if(block->get_train()!=blocking_train)
+ {
+ if(blocking_train->free_block(*contested_blocks.back()))
+ {
+ // Roll back and start actually reserving the blocks
+ block = blocks.back();
+ cur_route = routes.begin();
+ advance_route(cur_route, *block.track_iter().track());
+ if(blocking_train->get_priority()==priority)
+ blocking_train->yield_to(*this);
+ blocking_train = 0;
+ continue;
+ }
+ else
+ {
+ yield_to(*blocking_train);
+ pending_block = contested_blocks.front().block();
+ try_divert = divert_track;
+ break;
+ }
+ }
+ else
+ {
+ contested_blocks.push_back(block);
+ continue;
+ }
+ }
+
+ bool reserved = block->reserve(this);
+ if(!reserved)
+ {
+ /* We've found another train. If it wants to exit the block from the
+ same endpoint we're trying to enter from or the other way around,
+ treat it as coming towards us. Otherwise treat it as going in the
+ same direction. */
+ Train *other_train = block->get_train();
+ int other_entry = other_train->get_entry_to_block(*block);
+ if(other_entry<0)
+ throw LogicError("Block reservation inconsistency");
+
+ unsigned exit = block.reverse().entry();
+ unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
+ bool entry_conflict = (block.entry()==other_exit);
+ bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
+ if(!entry_conflict && !last->get_turnout_id())
+ {
+ /* The other train is not coming to the blocks we're holding, so we
+ can keep them. */
+ good = last;
+ good_sens = nsens;
+ good_dist = dist;
+ }
+
+ int other_prio = other_train->get_priority();
+
+ if(!entry_conflict && !exit_conflict && other_prio<priority)
+ {
+ /* Ask a lesser priority train going to the same direction to free
+ the block for us */
+ if(other_train->free_block(*block))
+ reserved = block->reserve(this);
+ }
+ else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
+ {
+ /* A lesser priority train is coming at us, we must ask it to free
+ enough blocks to get clear of it to avoid a potential deadlock */
+ blocking_train = other_train;
+ contested_blocks.clear();
+ contested_blocks.push_back(block);
+ continue;
+ }
+ else if(divert_track && (entry_conflict || exit_conflict))
+ // We are blocked, but there's a diversion possibility
+ try_divert = true;
+
+ if(!reserved)
+ {
+ pending_block = &*block;
+ break;
+ }
+ }
+
+ if(block->get_turnout_id())
+ {
+ const TrackType::Endpoint &track_ep = track->get_type().get_endpoints()[track.entry()];
+ bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
+
+ if(multiple_paths || !last->get_turnout_id())
+ {
+ /* We can keep the blocks reserved so far if we are facing the
+ points or if there was no turnout immediately before this one.
+ With multiple successive turnouts (as is common in crossovers) it's
+ best to hold at one we can divert from. */
+ good = last;
+ good_sens = nsens;
+ good_dist = dist;
+ }
+
+ // Figure out what path we'd like to take on the turnout
+ int path = -1;
+ for(list<RouteRef>::iterator i=cur_route; (path<0 && i!=routes.end()); ++i)
+ path = i->route->get_turnout(block->get_turnout_id());
+ if(path<0)
+ path = track->get_active_path();
+ if(!(track_ep.paths&(1<<path)))
+ {
+ for(unsigned i=0; track_ep.paths>>i; ++i)
+ if(track_ep.paths&(1<<i))
+ path = i;
+ }
+
+ if(path!=static_cast<int>(track->get_active_path()))
+ {
+ // The turnout is set to wrong path - switch and wait for it
+ pending_block = &*block;
+ track->set_active_path(path);
+ if(pending_block)
+ {
+ block->reserve(0);
+ break;
+ }
+ }
+
+ if(multiple_paths && cur_route!=routes.end() && cur_route->diversion!=block->get_turnout_id())
+ /* There's multiple paths to be taken and we are on a route - take
+ note of the diversion possibility */
+ divert_track = &*track;
+ }
+
+ if(!contested_blocks.empty() && contested_blocks.front()==block)
+ contested_blocks.pop_front();
+
+ blocks.push_back(block);
+ if(cur_blocks_end==blocks.end())
+ --cur_blocks_end;
+ if(block->get_sensor_id())
+ ++nsens;
+ if(nsens>0)
+ dist += block->get_path_length(block.entry());