- if(&block==pending_block && !train && !reserving)
- reserve_more();
-}
-
-void Train::reserve_more()
-{
- if(!active || blocks.empty() || end_of_route)
- return;
-
- BlockIter start = blocks.back();
-
- pending_block = 0;
- preceding_train = 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());
- }
-
- 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;
- list<BlockIter>::iterator good_end = blocks.end();
- Track *divert_track = 0;
- bool try_divert = false;
- Train *blocking_train = 0;
- BlockList contested_blocks;
-
- SetFlag setf(reserving);
-
- while(1)
- {
- BlockIter last = block;
- block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
- if(!block || block->get_endpoints().size()<2)
- {
- if(!blocking_train)
- {
- good_end = blocks.end();
- end_of_route = true;
- }
- 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_end = blocks.end();
- end_of_route = true;
- }
- break;
- }
- }
-
- if(block->get_turnout_id() && !last->get_turnout_id())
- {
- /* We can keep the blocks if we arrive at a turnout from a non-turnout
- block. Having a turnout block as our last reserved block is not good
- as it would limit our diversion possibilities for little benefit. */
- good_end = blocks.end();
- if(nsens>=3 && dist>=min_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_end = blocks.end();
-
- if(static_cast<unsigned>(other_entry)==block.entry())
- preceding_train = other_train;
- }
-
- 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 || !other_train->is_active()))
- // 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.endpoint();
- bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
-
- 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(clear_blocks_end==blocks.end())
- --clear_blocks_end;
- if(good_end==blocks.end())
- --good_end;
-
- if(block->get_sensor_id())
- ++nsens;
- if(nsens>0)
- dist += block->get_path_length(block.entry());
- }
-
- // Unreserve blocks that were not good
- release_blocks(good_end, blocks.end());
-
- if(blocks.back()!=start)
- // We got some new blocks, so no longer need to yield
- yielding_to = 0;
-
- check_turnout_paths(true);
-
- // Make any sensorless blocks at the beginning immediately current
- while(cur_blocks_end!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
- ++cur_blocks_end;
-
- if(try_divert && divert(*divert_track))
- reserve_more();
-}
-
-void Train::check_turnout_paths(bool set)
-{
- if(clear_blocks_end==blocks.end())
- return;
-
- for(list<BlockIter>::iterator i=clear_blocks_end; i!=blocks.end(); ++i)
- {
- if((*i)->get_turnout_id())
- {
- TrackIter track = i->track_iter();
- const TrackType::Endpoint &track_ep = track.endpoint();
-
- unsigned path = 0;
- list<BlockIter>::iterator j = i;
- if(++j!=blocks.end())
- {
- TrackIter rev = j->track_iter().flip();
- unsigned mask = rev.endpoint().paths&track_ep.paths;
- for(path=0; mask>1; mask>>=1, ++path) ;
- }
- else
- return;
-
- if(path!=track->get_active_path())
- {
- if(set)
- track->set_active_path(path);
-
- /* Check again, in case the driver was able to service the request
- instantly */
- if(!set || path!=track->get_active_path())
- continue;
- }
- }
-
- if(i==clear_blocks_end)
- ++clear_blocks_end;
- }
-}
-
-float Train::get_reserved_distance_until(const Block *until_block, bool back) const
-{
- if(blocks.empty())