text: /*
   * This file is part of the Simutrans project under the Artistic License.
   * (see LICENSE.txt)
   */
  
  #include "../simcity.h"
  #include "../simconvoi.h"
  #include "../simfab.h"
  #include "../simhalt.h"
  #include "../simline.h"
  #include "../simmenu.h"
  #include "../simmesg.h"
  #include "../simworld.h"
  
  #include "../bauer/brueckenbauer.h"
  #include "../bauer/hausbauer.h"
  #include "../bauer/tunnelbauer.h"
  #include "../bauer/vehikelbauer.h"
  #include "../bauer/wegbauer.h"
  
  #include "../dataobj/schedule.h"
  #include "../dataobj/loadsave.h"
  #include "../dataobj/marker.h"
  
  #include "../utils/cbuffer_t.h"
  #include "../utils/simrandom.h"
  #include "../utils/simstring.h"
  
  #include "../vehicle/vehicle.h"
  
  #include "ai_passenger.h"
  #include "finance.h"
  
  
  ai_passenger_t::ai_passenger_t(uint8 nr) : ai_t( nr )
  {
  	state = NR_INIT;
  
  	road_vehicle = NULL;
  	road_weg = NULL;
  
  	next_construction_steps = welt->get_steps() + 50;
  
  	road_transport = true;
  	rail_transport = false;
  	air_transport = true;
  	ship_transport = true;
  
  	start_stadt = end_stadt = NULL;
  	ziel = NULL;
  	end_attraction = NULL;
  }
  
  
  /**
   * Activates/deactivates a player
   */
  bool ai_passenger_t::set_active(bool new_state)
  {
  	// only activate, when there are buses available!
  	if(  new_state  ) {
  		new_state = NULL!=vehikel_search( road_wt, 50, 80, goods_manager_t::passengers, false);
  	}
  	return player_t::set_active( new_state );
  }
  
  
  /** return the hub of a city (always the very first stop) or zero
   */
  halthandle_t ai_passenger_t::get_our_hub( const stadt_t *s ) const
  {
  	FOR(vector_tpl<halthandle_t>, const halt, haltestelle_t::get_alle_haltestellen()) {
  		if (halt->get_owner() == sim::up_cast<player_t const*>(this)) {
  			if(  halt->get_pax_enabled()  &&  (halt->get_station_type()&haltestelle_t::busstop)!=0  ) {
  				koord h=halt->get_basis_pos();
  				if(h.x>=s->get_linksoben().x  &&  h.y>=s->get_linksoben().y  &&  h.x<=s->get_rechtsunten().x  &&  h.y<=s->get_rechtsunten().y  ) {
  					return halt;
  				}
  			}
  		}
  	}
  	return halthandle_t();
  }
  
  
  koord ai_passenger_t::find_area_for_hub( const koord lo, const koord ru, const koord basis ) const
  {
  	// no found found
  	koord best_pos = koord::invalid;
  	// "shortest" distance
  	int dist = 999;
  
  	for( sint16 x=lo.x;  x<=ru.x;  x++ ) {
  		for( sint16 y=lo.y;  y<=ru.y;  y++ ) {
  			const koord trypos(x,y);
  			const grund_t * gr = welt->lookup_kartenboden(trypos);
  			if(gr) {
  				// flat, solid
  				if(  gr->get_typ()==grund_t::boden  &&  gr->get_grund_hang()==slope_t::flat  ) {
  					const obj_t* obj = gr->obj_bei(0);
  					int test_dist = koord_distance( trypos, basis );
  					if (!obj || !obj->get_owner() || obj->get_owner() == sim::up_cast<player_t const*>(this)) {
  						if(  gr->is_halt()  &&  check_owner( gr->get_halt()->get_owner(), this )  &&  gr->hat_weg(road_wt)  ) {
  							// ok, one halt belongs already to us ... (should not really happen!) but might be a public stop
  							return trypos;
  						} else if(  test_dist<dist  &&  gr->hat_weg(road_wt)  &&  !gr->is_halt()  ) {
  							ribi_t::ribi  ribi = gr->get_weg_ribi_unmasked(road_wt);
  							if(  ribi_t::is_straight(ribi)  ||  ribi_t::is_single(ribi)  ) {
  								best_pos = trypos;
  								dist = test_dist;
  							}
  						} else if(  test_dist+2<dist  &&  gr->ist_natur()  ) {
  							// also ok for a stop, but second choice
  							// so we gave it a malus of 2
  							best_pos = trypos;
  							dist = test_dist+2;
  						}
  					}
  				}
  			}
  		}
  	}
  DBG_MESSAGE("ai_passenger_t::find_area_for_hub()","suggest hub at (%i,%i)",best_pos.x,best_pos.y);
  	return best_pos;
  }
  
  
  /**
   * tries to built a hub near the koordinate
   */
  koord ai_passenger_t::find_place_for_hub( const stadt_t *s ) const
  {
  	halthandle_t h = get_our_hub( s );
  	if(h.is_bound()) {
  		return h->get_basis_pos();
  	}
  	return find_area_for_hub( s->get_linksoben(), s->get_rechtsunten(), s->get_pos() );
  }
  
  
  koord ai_passenger_t::find_harbour_pos(karte_t* welt, const stadt_t *s )
  {
  	koord bestpos = koord::invalid, k;
  	sint32 bestdist = 999999;
  
  	// try to find an airport place as close to the city as possible
  	for(  k.y=max(6,s->get_linksoben().y-10); k.y<=min(welt->get_size().y-3-6,s->get_rechtsunten().y+10); k.y++  ) {
  		for(  k.x=max(6,s->get_linksoben().x-25); k.x<=min(welt->get_size().x-3-6,s->get_rechtsunten().x+10); k.x++  ) {
  			sint32 testdist = koord_distance( k, s->get_pos() );
  			if(  testdist<bestdist  ) {
  				if(  k.x+2<s->get_linksoben().x  ||  k.y+2<s->get_linksoben().y  ||  k.x>=s->get_rechtsunten().x  ||  k.y>=s->get_rechtsunten().y  ) {
  					// malus for out of town
  					testdist += 5;
  				}
  				if(  testdist<bestdist  ) {
  					grund_t *gr = welt->lookup_kartenboden(k);
  					slope_t::type hang = gr->get_grund_hang();
  					if(  gr->ist_natur()  &&  gr->get_hoehe() == welt->get_water_hgt(k)  &&  slope_t::is_way(hang)  &&  welt->is_water( k - koord(hang), koord(hang) * 4 + koord(1, 1) )  ) {
  						// can built busstop here?
  						koord bushalt = k+koord(hang);
  						gr = welt->lookup_kartenboden(bushalt);
  						if(gr  &&  gr->ist_natur()) {
  							bestpos = k;
  							bestdist = testdist;
  						}
  					}
  				}
  			}
  		}
  	}
  	return bestpos;
  }
  
  
  bool ai_passenger_t::create_water_transport_vehikel(const stadt_t* start_stadt, const koord target_pos)
  {
  	const vehicle_desc_t *v_desc = vehikel_search(water_wt, 10, 40, goods_manager_t::passengers, false);
  	if(v_desc==NULL  ) {
  		// no ship there
  		return false;
  	}
  
  	if(  convoihandle_t::is_exhausted()  ) {
  		// too many convois => cannot do anything about this ...
  		return false;
  	}
  
  	stadt_t *end_stadt = NULL;
  	grund_t *ziel = welt->lookup_kartenboden(target_pos);
  	gebaeude_t *gb = ziel->find<gebaeude_t>();
  	if(gb  &&  gb->is_townhall()) {
  		end_stadt = gb->get_stadt();
  	}
  	else if(!ziel->is_halt()  ||  !ziel->is_water()) {
  		// not townhall, not factory => we will not built this line for attractions!
  		return false;
  	}
  
  	halthandle_t start_hub = get_our_hub( start_stadt );
  	halthandle_t start_connect_hub;
  	koord start_harbour = koord::invalid;
  	if(start_hub.is_bound()) {
  		if(  (start_hub->get_station_type()&haltestelle_t::dock)==0  ) {
  			start_connect_hub = start_hub;
  			start_hub = halthandle_t();
  			// is there already one harbour next to this one?
  			FOR(vector_tpl<haltestelle_t::connection_t>, const& i, start_connect_hub->get_pax_connections()) {
  				halthandle_t const h = i.halt;
  				if( h->get_station_type()&haltestelle_t::dock  ) {
  					start_hub = h;
  					break;
  				}
  			}
  		}
  		else {
  			start_harbour = start_hub->get_basis_pos();
  		}
  	}
  	// find an dock place
  	if(!start_hub.is_bound()) {
  		start_harbour = find_harbour_pos( welt, start_stadt );
  	}
  	if(start_harbour==koord::invalid) {
  		// sorry, no suitable place
  		return false;
  	}
  	// same for end
  	halthandle_t end_hub = end_stadt ? get_our_hub( end_stadt ) : ziel->get_halt();
  	halthandle_t end_connect_hub;
  	koord end_harbour = koord::invalid;
  	if(end_hub.is_bound()) {
  		if(  (end_hub->get_station_type()&haltestelle_t::dock)==0  ) {
  			end_connect_hub = end_hub;
  			end_hub = halthandle_t();
  			// is there already one harbour next to this one?
  			FOR(vector_tpl<haltestelle_t::connection_t>, const& i, end_connect_hub->get_pax_connections()) {
  				halthandle_t const h = i.halt;
  				if( h->get_station_type()&haltestelle_t::dock  ) {
  					start_hub = h;
  					break;
  				}
  			}
  		}
  		else {
  			end_harbour = end_hub->get_basis_pos();
  		}
  	}
  	if(!end_hub.is_bound()  &&  end_stadt) {
  		// find an dock place
  		end_harbour = find_harbour_pos( welt, end_stadt );
  	}
  	if(end_harbour==koord::invalid) {
  		// sorry, no suitable place
  		return false;
  	}
  
  	const koord start_dx(welt->lookup_kartenboden(start_harbour)->get_grund_hang());
  	const koord end_dx(welt->lookup_kartenboden(end_harbour)->get_grund_hang());
  
  	// now we must check, if these two seas are connected
  	{
  		// we use the free own vehicle_desc_t
  		vehicle_desc_t remover_desc( water_wt, 500, vehicle_desc_t::diesel );
  		vehicle_t* test_driver = vehicle_builder_t::build( koord3d( start_harbour - start_dx, welt->get_water_hgt( start_harbour - start_dx ) ), this, NULL, &remover_desc );
  		test_driver->set_flag( obj_t::not_on_map );
  		route_t verbindung;
  		bool connected = verbindung.calc_route( welt, koord3d( start_harbour - start_dx, welt->get_water_hgt( start_harbour - start_dx ) ), koord3d( end_harbour - end_dx, welt->get_water_hgt( end_harbour - end_dx ) ), test_driver, 0, 0 );
  		delete test_driver;
  		if(!connected) {
  			return false;
  		}
  	}
  
  	// build the harbour if necessary
  	if(!start_hub.is_bound()) {
  		koord bushalt = start_harbour+start_dx;
  		const koord town_road = find_place_for_hub( start_stadt );
  		// first: built street to harbour
  		if(town_road!=bushalt) {
  			way_builder_t bauigel(this);
  			// no bridges => otherwise first tile might be bridge start ...
  			bauigel.init_builder( way_builder_t::strasse, way_builder_t::weg_search( road_wt, 25, welt->get_timeline_year_month(), type_flat ), tunnel_builder_t::get_tunnel_desc(road_wt,road_vehicle->get_topspeed(),welt->get_timeline_year_month()), NULL );
  			bauigel.set_keep_existing_faster_ways(true);
  			bauigel.set_keep_city_roads(true);
  			bauigel.set_maximum(10000);
  			bauigel.calc_route( welt->lookup_kartenboden(bushalt)->get_pos(), welt->lookup_kartenboden(town_road)->get_pos() );
  			if(bauigel.get_count()-1 <= 1) {
  				return false;
  			}
  			bauigel.build();
  		}
  	}
  	if(!end_hub.is_bound()) {
  		koord bushalt = end_harbour+end_dx;
  		const koord town_road = find_place_for_hub( end_stadt );
  		// first: built street to harbour
  		if(town_road!=bushalt) {
  			way_builder_t bauigel(this);
  			// no bridges => otherwise first tile might be bridge start ...
  			bauigel.init_builder( way_builder_t::strasse, way_builder_t::weg_search( road_wt, 25, welt->get_timeline_year_month(), type_flat ), tunnel_builder_t::get_tunnel_desc(road_wt,road_vehicle->get_topspeed(),welt->get_timeline_year_month()), NULL );
  			bauigel.set_keep_existing_faster_ways(true);
  			bauigel.set_keep_city_roads(true);
  			bauigel.set_maximum(10000);
  			bauigel.calc_route( welt->lookup_kartenboden(bushalt)->get_pos(), welt->lookup_kartenboden(town_road)->get_pos() );
  			if(bauigel.get_count()-1 <= 1) {
  				return false;
  			}
  			bauigel.build();
  		}
  	}
  	// now built the stops ... (since the roads were ok!)
  	if(!start_hub.is_bound()) {
  		/* first we must built the bus stop, since this will be the default stop for all our buses
  		 * we want to keep the name of a dock, thus we will create it beforehand
  		 */
  		koord bushalt = start_harbour+start_dx;
  		const building_desc_t* busstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX );
  		// now built the bus stop
  		if(!call_general_tool( TOOL_BUILD_STATION, bushalt, busstop_desc->get_name() )) {
  			return false;
  		}
  		// and change name to dock ...
  		grund_t *gr = welt->lookup_kartenboden(bushalt);
  		halthandle_t halt = gr ? gr->get_halt() : halthandle_t();
  		if (halt.is_bound()) { // it should be, but avoid crashes
  			char* const name = halt->create_name(bushalt, "Dock");
  			halt->set_name( name );
  			free(name);
  		}
  		// finally built the dock
  		const building_desc_t* dock_desc = hausbauer_t::get_random_station(building_desc_t::dock, water_wt, welt->get_timeline_year_month(), 0);
  		welt->lookup_kartenboden(start_harbour)->obj_loesche_alle(this);
  		call_general_tool( TOOL_BUILD_STATION, start_harbour, dock_desc->get_name() );
  		grund_t *harbour_gr = welt->lookup_kartenboden(start_harbour);
  		start_hub = harbour_gr ? harbour_gr->get_halt() : halthandle_t();
  		// eventually we must built a hub in the next town
  		start_connect_hub = get_our_hub( start_stadt );
  		if(!start_connect_hub.is_bound()) {
  			koord sch = find_place_for_hub( start_stadt );
  			call_general_tool( TOOL_BUILD_STATION, sch, busstop_desc->get_name() );
  			start_connect_hub = get_our_hub( start_stadt );
  			assert( start_connect_hub.is_bound() );
  		}
  	}
  	if(!end_hub.is_bound()) {
  		/* again we must built the bus stop first, since this will be the default stop for all our buses
  		 * we want to keep the name of a dock, thus we will create it beforehand
  		 */
  		koord bushalt = end_harbour+end_dx;
  		const building_desc_t* busstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX );
  		// now built the bus stop
  		if(!call_general_tool( TOOL_BUILD_STATION, bushalt, busstop_desc->get_name() )) {
  			return false;
  		}
  		// and change name to dock ...
  		grund_t *gr = welt->lookup_kartenboden(bushalt);
  		halthandle_t halt = gr ? gr->get_halt() : halthandle_t();
  		if (halt.is_bound()) { // it should be, but avoid crashes
  			char* const name = halt->create_name(bushalt, "Dock");
  			halt->set_name( name );
  			free(name);
  		}
  		// finally built the dock
  		const building_desc_t* dock_desc = hausbauer_t::get_random_station(building_desc_t::dock, water_wt, welt->get_timeline_year_month(), 0 );
  		welt->lookup_kartenboden(end_harbour)->obj_loesche_alle(this);
  		call_general_tool( TOOL_BUILD_STATION, end_harbour, dock_desc->get_name() );
  		grund_t *harbour_gr = welt->lookup_kartenboden(end_harbour);
  		end_hub = harbour_gr ? harbour_gr->get_halt() : halthandle_t();
  		// eventually we must built a hub in the next town
  		end_connect_hub = get_our_hub( end_stadt );
  		if(!end_connect_hub.is_bound()) {
  			koord ech = find_place_for_hub( end_stadt );
  			call_general_tool( TOOL_BUILD_STATION, ech, busstop_desc->get_name() );
  			end_connect_hub = get_our_hub( end_stadt );
  			assert( end_connect_hub.is_bound() );
  		}
  	}
  
  	uint16 const cov = welt->get_settings().get_station_coverage();
  	// now we have harbour => find start position for ships
  	koord pos1 = start_harbour-start_dx;
  	koord start_pos = pos1;
  	for (int y = pos1.y - cov; y <= pos1.y + cov; ++y) {
  		for (int x = pos1.x - cov; x <= pos1.x + cov; ++x) {
  			koord p(x,y);
  			const planquadrat_t *plan = welt->access(p);
  			if(plan) {
  				grund_t *gr = plan->get_kartenboden();
  				if(  gr->is_water()  &&  !gr->get_halt().is_bound()  ) {
  					if(plan->get_haltlist_count()>=1  &&  plan->get_haltlist()[0]==start_hub  &&  koord_distance(start_pos,end_harbour)>koord_distance(p,end_harbour)) {
  						start_pos = p;
  					}
  				}
  			}
  		}
  	}
  	// now we have harbour => find start position for ships
  	pos1 = end_harbour-end_dx;
  	koord end_pos = pos1;
  	for (int y = pos1.y - cov; y <= pos1.y + cov; ++y) {
  		for (int x = pos1.x - cov; x <= pos1.x + cov; ++x) {
  			koord p(x,y);
  			const planquadrat_t *plan = welt->access(p);
  			if(plan) {
  				grund_t *gr = plan->get_kartenboden();
  				if(  gr->is_water()  &&  !gr->get_halt().is_bound()  ) {
  					if(plan->get_haltlist_count()>=1  &&  plan->get_haltlist()[0]==end_hub  &&  koord_distance(end_pos,start_harbour)>koord_distance(p,start_harbour)) {
  						end_pos = p;
  					}
  				}
  			}
  		}
  	}
  
  	// since 86.01 we use lines for vehicles ...
  	schedule_t *schedule=new ship_schedule_t();
  	schedule->append( welt->lookup_kartenboden(start_pos), 0, 0 );
  	schedule->append( welt->lookup_kartenboden(end_pos), 90, 0 );
  	schedule->set_current_stop( 1 );
  	schedule->finish_editing();
  	linehandle_t line=simlinemgmt.create_line(simline_t::shipline,this,schedule);
  	delete schedule;
  
  	// now create one ship
  	vehicle_t* v = vehicle_builder_t::build( koord3d( start_pos, welt->get_water_hgt( start_pos ) ), this, NULL, v_desc );
  	convoi_t* cnv = new convoi_t(this);
  	cnv->set_name(v->get_desc()->get_name());
  	cnv->add_vehikel( v );
  	cnv->set_line(line);
  	cnv->start();
  
  	// eventually build a shuttle bus ...
  	if(start_connect_hub.is_bound()  &&  start_connect_hub!=start_hub) {
  		koord stops[2] = { start_harbour+start_dx, start_connect_hub->get_basis_pos() };
  		create_bus_transport_vehikel( stops[1], 1, stops, 2, false );
  	}
  
  	// eventually build a airport shuttle bus ...
  	if(end_connect_hub.is_bound()  &&  end_connect_hub!=end_hub) {
  		koord stops[2] = { end_harbour+end_dx, end_connect_hub->get_basis_pos() };
  		create_bus_transport_vehikel( stops[1], 1, stops, 2, false );
  	}
  
  	return true;
  }
  
  
  halthandle_t ai_passenger_t::build_airport(const stadt_t* city, koord pos, int rotation)
  {
  	// not too close to border?
  	if(pos.x<6  ||  pos.y<6  ||  pos.x+3+6>=welt->get_size().x  ||  pos.y+3+6>=welt->get_size().y  ) {
  		return halthandle_t();
  	}
  	// ok, not prematurely doomed
  	// can we built airports at all?
  	const way_desc_t *taxi_desc = way_builder_t::weg_search( air_wt, 25, welt->get_timeline_year_month(), type_flat );
  	const way_desc_t *runway_desc = way_builder_t::weg_search( air_wt, 250, welt->get_timeline_year_month(), type_runway );
  	if(taxi_desc==NULL  ||  runway_desc==NULL) {
  		return halthandle_t();
  	}
  	// first, check if at least one tile is within city limits
  	const koord lo = city->get_linksoben();
  	koord size(3-1,3-1);
  	// make sure pos is within city limits!
  	if(pos.x<lo.x) {
  		pos.x += size.x;
  		size.x = -size.x;
  	}
  	if(pos.y<lo.y) {
  		pos.y += size.y;
  		size.y = -size.y;
  	}
  	// find coord to connect in the town
  	halthandle_t hub = get_our_hub( city );
  	const koord town_road = hub.is_bound() ? hub->get_basis_pos() : find_place_for_hub( city );
  	if(  town_road==koord::invalid) {
  		return halthandle_t();
  	}
  	// ok, now we could built it => flatten the land
  	sint8 h = max( welt->get_water_hgt(pos) + 1, welt->lookup_kartenboden(pos)->get_hoehe() );
  	const koord dx( size.x/2, size.y/2 );
  	for(  sint16 i=0;  i!=size.y+dx.y;  i+=dx.y  ) {
  		for( sint16 j=0;  j!=size.x+dx.x;  j+=dx.x  ) {
  			climate c = welt->get_climate(pos+koord(j,i));
  			if(!welt->flatten_tile(this,pos+koord(j,i),h)) {
  				return halthandle_t();
  			}
  			// ensure is land
  			grund_t* bd = welt->lookup_kartenboden(pos+koord(j,i));
  			if (bd->get_typ() == grund_t::wasser) {
  				welt->set_water_hgt(pos+koord(j,i), bd->get_hoehe()-1);
  				welt->access(pos+koord(j,i))->correct_water();
  				welt->set_climate(pos+koord(j,i), c, true);
  			}
  		}
  	}
  	// now taxiways
  	way_builder_t bauigel(this);
  	// 3x3 layout, first we make the taxiway cross
  	koord center=pos+dx;
  	bauigel.init_builder( way_builder_t::luft, taxi_desc, NULL, NULL );
  	bauigel.calc_straight_route( welt->lookup_kartenboden(center+koord::north)->get_pos(), welt->lookup_kartenboden(center+koord::south)->get_pos() );
  	assert(bauigel.get_count()-1 > 1);
  	bauigel.build();
  	bauigel.init_builder( way_builder_t::luft, taxi_desc, NULL, NULL );
  	bauigel.calc_straight_route( welt->lookup_kartenboden(center+koord::west)->get_pos(), welt->lookup_kartenboden(center+koord::east)->get_pos() );
  	assert(bauigel.get_count()-1 > 1);
  	bauigel.build();
  	// now try to connect one of the corners with a road
  	koord bushalt = koord::invalid, runwaystart, runwayend;
  	koord trypos[4] = { koord(0,0), koord(size.x,0), koord(0,size.y), koord(size.x,size.y) };
  	uint32 length=9999;
  	rotation=-1;
  
  	bauigel.init_builder( way_builder_t::strasse, way_builder_t::weg_search( road_wt, 25, welt->get_timeline_year_month(), type_flat ), tunnel_builder_t::get_tunnel_desc(road_wt,road_vehicle->get_topspeed(),welt->get_timeline_year_month()), bridge_builder_t::find_bridge(road_wt,road_vehicle->get_topspeed(),welt->get_timeline_year_month()) );
  	bauigel.set_keep_existing_faster_ways(true);
  	bauigel.set_keep_city_roads(true);
  	bauigel.set_maximum(10000);
  
  	// find the closest one to town ...
  	for(  int i=0;  i<4;  i++  ) {
  		bushalt = pos+trypos[i];
  		bauigel.calc_route(welt->lookup_kartenboden(bushalt)->get_pos(),welt->lookup_kartenboden(town_road)->get_pos());
  		// no road => try next
  		if(  bauigel.get_count() >= 2  &&   bauigel.get_count() < length+1  ) {
  			rotation = i;
  			length = bauigel.get_count()-1;
  		}
  	}
  
  	if(rotation==-1) {
  		// if we every get here that means no connection road => remove airport
  		welt->lookup_kartenboden(center+koord::north)->remove_everything_from_way( this, air_wt, ribi_t::none );
  		welt->lookup_kartenboden(center+koord::south)->remove_everything_from_way( this, air_wt, ribi_t::none );
  		welt->lookup_kartenboden(center+koord::west)->remove_everything_from_way( this, air_wt, ribi_t::none );
  		welt->lookup_kartenboden(center+koord::east)->remove_everything_from_way( this, air_wt, ribi_t::none );
  		welt->lookup_kartenboden(center)->remove_everything_from_way( this, air_wt, ribi_t::all );
  		return halthandle_t();
  	}
  
  	bushalt = pos+trypos[rotation];
  	bauigel.calc_route(welt->lookup_kartenboden(bushalt)->get_pos(),welt->lookup_kartenboden(town_road)->get_pos());
  	bauigel.build();
  	// now the busstop (our next hub ... )
  	const building_desc_t* busstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX );
  	// get an airport name (even though the hub is the bus stop ... )
  	// now built the bus stop
  	if(!call_general_tool( TOOL_BUILD_STATION, bushalt, busstop_desc->get_name() )) {
  		welt->lookup_kartenboden(center+koord::north)->remove_everything_from_way( this, air_wt, ribi_t::none );
  		welt->lookup_kartenboden(center+koord::south)->remove_everything_from_way( this, air_wt, ribi_t::none );
  		welt->lookup_kartenboden(center+koord::west)->remove_everything_from_way( this, air_wt, ribi_t::none );
  		welt->lookup_kartenboden(center+koord::east)->remove_everything_from_way( this, air_wt, ribi_t::none );
  		welt->lookup_kartenboden(center)->remove_everything_from_way( this, air_wt, ribi_t::all );
  		return halthandle_t();
  	}
  	// and change name to airport ...
  	grund_t *gr = welt->lookup_kartenboden(bushalt);
  	halthandle_t halt = gr ? gr->get_halt() : halthandle_t();
  	if (halt.is_bound()) { // it should be, but avoid crashes
  		char* const name = halt->create_name(bushalt, "Airport");
  		halt->set_name( name );
  		free(name);
  	}
  	// built also runway now ...
  	bauigel.init_builder( way_builder_t::luft, runway_desc, NULL, NULL );
  	bauigel.calc_straight_route( welt->lookup_kartenboden(pos+trypos[rotation==0?3:0])->get_pos(), welt->lookup_kartenboden(pos+trypos[1+(rotation&1)])->get_pos() );
  	assert(bauigel.get_count()-1 > 1);
  	bauigel.build();
  	// now the airstops (only on single tiles, this will always work
  	const building_desc_t* airstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, air_wt, welt->get_timeline_year_month(), 0 );
  	for(  int i=0;  i<4;  i++  ) {
  		if(  koord_distance(center+koord::nesw[i],bushalt)==1  &&  ribi_t::is_single( welt->lookup_kartenboden(center+koord::nesw[i])->get_weg_ribi_unmasked(air_wt) )  ) {
  			call_general_tool( TOOL_BUILD_STATION, center+koord::nesw[i], airstop_desc->get_name() );
  		}
  	}
  	// and now the one far away ...
  	for(  int i=0;  i<4;  i++  ) {
  		if(  koord_distance(center+koord::nesw[i],bushalt)>1  &&  ribi_t::is_single( welt->lookup_kartenboden(center+koord::nesw[i])->get_weg_ribi_unmasked(air_wt) )  ) {
  			call_general_tool( TOOL_BUILD_STATION, center+koord::nesw[i], airstop_desc->get_name() );
  		}
  	}
  	// success
  	return halt;
  }
  
  
  static koord find_airport_pos(karte_t* welt, const stadt_t *s )
  {
  	koord bestpos = koord::invalid, k;
  	sint32 bestdist = 999999;
  
  	// try to find an airport place as close to the city as possible
  	for(  k.y=max(6,s->get_linksoben().y-10); k.y<=min(welt->get_size().y-3-6,s->get_rechtsunten().y+10); k.y++  ) {
  		for(  k.x=max(6,s->get_linksoben().x-25); k.x<=min(welt->get_size().x-3-6,s->get_rechtsunten().x+10); k.x++  ) {
  			sint32 testdist = koord_distance( k, s->get_pos() );
  			if(  testdist<bestdist  ) {
  				if(  k.x+2<s->get_linksoben().x  ||  k.y+2<s->get_linksoben().y  ||  k.x>=s->get_rechtsunten().x  ||  k.y>=s->get_rechtsunten().y  ) {
  					// malus for out of town
  					testdist += 5;
  				}
  				if(  testdist<bestdist  &&  welt->square_is_free( k, 3, 3, NULL, ALL_CLIMATES )  ) {
  					bestpos = k;
  					bestdist = testdist;
  				}
  			}
  		}
  	}
  	return bestpos;
  }
  
  
  /** build airports and planes
   */
  bool ai_passenger_t::create_air_transport_vehikel(const stadt_t *start_stadt, const stadt_t *end_stadt)
  {
  	const vehicle_desc_t *v_desc = vehikel_search(air_wt, 10, 900, goods_manager_t::passengers, false);
  	if(v_desc==NULL) {
  		// no aircraft there
  		return false;
  	}
  
  	if(  convoihandle_t::is_exhausted()  ) {
  		// too many convois => cannot do anything about this ...
  		return false;
  	}
  
  	halthandle_t start_hub = get_our_hub( start_stadt );
  	halthandle_t start_connect_hub;
  	koord start_airport;
  	if(start_hub.is_bound()) {
  		if(  (start_hub->get_station_type()&haltestelle_t::airstop)==0  ) {
  			start_connect_hub = start_hub;
  			start_hub = halthandle_t();
  			// is there already one airport next to this town?
  			FOR(vector_tpl<haltestelle_t::connection_t>, const& i, start_connect_hub->get_pax_connections()) {
  				halthandle_t const h = i.halt;
  				if( h->get_station_type()&haltestelle_t::airstop  ) {
  					start_hub = h;
  					break;
  				}
  			}
  		}
  		else {
  			start_airport = start_hub->get_basis_pos();
  		}
  	}
  	// find an airport place
  	if(!start_hub.is_bound()) {
  		start_airport = find_airport_pos( welt, start_stadt );
  		if(start_airport==koord::invalid) {
  			// sorry, no suitable place
  			return false;
  		}
  	}
  	// same for end
  	halthandle_t end_hub = get_our_hub( end_stadt );
  	halthandle_t end_connect_hub;
  	koord end_airport;
  	if(end_hub.is_bound()) {
  		if(  (end_hub->get_station_type()&haltestelle_t::airstop)==0  ) {
  			end_connect_hub = end_hub;
  			end_hub = halthandle_t();
  			// is there already one airport next to this town?
  			FOR(vector_tpl<haltestelle_t::connection_t>, const& i, end_connect_hub->get_pax_connections()) {
  				halthandle_t const h = i.halt;
  				if( h->get_station_type()&haltestelle_t::airstop  ) {
  					start_hub = h;
  					break;
  				}
  			}
  		}
  		else {
  			end_airport = end_hub->get_basis_pos();
  		}
  	}
  	if(!end_hub.is_bound()) {
  		// find an airport place
  		end_airport = find_airport_pos( welt, end_stadt );
  		if(end_airport==koord::invalid) {
  			// sorry, no suitable place
  			return false;
  		}
  	}
  	// eventually construct them
  	if(start_airport!=koord::invalid  &&  end_airport!=koord::invalid) {
  		// build the airport if necessary
  		if(!start_hub.is_bound()) {
  			start_hub = build_airport(start_stadt, start_airport, true);
  			if(!start_hub.is_bound()) {
  				return false;
  			}
  			start_connect_hub = get_our_hub( start_stadt );
  			if(!start_connect_hub.is_bound()) {
  				koord sch = find_place_for_hub( start_stadt );
  				// probably we must construct a city hub, since the airport is outside of the city limits
  				const building_desc_t* busstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX );
  				call_general_tool( TOOL_BUILD_STATION, sch, busstop_desc->get_name() );
  				start_connect_hub = get_our_hub( start_stadt );
  				assert( start_connect_hub.is_bound() );
  			}
  		}
  		if(!end_hub.is_bound()) {
  			end_hub = build_airport(end_stadt, end_airport, true);
  			if(!end_hub.is_bound()) {
  				if (start_hub->get_pax_connections().empty()) {
  					// remove airport busstop
  					welt->lookup_kartenboden(start_hub->get_basis_pos())->remove_everything_from_way( this, road_wt, ribi_t::none );
  					koord center = start_hub->get_basis_pos() + koord( welt->lookup_kartenboden(start_hub->get_basis_pos())->get_weg_ribi_unmasked( air_wt ) );
  					// now the remaining taxi-/runways
  					for( sint16 y=center.y-1;  y<=center.y+1;  y++  ) {
  						for( sint16 x=center.x-1;  x<=center.x+1;  x++  ) {
  							welt->lookup_kartenboden(koord(x,y))->remove_everything_from_way( this, air_wt, ribi_t::none );
  						}
  					}
  				}
  				return false;
  			}
  			end_connect_hub = get_our_hub( end_stadt );
  			if(!end_connect_hub.is_bound()) {
  				koord ech = find_place_for_hub( end_stadt );
  				// probably we must construct a city hub, since the airport is outside of the city limits
  				const building_desc_t* busstop_desc = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX );
  				call_general_tool( TOOL_BUILD_STATION, ech, busstop_desc->get_name() );
  				end_connect_hub = get_our_hub( end_stadt );
  				assert( end_connect_hub.is_bound() );
  			}
  		}
  	}
  	// now we have airports (albeit first tile is a bus stop)
  	const grund_t *start = start_hub->find_matching_position(air_wt);
  	const grund_t *end = end_hub->find_matching_position(air_wt);
  
  	// since 86.01 we use lines for vehicles ...
  	schedule_t *schedule=new airplane_schedule_t();
  	schedule->append( start, 0, 0 );
  	schedule->append( end, 90, 0 );
  	schedule->set_current_stop( 1 );
  	schedule->finish_editing();
  	linehandle_t line=simlinemgmt.create_line(simline_t::airline,this,schedule);
  	delete schedule;
  
  	// now create one plane
  	vehicle_t* v = vehicle_builder_t::build( start->get_pos(), this, NULL, v_desc);
  	convoi_t* cnv = new convoi_t(this);
  	cnv->set_name(v->get_desc()->get_name());
  	cnv->add_vehikel( v );
  	cnv->set_line(line);
  	cnv->start();
  
  	// eventually build a airport shuttle bus ...
  	if(start_connect_hub.is_bound()  &&  start_connect_hub!=start_hub) {
  		koord stops[2] = { start_hub->get_basis_pos(), start_connect_hub->get_basis_pos() };
  		create_bus_transport_vehikel( stops[1], 1, stops, 2, false );
  	}
  
  	// eventually build a airport shuttle bus ...
  	if(end_connect_hub.is_bound()  &&  end_connect_hub!=end_hub) {
  		koord stops[2] = { end_hub->get_basis_pos(), end_connect_hub->get_basis_pos() };
  		create_bus_transport_vehikel( stops[1], 1, stops, 2, false );
  	}
  
  	return true;
  }
  
  
  /** creates a more general road transport
   */
  void ai_passenger_t::create_bus_transport_vehikel(koord startpos2d,int anz_vehikel,koord *stops,int count,bool do_wait)
  {
  DBG_MESSAGE("ai_passenger_t::create_bus_transport_vehikel()","bus at (%i,%i)",startpos2d.x,startpos2d.y);
  	// now start all vehicle one field before, so they load immediately
  	koord3d startpos = welt->lookup_kartenboden(startpos2d)->get_pos();
  
  	// since 86.01 we use lines for road vehicles ...
  	schedule_t *schedule=new truck_schedule_t();
  	// do not start at current stop => wont work ...
  	for(int j=0;  j<count;  j++) {
  		schedule->append(welt->lookup_kartenboden(stops[j]), j == 0 || !do_wait ? 0 : 10, j == 0 || !do_wait ? 0 : 15 );
  	}
  	schedule->set_current_stop( stops[0]==startpos2d );
  	schedule->finish_editing();
  	linehandle_t line=simlinemgmt.create_line(simline_t::truckline,this,schedule);
  	delete schedule;
  
  	// now create all vehicles as convois
  	for(int i=0;  i<anz_vehikel;  i++) {
  		vehicle_t* v = vehicle_builder_t::build(startpos, this, NULL, road_vehicle);
  		if(  convoihandle_t::is_exhausted()  ) {
  			// too many convois => cannot do anything about this ...
  			return;
  		}
  		convoi_t* cnv = new convoi_t(this);
  		// give the new convoi name from first vehicle
  		cnv->set_name(v->get_desc()->get_name());
  		cnv->add_vehikel( v );
  
  		cnv->set_line(line);
  		cnv->start();
  	}
  }
  
  
  /* now we follow all adjacent streets recursively and mark them
   * if they below to this stop, then we continue
   */
  void ai_passenger_t::walk_city(linehandle_t const line, grund_t* const start, int const limit)
  {
  	//maximum number of stops reached?
  	if(line->get_schedule()->get_count()>=limit)  {
  		return;
  	}
  
  	ribi_t::ribi ribi = start->get_weg_ribi(road_wt);
  
  	for(int r=0; r<4; r++) {
  
  		// a way in our direction?
  		if(  (ribi & ribi_t::nesw[r])==0  ) {
  			continue;
  		}
  
  		// ok, if connected, not marked, and not owner by somebody else
  		grund_t *to;
  		if(  start->get_neighbour(to, road_wt, ribi_t::nesw[r] )  &&  !marker->is_marked(to)  &&  check_owner(to->obj_bei(0)->get_owner(),this)  ) {
  
  			// ok, here is a valid street tile
  			marker->mark(to);
  
  			// can built a station here
  			if(  ribi_t::is_straight(to->get_weg_ribi(road_wt))  ) {
  
  				// find out how many tiles we have covered already
  				int covered_tiles=0;
  				int house_tiles=0;
  				uint16 const cov = welt->get_settings().get_station_coverage();
  				for (sint16 y = to->get_pos().y - cov; y <= to->get_pos().y + cov + 1; ++y) {
  					for (sint16 x = to->get_pos().x - cov; x <= to->get_pos().x + cov + 1; ++x) {
  						const planquadrat_t *pl = welt->access(koord(x,y));
  						// check, if we have a passenger stop already here
  						if(pl  &&  pl->get_haltlist_count()>0) {
  							const halthandle_t *hl=pl->get_haltlist();
  							for( uint8 own=0;  own<pl->get_haltlist_count();  own++  ) {
  								if(  hl[own]->is_enabled(goods_manager_t::INDEX_PAS)  ) {
  									// our stop => nothing to do
  #ifdef AUTOJOIN_PUBLIC
  									// we leave also public stops alone
  									if(  hl[own]->get_owner()==this  ||  hl[own]->get_owner()==welt->get_public_player()  ) {
  #else
  									if(  hl[own]->get_owner()==this  ) {
  #endif
  										covered_tiles ++;
  										break;
  									}
  								}
  							}
  						}
  						// check for houses
  						if(pl  &&  pl->get_kartenboden()->get_typ()==grund_t::fundament) {
  							house_tiles++;
  						}
  					}
  				}
  				// now decide, if we build here
  				// just using the ration of covered tiles versus house tiles
  				int const max_tiles = cov * 2 + 1;
  				if(  covered_tiles<(max_tiles*max_tiles)/3  &&  house_tiles>=3  ) {
  					// ok, lets do it
  					const building_desc_t* bs = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX);
  					if(  call_general_tool( TOOL_BUILD_STATION, to->get_pos().get_2d(), bs->get_name() )  ) {
  						//add to line
  						line->get_schedule()->append(to,0); // no need to register it yet; done automatically, when convois will be assigned
  					}
  				}
  				// start road, but no houses anywhere => stop searching
  				if(house_tiles==0) {
  					return;
  				}
  			}
  			// now do recursion
  			walk_city( line, to, limit );
  		}
  	}
  }
  
  
  /* tries to cover a city with bus stops that does not overlap much and cover as much as possible
   * returns the line created, if successful
   */
  void ai_passenger_t::cover_city_with_bus_route(koord start_pos, int number_of_stops)
  {
  	if(  convoihandle_t::is_exhausted()  ) {
  		// too many convois => cannot do anything about this ...
  		return;
  	}
  
  	// nothing in lists
  	marker = &marker_t::instance(welt->get_size().x, welt->get_size().y);
  
  	// and init all stuff for recursion
  	grund_t *start = welt->lookup_kartenboden(start_pos);
  	linehandle_t line = simlinemgmt.create_line( simline_t::truckline,this, new truck_schedule_t() );
  	line->get_schedule()->append(start,0);
  
  	// now create a line
  	walk_city( line, start, number_of_stops );
  	line->get_schedule()->finish_editing();
  
  	road_vehicle = vehikel_search( road_wt, 1, 50, goods_manager_t::passengers, false);
  	if( line->get_schedule()->get_count()>1  ) {
  		// success: add a bus to the line
  		vehicle_t* v = vehicle_builder_t::build(start->get_pos(), this, NULL, road_vehicle);
  		convoi_t* cnv = new convoi_t(this);
  
  		cnv->set_name(v->get_desc()->get_name());
  		cnv->add_vehikel( v );
  
  		cnv->set_line(line);
  		cnv->start();
  	}
  	else {
  		simlinemgmt.delete_line( line );
  	}
  	marker = NULL;
  }
  
  
  void ai_passenger_t::step()
  {
  	// needed for schedule of stops ...
  	player_t::step();
  
  	if(!active) {
  		// I am off ...
  		return;
  	}
  
  	// one route per month ...
  	if(  welt->get_steps() < next_construction_steps  ) {
  		return;
  	}
  
  	switch(state) {
  		case NR_INIT:
  		{
  			// time to update hq?
  			built_update_headquarter();
  
  			// assume fail
  			state = CHECK_CONVOI;
  
  			/* if we have this little money, we do nothing
  			 * The second condition may happen due to extensive replacement operations;
  			 * in such a case it is save enough to expand anyway.
  			 */
  			if(!(finance->get_account_balance()>0  ||  finance->has_money_or_assets())  ) {
  				return;
  			}
  
  			const weighted_vector_tpl<stadt_t*>& staedte = welt->get_cities();
  			int count = staedte.get_count();
  			int offset = (count>1) ? simrand(count-1) : 0;
  			// start with previous target
  			const stadt_t* last_start_stadt=start_stadt;
  			start_stadt = end_stadt;
  			end_stadt = NULL;
  			end_attraction = NULL;
  			ziel = NULL;
  			platz2 = koord::invalid;
  			// if no previous town => find one
  			if(start_stadt==NULL) {
  				// larger start town preferred
  				start_stadt = pick_any_weighted(staedte);
  				offset = staedte.index_of(start_stadt);
  			}
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","using city %s for start",start_stadt->get_name());
  			const halthandle_t start_halt = get_our_hub(start_stadt);
  			// find starting place
  
  if(!start_halt.is_bound()) {
  	DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","new_hub");
  }
  			platz1 = start_halt.is_bound() ? start_halt->get_basis_pos() : find_place_for_hub( start_stadt );
  			if(platz1==koord::invalid) {
  				return;
  			}
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","using place (%i,%i) for start",platz1.x,platz1.y);
  
  			if(count==1  ||  simrand(3)==0) {
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","searching attraction");
  				// 25 % of all connections are tourist attractions
  				const weighted_vector_tpl<gebaeude_t*> &attractions = welt->get_attractions();
  				// this way, we are sure, our factory is connected to this town ...
  				const vector_tpl<stadt_t::factory_entry_t> &fabriken = start_stadt->get_target_factories_for_pax().get_entries();
  				unsigned last_dist = 0xFFFFFFFF;
  				bool ausflug=simrand(2)!=0; // holidays first ...
  				int ziel_count=ausflug?attractions.get_count():fabriken.get_count();
  				for( int i=0;  i<ziel_count;  i++  ) {
  					unsigned dist;
  					koord pos, size;
  
  					if(ausflug) {
  						const gebaeude_t* a = attractions[i];
  						if (a->get_mail_level() <= 25) {
  							// not a good object to go to ...
  							continue;
  						}
  						pos  = a->get_pos().get_2d();
  						size = a->get_tile()->get_desc()->get_size(a->get_tile()->get_layout());
  					}
  					else {
  						const fabrik_t* f = fabriken[i].factory;
  						const factory_desc_t *const desc = f->get_desc();
  						if (( desc->get_pax_demand()==65535 ? desc->get_pax_level() : desc->get_pax_demand() ) <= 10) {
  							// not a good object to go to ... we want more action ...
  							continue;
  						}
  						pos  = f->get_pos().get_2d();
  						size = f->get_desc()->get_building()->get_size(f->get_rotate());
  					}
  					const stadt_t *next_town = welt->find_nearest_city(pos);
  					if(next_town==NULL  ||  start_stadt==next_town) {
  						// this is either a town already served (so we do not create a new hub)
  						// or a lonely point somewhere
  						// in any case we do not want to serve this location already
  						uint16 const c   = welt->get_settings().get_station_coverage();
  						koord  const cov = koord(c, c);
  						koord test_platz=find_area_for_hub(pos-cov,pos+size+cov,pos);
  						if(  !get_halt(test_platz).is_bound()  ) {
  							// not served
  							dist = koord_distance(platz1,test_platz);
  							if(dist+simrand(50)<last_dist  &&   dist>3) {
  								// but closer than the others
  								if(ausflug) {
  									end_attraction = attractions[i];
  								}
  								else {
  									ziel = fabriken[i].factory;
  								}
  								last_dist = dist;
  								platz2 = test_platz;
  							}
  						}
  					}
  				}
  				// test for success
  				if(platz2!=koord::invalid) {
  					// found something
  					state = NR_SAMMLE_ROUTEN;
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","decision: %s wants to built network between %s and %s",get_name(),start_stadt->get_name(),ausflug?end_attraction->get_tile()->get_desc()->get_name():ziel->get_name());
  				}
  			}
  			else {
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","searching town");
  				int last_dist = 9999999;
  				// find a good route
  				for( int i=0;  i<count;  i++  ) {
  					const int nr = (i+offset)%count;
  					const stadt_t* cur = staedte[nr];
  					if(cur!=last_start_stadt  &&  cur!=start_stadt) {
  						halthandle_t end_halt = get_our_hub(cur);
  						int dist = koord_distance(platz1,cur->get_pos());
  						if(  end_halt.is_bound()  &&  is_connected(platz1,end_halt->get_basis_pos(),goods_manager_t::passengers) ) {
  							// already connected
  							continue;
  						}
  						// check if more close
  						if(  dist<last_dist  ) {
  							end_stadt = cur;
  							last_dist = dist;
  						}
  					}
  				}
  				// ok, found two cities
  				if(start_stadt!=NULL  &&  end_stadt!=NULL) {
  					state = NR_SAMMLE_ROUTEN;
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","%s wants to built network between %s and %s",get_name(),start_stadt->get_name(),end_stadt->get_name());
  				}
  			}
  		}
  		break;
  
  		// so far only busses
  		case NR_SAMMLE_ROUTEN:
  		{
  			//
  			koord end_hub_pos = koord::invalid;
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","Find hub");
  			// also for target (if not tourist attraction!)
  			if(end_stadt!=NULL) {
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","try to built connect to city %p", end_stadt );
  				// target is town
  				halthandle_t h = get_our_hub(end_stadt);
  				if(h.is_bound()) {
  					end_hub_pos = h->get_basis_pos();
  				}
  				else {
  					end_hub_pos = find_place_for_hub( end_stadt );
  				}
  			}
  			else {
  				// already found place
  				end_hub_pos = platz2;
  			}
  			// was successful?
  			if(end_hub_pos!=koord::invalid) {
  				// ok, now we check the vehicle
  				platz2 = end_hub_pos;
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","hub found -> NR_BAUE_ROUTE1");
  				state = NR_BAUE_ROUTE1;
  			}
  			else {
  				// no success
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","no suitable hub found");
  				end_stadt = NULL;
  				state = CHECK_CONVOI;
  			}
  		}
  		break;
  
  		// get a suitable vehicle
  		case NR_BAUE_ROUTE1:
  		// wait for construction semaphore
  		{
  			// we want the fastest we can get!
  			road_vehicle = vehikel_search( road_wt, 50, 80, goods_manager_t::passengers, false);
  			if(road_vehicle!=NULL) {
  				// find the best => AI will never survive
  //				road_weg = way_builder_t::weg_search( road_wt, road_vehicle->get_topspeed(), welt->get_timeline_year_month(),type_flat );
  				// find the really cheapest road
  				road_weg = way_builder_t::weg_search( road_wt, 10, welt->get_timeline_year_month(), type_flat );
  				state = NR_BAUE_STRASSEN_ROUTE;
  DBG_MESSAGE("ai_passenger_t::do_passenger_ki()","using %s on %s",road_vehicle->get_name(),road_weg->get_name());
  			}
  			else {
  				// no success
  				end_stadt = NULL;
  				state = CHECK_CONVOI;
  			}
  		}
  		break;
  
  		// built a simple road (no bridges, no tunnels)
  		case NR_BAUE_STRASSEN_ROUTE:
  		{
  			state = NR_BAUE_WATER_ROUTE; // assume failure
  			if(  !ai_t::road_transport  ) {
  				// no overland bus lines
  				break;
  			}
  			const building_desc_t* bs = hausbauer_t::get_random_station(building_desc_t::generic_stop, road_wt, welt->get_timeline_year_month(), haltestelle_t::PAX);
  			if(bs  &&  create_simple_road_transport(platz1, koord(1,1),platz2,koord(1,1),road_weg)  ) {
  				// since the road may have led to a crossing at the intended stop position ...
  				bool ok = true;
  				if(  !get_halt(platz1).is_bound()  ) {
  					if(  !call_general_tool( TOOL_BUILD_STATION, platz1, bs->get_name() )  ) {
  						platz1 = find_area_for_hub( platz1-koord(2,2), platz1+koord(2,2), platz1 );
  						ok = call_general_tool( TOOL_BUILD_STATION, platz1, bs->get_name() );
  					}
  				}
  				if(  ok  ) {
  					if(  !get_halt(platz2).is_bound()  ) {
  						if(  !call_general_tool( TOOL_BUILD_STATION, platz2, bs->get_name() )  ) {
  							platz2 = find_area_for_hub( platz2-koord(2,2), platz2+koord(2,2), platz2 );
  							ok = call_general_tool( TOOL_BUILD_STATION, platz2, bs->get_name() );
  						}
  					}
  				}
  				// still continue?
  				if(ok) {
  					koord list[2]={ platz1, platz2 };
  					// wait only, if target is not a hub but an attraction/factory
  					create_bus_transport_vehikel(platz1,1,list,2,end_stadt==NULL);
  					state = NR_SUCCESS;
  					// tell the player
  					cbuffer_t buf;
  					if(end_attraction!=NULL) {
  						platz1 = end_attraction->get_pos().get_2d();
  						buf.printf(translator::translate("%s now\noffers bus services\nbetween %s\nand attraction\n%s\nat (%i,%i).\n"), get_name(), start_stadt->get_name(), make_single_line_string(translator::translate(end_attraction->get_tile()->get_desc()->get_name()),2), platz1.x, platz1.y );
  						end_stadt = start_stadt;
  					}
  					else if(ziel!=NULL) {
  						platz1 = ziel->get_pos().get_2d();
  						buf.printf( translator::translate("%s now\noffers bus services\nbetween %s\nand factory\n%s\nat (%i,%i).\n"), get_name(), start_stadt->get_name(), make_single_line_string(translator::translate(ziel->get_name()),2), platz1.x, platz1.y );
  						end_stadt = start_stadt;
  					}
  					else {
  						buf.printf( translator::translate("Travellers now\nuse %s's\nbusses between\n%s \nand %s.\n"), get_name(), start_stadt->get_name(), end_stadt->get_name() );
  						// add two intown routes
  						cover_city_with_bus_route(platz1, 6);
  						cover_city_with_bus_route(platz2, 6);
  					}
  					welt->get_message()->add_message(buf, platz1, message_t::ai, PLAYER_FLAG|player_nr, road_vehicle->get_base_image());
  				}
  			}
  		}
  		break;
  
  		case NR_BAUE_WATER_ROUTE:
  			if(  !ai_t::ship_transport  ) {
  				// no overland bus lines
  				state = NR_BAUE_AIRPORT_ROUTE;
  				break;
  			}
  			if(  end_attraction == NULL  &&  ship_transport  &&
  					create_water_transport_vehikel(start_stadt, end_stadt ? end_stadt->get_pos() : ziel->get_pos().get_2d())) {
  				// add two intown routes
  				cover_city_with_bus_route( get_our_hub(start_stadt)->get_basis_pos(), 6);
  				if(end_stadt!=NULL) {
  					cover_city_with_bus_route( get_our_hub(end_stadt)->get_basis_pos(), 6);
  				}
  				else {
  					// start again with same town
  					end_stadt = start_stadt;
  				}
  				cbuffer_t buf;
  				buf.printf( translator::translate("Ferry service by\n%s\nnow between\n%s \nand %s.\n"), get_name(), start_stadt->get_name(), end_stadt->get_name() );
  				welt->get_message()->add_message(buf, end_stadt->get_pos(), message_t::ai, PLAYER_FLAG | player_nr, road_vehicle->get_base_image());
  				state = NR_SUCCESS;
  			}
  			else {
  				if(  end_attraction==NULL  &&  ziel==NULL  ) {
  					state = NR_BAUE_AIRPORT_ROUTE;
  				}
  				else {
  					state = NR_BAUE_CLEAN_UP;
  				}
  			}
  		break;
  
  		// despite its name: try airplane
  		case NR_BAUE_AIRPORT_ROUTE:
  			// try airline (if we are wealthy enough) ...
  			if(  !air_transport  ||  finance->get_history_com_month(1, ATC_CASH) < finance->get_starting_money()  ||
  			     !end_stadt  ||  !create_air_transport_vehikel( start_stadt, end_stadt )  ) {
  				state = NR_BAUE_CLEAN_UP;
  			}
  			else {
  				// add two intown routes
  				cover_city_with_bus_route( get_our_hub(start_stadt)->get_basis_pos(), 6);
  				cover_city_with_bus_route( get_our_hub(end_stadt)->get_basis_pos(), 6);
  				cbuffer_t buf;
  				buf.printf( translator::translate("Airline service by\n%s\nnow between\n%s \nand %s.\n"), get_name(), start_stadt->get_name(), end_stadt->get_name() );
  				welt->get_message()->add_message(buf, end_stadt->get_pos(), message_t::ai, PLAYER_FLAG | player_nr, road_vehicle->get_base_image());
  				state = NR_SUCCESS;
  			}
  		break;
  
  		// remove marker etc.
  		case NR_BAUE_CLEAN_UP:
  			state = CHECK_CONVOI;
  			end_stadt = NULL; // otherwise it may always try to built the same route!
  		break;
  
  		// successful construction
  		case NR_SUCCESS:
  		{
  			state = CHECK_CONVOI;
  			next_construction_steps = welt->get_steps() + simrand( construction_speed/16 );
  		}
  		break;
  
  
  		// add vehicles to crowded lines
  		case CHECK_CONVOI:
  		{
  			// next time: do something different
  			state = NR_INIT;
  			next_construction_steps = welt->get_steps() + simrand( ai_t::construction_speed ) + 25;
  
  			vector_tpl<linehandle_t> lines(0);
  			simlinemgmt.get_lines( simline_t::line, &lines);
  			const uint32 offset = simrand(lines.get_count());
  			for (uint32 i = 0;  i<lines.get_count();  i++  ) {
  				linehandle_t line = lines[(i+offset)%lines.get_count()];
  				if(line->get_linetype()!=simline_t::airline  &&  line->get_linetype()!=simline_t::truckline) {
  					continue;
  				}
  
  				// remove empty lines
  				if(line->count_convoys()==0) {
  					simlinemgmt.delete_line(line);
  					break;
  				}
  
  				// avoid empty schedule ?!?
  				assert(!line->get_schedule()->empty());
  
  				// made loss with this line
  				if(line->get_finance_history(0,LINE_PROFIT)<0) {
  					// try to update the vehicles
  					if(welt->use_timeline()  &&  line->count_convoys()>1) {
  						// do not update unimportant lines with single vehicles
  						slist_tpl <convoihandle_t> obsolete;
  						uint32 capacity = 0;
  						for(  uint i=0;  i<line->count_convoys();  i++  ) {
  							convoihandle_t cnv = line->get_convoy(i);
  							if(cnv->has_obsolete_vehicles()) {
  								obsolete.append(cnv);
  								capacity += cnv->front()->get_desc()->get_capacity();
  							}
  						}
  						if(capacity>0) {
  							// now try to find new vehicle
  							vehicle_t              const& v       = *line->get_convoy(0)->front();
  							waytype_t              const  wt      = v.get_waytype();
  							vehicle_desc_t const* const  v_desc = vehicle_builder_t::vehikel_search(wt, welt->get_current_month(), 50, welt->get_average_speed(wt), goods_manager_t::passengers, false, true);
  							if (!v_desc->is_retired(welt->get_current_month()) && v_desc != v.get_desc()) {
  								// there is a newer one ...
  								for(  uint32 new_capacity=0;  capacity>new_capacity;  new_capacity+=v_desc->get_capacity()) {
  									if(  convoihandle_t::is_exhausted()  ) {
  										// too many convois => cannot do anything about this ...
  										break;
  									}
  									vehicle_t* v = vehicle_builder_t::build( line->get_schedule()->entries[0].pos, this, NULL, v_desc  );
  									convoi_t* new_cnv = new convoi_t(this);
  									new_cnv->set_name( v->get_desc()->get_name() );
  									new_cnv->add_vehikel( v );
  									new_cnv->set_line(line);
  									new_cnv->start();
  								}
  								// delete all old convois
  								while(!obsolete.empty()) {
  									obsolete.remove_first()->self_destruct();
  								}
  								return;
  							}
  						}
  					}
  				}
  				// next: check for stuck convois ...
  
  				sint64 free_cap = line->get_finance_history(0,LINE_CAPACITY);
  				sint64 used_cap = line->get_finance_history(0,LINE_TRANSPORTED_GOODS);
  
  				if(free_cap+used_cap==0) {
  					continue;
  				}
  
  				sint32 ratio = (sint32)((free_cap*100l)/(free_cap+used_cap));
  
  				// next: check for overflowing lines, i.e. running with 3/4 of the capacity
  				if(  ratio<10  &&  !convoihandle_t::is_exhausted()  ) {
  					// else add the first convoi again
  					vehicle_t* const v = vehicle_builder_t::build(line->get_schedule()->entries[0].pos, this, NULL, line->get_convoy(0)->front()->get_desc());
  					convoi_t* new_cnv = new convoi_t(this);
  					new_cnv->set_name( v->get_desc()->get_name() );
  					new_cnv->add_vehikel( v );
  					new_cnv->set_line( line );
  					// on waiting line, wait at alternating stations for load balancing
  					if(  line->get_schedule()->entries[1].minimum_loading==90  &&  line->get_linetype()!=simline_t::truckline  &&  (line->count_convoys()&1)==0  ) {
  						new_cnv->get_schedule()->entries[0].minimum_loading = 90;
  						new_cnv->get_schedule()->entries[1].minimum_loading = 0;
  					}
  					new_cnv->start();
  					return;
  				}
  
  				// next: check for too many cars, i.e. running with too many cars
  				if(  ratio>40  &&  line->count_convoys()>1) {
  					// remove one convoi
  					line->get_convoy(0)->self_destruct();
  					return;
  				}
  			}
  		}
  		break;
  
  		default:
  			DBG_MESSAGE("ai_passenger_t::do_passenger_ki()", "Illegal state %d!", state );
  			end_stadt = NULL;
  			state = NR_INIT;
  	}
  }
  
  
  
  void ai_passenger_t::rdwr(loadsave_t *file)
  {
  	if(  file->is_version_less(102, 2)  ) {
  		// due to an error the player was never saved correctly
  		player_t::rdwr(file);
  		return;
  	}
  
  	xml_tag_t t( file, "ai_passenger_t" );
  
  	ai_t::rdwr(file);
  
  	// then check, if we have to do something or the game is too old ...
  	if(file->is_version_less(101, 0)) {
  		// ignore saving, reinit on loading
  		if(  file->is_loading()  ) {
  			next_construction_steps = welt->get_steps()+simrand(ai_t::construction_speed);
  		}
  		return;
  	}
  
  	// now save current state ...
  	file->rdwr_enum(state);
  	if(  file->is_version_less(111, 1)  ) {
  		file->rdwr_long(ai_t::construction_speed);
  		file->rdwr_bool(air_transport);
  		file->rdwr_bool(ship_transport);
  		road_transport = true;
  		rail_transport = false;
  	}
  	platz1.rdwr( file );
  	platz2.rdwr( file );
  
  	if(file->is_saving()) {
  		// save current pointers
  		sint32 delta_steps = next_construction_steps-welt->get_steps();
  		file->rdwr_long(delta_steps);
  		koord k = start_stadt ? start_stadt->get_pos() : koord::invalid;
  		k.rdwr(file);
  		k = end_stadt ? end_stadt->get_pos() : koord::invalid;
  		k.rdwr(file);
  		koord3d k3d = end_attraction ? end_attraction->get_pos() : koord3d::invalid;
  		k3d.rdwr(file);
  		k3d = ziel ? ziel->get_pos() : koord3d::invalid;
  		k3d.rdwr(file);
  	}
  	else {
  		// since steps in loaded game == 0
  		file->rdwr_long(next_construction_steps);
  		next_construction_steps += welt->get_steps();
  		// reinit current pointers
  		koord k;
  		k.rdwr(file);
  		start_stadt = welt->find_nearest_city(k);
  		k.rdwr(file);
  		end_stadt = welt->find_nearest_city(k);
  		koord3d k3d;
  		k3d.rdwr(file);
  		end_attraction = welt->lookup(k3d) ? welt->lookup(k3d)->find<gebaeude_t>() : NULL;
  		k3d.rdwr(file);
  		ziel = fabrik_t::get_fab(k3d.get_2d() );
  	}
  
  	if (file->is_version_atleast(122, 1)) {
  		plainstring road_vehicle_name = road_vehicle ? road_vehicle->get_name() : "";
  		file->rdwr_str(road_vehicle_name);
  		if (file->is_loading() && road_vehicle_name != "") {
  			road_vehicle = vehicle_builder_t::get_info(road_vehicle_name);
  		}
  	}
  }
  
  
  /**
   * Dealing with stuck  or lost vehicles:
   * - delete lost ones
   * - ignore stuck ones
   */
  void ai_passenger_t::report_vehicle_problem(convoihandle_t cnv,const koord3d ziel)
  {
  	if(  cnv->get_state() == convoi_t::NO_ROUTE  &&  this!=welt->get_active_player()  ) {
  			DBG_MESSAGE("ai_passenger_t::report_vehicle_problem","Vehicle %s can't find a route to (%i,%i)!", cnv->get_name(),ziel.x,ziel.y);
  			cnv->self_destruct();
  			return;
  	}
  	player_t::report_vehicle_problem( cnv, ziel );
  }
  
  
  void ai_passenger_t::finish_rd()
  {
  	if (!road_vehicle) {
  		dbg->warning("ai_passenger_t::finish_rd", "Default road vehicle could not be loaded, searching for one...");
  		road_vehicle = vehikel_search( road_wt, 50, 80, goods_manager_t::passengers, false);
  	}
  
  	if (road_vehicle == NULL) {
  		// reset state
  		end_stadt = NULL;
  		state = CHECK_CONVOI;
  	}
  }
  