1 /*
2  * Copyright (c) 1997 - 2001 Hj. Malthaner
3  *
4  * This file is part of the Simutrans project under the artistic license.
5  * (see license.txt)
6  */
7 
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <string.h>
11 
12 #include "simconvoi.h"
13 #include "vehicle/simvehicle.h"
14 #include "gui/simwin.h"
15 #include "player/simplay.h"
16 #include "simworld.h"
17 #include "simdepot.h"
18 #include "simline.h"
19 #include "simlinemgmt.h"
20 #include "simmenu.h"
21 
22 #include "gui/depot_frame.h"
23 #include "gui/messagebox.h"
24 
25 #include "dataobj/schedule.h"
26 #include "dataobj/loadsave.h"
27 #include "dataobj/translator.h"
28 
29 #include "bauer/hausbauer.h"
30 #include "obj/gebaeude.h"
31 
32 #include "bauer/vehikelbauer.h"
33 
34 #include "descriptor/building_desc.h"
35 
36 #include "utils/cbuffer_t.h"
37 
38 
39 slist_tpl<depot_t *> depot_t::all_depots;
40 
depot_t(loadsave_t * file)41 depot_t::depot_t(loadsave_t *file) : gebaeude_t()
42 {
43 	rdwr(file);
44 	if(file->is_version_less(88, 2)) {
45 		set_yoff(0);
46 	}
47 	all_depots.append(this);
48 	selected_filter = VEHICLE_FILTER_RELEVANT;
49 	selected_sort_by = SORT_BY_DEFAULT;
50 	last_selected_line = linehandle_t();
51 	command_pending = false;
52 }
53 
54 
depot_t(koord3d pos,player_t * player,const building_tile_desc_t * t)55 depot_t::depot_t(koord3d pos, player_t *player, const building_tile_desc_t *t) :
56     gebaeude_t(pos, player, t)
57 {
58 	all_depots.append(this);
59 	selected_filter = VEHICLE_FILTER_RELEVANT;
60 	selected_sort_by = SORT_BY_DEFAULT;
61 	last_selected_line = linehandle_t();
62 	command_pending = false;
63 }
64 
65 
~depot_t()66 depot_t::~depot_t()
67 {
68 	destroy_win((ptrdiff_t)this);
69 	all_depots.remove(this);
70 }
71 
72 
73 // finds the next/previous depot relative to the current position
find_depot(koord3d start,const obj_t::typ depot_type,const player_t * player,bool forward)74 depot_t *depot_t::find_depot( koord3d start, const obj_t::typ depot_type, const player_t *player, bool forward)
75 {
76 	depot_t *found = NULL;
77 	koord3d found_pos = forward ? koord3d(welt->get_size().x+1,welt->get_size().y+1,welt->get_groundwater()) : koord3d(-1,-1,-1);
78 	sint32 found_hash = forward ? 0x7FFFFFF : -1;
79 	sint32 start_hash = start.x + (8192 * start.y);
80 	FOR(slist_tpl<depot_t*>, const d, all_depots) {
81 		if(d->get_typ()==depot_type  &&  d->get_owner()==player) {
82 			// ok, the right type of depot
83 			const koord3d pos = d->get_pos();
84 			if(pos==start) {
85 				// ignore the start point
86 				continue;
87 			}
88 			sint32 hash = (pos.x + (8192 * pos.y));
89 			if(forward) {
90 				if(hash>start_hash  ||  (hash==start_hash  &&  pos.z>start.z)) {
91 				// found a suitable one
92 					if(hash<found_hash  ||  (hash==found_hash  &&  pos.z<found_pos.z)) {
93 						// which is closer ...
94 						found = d;
95 						found_pos = pos;
96 						found_hash = hash;
97 					}
98 				}
99 			}
100 			else {
101 				// search to start of the map
102 				if(hash<start_hash  ||  (hash==start_hash  &&  pos.z<start.z)) {
103 				// found a suitable one
104 					if(hash>found_hash  ||  (hash==found_hash  &&  pos.z>found_pos.z)) {
105 						// which is closer ...
106 						found = d;
107 						found_pos = pos;
108 						found_hash = hash;
109 					}
110 				}
111 			}
112 		}
113 	}
114 	return found;
115 }
116 
117 
118 // again needed for server
call_depot_tool(char tool,convoihandle_t cnv,const char * extra)119 void depot_t::call_depot_tool( char tool, convoihandle_t cnv, const char *extra)
120 {
121 	// call depot tool
122 	tool_t *tmp_tool = create_tool( TOOL_CHANGE_DEPOT | SIMPLE_TOOL );
123 	cbuffer_t buf;
124 	buf.printf( "%c,%s,%hu", tool, get_pos().get_str(), cnv.get_id() );
125 	if(  extra  ) {
126 		buf.append( "," );
127 		buf.append( extra );
128 	}
129 	tmp_tool->set_default_param(buf);
130 	welt->set_tool( tmp_tool, get_owner() );
131 	// since init always returns false, it is safe to delete immediately
132 	delete tmp_tool;
133 }
134 
135 
136 /* this is called on two occasions:
137  * first a convoy reaches the depot during its journey
138  * second during loading a convoi is stored in a depot => only store it again
139  */
convoi_arrived(convoihandle_t acnv,bool schedule_adjust)140 void depot_t::convoi_arrived(convoihandle_t acnv, bool schedule_adjust)
141 {
142 	if(schedule_adjust) {
143 		// here a regular convoi arrived
144 
145 		for(unsigned i=0; i<acnv->get_vehicle_count(); i++) {
146 			vehicle_t *v = acnv->get_vehikel(i);
147 			// Hajo: reset vehikel data
148 			v->discard_cargo();
149 			v->set_pos( koord3d::invalid );
150 			v->set_leading( i==0 );
151 			v->set_last( i+1==acnv->get_vehicle_count() );
152 		}
153 		// Volker: remove depot from schedule
154 		schedule_t *schedule = acnv->get_schedule();
155 		for(  int i=0;  i<schedule->get_count();  i++  ) {
156 			// only if convoi found
157 			if(schedule->entries[i].pos==get_pos()) {
158 				schedule->set_current_stop( i );
159 				schedule->remove();
160 				acnv->set_schedule(schedule);
161 				break;
162 			}
163 		}
164 	}
165 	// this part stores the convoi in the depot
166 	convois.append(acnv);
167 	depot_frame_t *depot_frame = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)this ));
168 	if(depot_frame) {
169 		depot_frame->action_triggered(NULL,(long int)0);
170 	}
171 	acnv->set_home_depot( get_pos() );
172 	DBG_MESSAGE("depot_t::convoi_arrived()", "convoi %d, %p entered depot", acnv.get_id(), acnv.get_rep());
173 }
174 
175 
show_info()176 void depot_t::show_info()
177 {
178 	create_win( new depot_frame_t(this), w_info, (ptrdiff_t)this );
179 }
180 
181 
buy_vehicle(const vehicle_desc_t * info)182 vehicle_t* depot_t::buy_vehicle(const vehicle_desc_t* info)
183 {
184 	DBG_DEBUG("depot_t::buy_vehicle()", info->get_name());
185 	vehicle_t* veh = vehicle_builder_t::build(get_pos(), get_owner(), NULL, info );
186 	DBG_DEBUG("depot_t::buy_vehicle()", "vehiclebauer %p", veh);
187 
188 	vehicles.append(veh);
189 	DBG_DEBUG("depot_t::buy_vehicle()", "appended %i vehicle", vehicles.get_count());
190 	return veh;
191 }
192 
193 
append_vehicle(convoihandle_t cnv,vehicle_t * veh,bool infront,bool local_execution)194 void depot_t::append_vehicle(convoihandle_t cnv, vehicle_t* veh, bool infront, bool local_execution)
195 {
196 	/* create  a new convoi, if necessary */
197 	if (!cnv.is_bound()) {
198 		cnv = add_convoi( local_execution );
199 	}
200 	veh->set_pos(get_pos());
201 	cnv->add_vehikel(veh, infront);
202 	vehicles.remove(veh);
203 }
204 
205 
remove_vehicle(convoihandle_t cnv,int ipos)206 void depot_t::remove_vehicle(convoihandle_t cnv, int ipos)
207 {
208 	vehicle_t* veh = cnv->remove_vehikel_bei( ipos );
209 	if(  veh  ) {
210 		vehicles.append( veh );
211 	}
212 }
213 
214 
remove_vehicles_to_end(convoihandle_t cnv,int ipos)215 void depot_t::remove_vehicles_to_end(convoihandle_t cnv, int ipos)
216 {
217 	while(  vehicle_t* veh = cnv->remove_vehikel_bei( ipos )  ) {
218 		vehicles.append( veh );
219 	}
220 }
221 
222 
sell_vehicle(vehicle_t * veh)223 void depot_t::sell_vehicle(vehicle_t* veh)
224 {
225 	vehicles.remove(veh);
226 	get_owner()->book_new_vehicle((sint64)veh->calc_sale_value(), get_pos().get_2d(), get_waytype() );
227 	DBG_MESSAGE("depot_t::sell_vehicle()", "this=%p sells %p", this, veh);
228 	delete veh;
229 }
230 
231 
232 // returns the index of the oldest/newest vehicle in a list
find_oldest_newest(const vehicle_desc_t * desc,bool old)233 vehicle_t* depot_t::find_oldest_newest(const vehicle_desc_t* desc, bool old)
234 {
235 	vehicle_t* found_veh = NULL;
236 	FOR(  slist_tpl<vehicle_t*>,  const veh,  vehicles  ) {
237 		if(  veh->get_desc() == desc  ) {
238 			// joy of XOR, finally a line where I could use it!
239 			if(  found_veh == NULL  ||
240 					old ^ (found_veh->get_purchase_time() > veh->get_purchase_time())  ) {
241 				found_veh = veh;
242 			}
243 		}
244 	}
245 	return found_veh;
246 }
247 
248 
add_convoi(bool local_execution)249 convoihandle_t depot_t::add_convoi(bool local_execution)
250 {
251 	convoi_t* new_cnv = new convoi_t(get_owner());
252 	new_cnv->set_home_depot(get_pos());
253 	convois.append(new_cnv->self);
254 	depot_frame_t *win = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)this ));
255 	if(  win  &&  local_execution  ) {
256 		win->activate_convoi( new_cnv->self );
257 	}
258 	return new_cnv->self;
259 }
260 
261 
check_obsolete_inventory(convoihandle_t cnv)262 bool depot_t::check_obsolete_inventory(convoihandle_t cnv)
263 {
264 	bool ok = true;
265 	slist_tpl<vehicle_t*> veh_tmp_list;
266 
267 	for(  int i = 0;  i < cnv->get_vehicle_count();  i++  ) {
268 		const vehicle_desc_t* const vb = cnv->get_vehikel(i)->get_desc();
269 		if(  vb  ) {
270 			// search storage for matching vehicle
271 			vehicle_t* veh = NULL;
272 			for(  slist_tpl<vehicle_t*>::iterator i = vehicles.begin();  i != vehicles.end();  ++i  ) {
273 				if(  (*i)->get_desc() == vb  ) {
274 					// found in storage, remove to temp list while searching for next vehicle
275 					veh = *i;
276 					vehicles.erase(i);
277 					veh_tmp_list.append( veh );
278 					break;
279 				}
280 			}
281 			if(  !veh  ) {
282 				// need to buy new
283 				if(  vb->is_retired( welt->get_timeline_year_month() )  ) {
284 					// is obsolete, return false
285 					ok = false;
286 					break;
287 				}
288 			}
289 		}
290 	}
291 
292 	// put vehicles back into storage
293 	vehicles.append_list( veh_tmp_list );
294 
295 	return ok;
296 }
297 
298 
copy_convoi(convoihandle_t old_cnv,bool local_execution)299 convoihandle_t depot_t::copy_convoi(convoihandle_t old_cnv, bool local_execution)
300 {
301 	if(  old_cnv.is_bound()  &&  !convoihandle_t::is_exhausted()  &&
302 		old_cnv->get_vehicle_count() > 0  &&  get_waytype() == old_cnv->front()->get_desc()->get_waytype() ) {
303 
304 		convoihandle_t new_cnv = add_convoi( false );
305 		new_cnv->set_name(old_cnv->get_internal_name());
306 		int vehicle_count = old_cnv->get_vehicle_count();
307 		for (int i = 0; i<vehicle_count; i++) {
308 			const vehicle_desc_t * info = old_cnv->get_vehikel(i)->get_desc();
309 			if (info != NULL) {
310 				// search in depot for an existing vehicle of correct type
311 				vehicle_t* oldest_vehicle = get_oldest_vehicle(info);
312 				if (oldest_vehicle != NULL) {
313 					// append existing vehicle
314 					append_vehicle( new_cnv, oldest_vehicle, false, local_execution );
315 				}
316 				else {
317 					// buy new vehicle
318 					vehicle_t* veh = vehicle_builder_t::build(get_pos(), get_owner(), NULL, info );
319 					veh->set_pos(get_pos());
320 					new_cnv->add_vehikel(veh, false);
321 				}
322 			}
323 		}
324 		if (old_cnv->get_line().is_bound()) {
325 			new_cnv->set_line(old_cnv->get_line());
326 			new_cnv->get_schedule()->set_current_stop( old_cnv->get_schedule()->get_current_stop() );
327 		}
328 		else {
329 			if (old_cnv->get_schedule() != NULL) {
330 				new_cnv->set_schedule(old_cnv->get_schedule()->copy());
331 			}
332 		}
333 
334 		// make this the current selected convoi
335 		depot_frame_t *win = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)this ));
336 		if(  win  ) {
337 			if(  local_execution  ) {
338 				win->activate_convoi( new_cnv );
339 			}
340 			else {
341 				win->update_data();
342 			}
343 		}
344 
345 		return new_cnv;
346 	}
347 	return convoihandle_t();
348 }
349 
350 
disassemble_convoi(convoihandle_t cnv,bool sell)351 bool depot_t::disassemble_convoi(convoihandle_t cnv, bool sell)
352 {
353 	if(  cnv.is_bound()  ) {
354 		if(  !sell  ) {
355 			// store vehicles in depot
356 			while(  vehicle_t* const v = cnv->remove_vehikel_bei(0)  ) {
357 				v->discard_cargo();
358 				v->set_leading(false);
359 				v->set_last(false);
360 				vehicles.append(v);
361 			}
362 		}
363 
364 		// remove from depot lists
365 		remove_convoi( cnv );
366 
367 		// and remove from welt
368 		cnv->self_destruct();
369 		return true;
370 	}
371 	return false;
372 }
373 
374 
start_all_convoys()375 bool depot_t::start_all_convoys()
376 {
377 	uint32 i = 0;
378 	while(  i < convois.get_count()  ) {
379 		if(  !start_convoi( convois.at(i), false )  ) {
380 			i++;
381 		}
382 	}
383 	return (convois.get_count() == 0);
384 }
385 
386 // implementation in simtool.cc
387 bool scenario_check_convoy(karte_t *welt, player_t *player, convoihandle_t cnv, depot_t* depot, bool local);
388 
389 
start_convoi(convoihandle_t cnv,bool local_execution)390 bool depot_t::start_convoi(convoihandle_t cnv, bool local_execution)
391 {
392 	// close schedule window if not yet closed
393 	if(cnv.is_bound() &&  cnv->get_schedule()!=NULL) {
394 		if(!cnv->get_schedule()->is_editing_finished()) {
395 			// close the schedule window
396 			destroy_win((ptrdiff_t)cnv->get_schedule());
397 		}
398 	}
399 
400 	// convoi not in depot anymore, maybe user double-clicked on start-button
401 	if(!convois.is_contained(cnv)) {
402 		return false;
403 	}
404 
405 	if (cnv.is_bound() && cnv->get_schedule() && !cnv->get_schedule()->empty()) {
406 		// if next schedule entry is this depot => advance to next entry
407 		const koord3d& cur_pos = cnv->get_schedule()->get_current_entry().pos;
408 		if (cur_pos == get_pos()) {
409 			cnv->get_schedule()->advance();
410 		}
411 
412 		// check if convoi is complete
413 		if(cnv->get_sum_power() == 0 || !cnv->pruefe_alle()) {
414 			if (local_execution) {
415 				create_win( new news_img("Diese Zusammenstellung kann nicht fahren!\n"), w_time_delete, magic_none);
416 			}
417 		}
418 		else if(  !cnv->front()->calc_route(this->get_pos(), cur_pos, cnv->get_min_top_speed(), cnv->access_route())  ) {
419 			// no route to go ...
420 			if(local_execution) {
421 				static cbuffer_t buf;
422 				buf.clear();
423 				buf.printf( translator::translate("Vehicle %s can't find a route!"), cnv->get_name() );
424 				create_win( new news_img(buf), w_time_delete, magic_none);
425 			}
426 		}
427 		else if (!scenario_check_convoy(welt, get_owner(), cnv, this, local_execution) ) {
428 			// not allowed by scenario
429 		}
430 		else {
431 			// convoi can start now
432 			cnv->start();
433 
434 			// remove from depot lists
435 			remove_convoi( cnv );
436 
437 			return true;
438 		}
439 	}
440 	else {
441 		if (local_execution) {
442 			create_win( new news_img("Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n"), w_time_delete, magic_none);
443 		}
444 
445 		if (!cnv.is_bound()) {
446 			dbg->warning("depot_t::start_convoi()","No convoi to start!");
447 		} else if (!cnv->get_schedule()) {
448 			dbg->warning("depot_t::start_convoi()","No schedule for convoi.");
449 		} else if (!cnv->get_schedule()->is_editing_finished()) {
450 			dbg->warning("depot_t::start_convoi()","Schedule is incomplete/not finished");
451 		}
452 	}
453 	return false;
454 }
455 
456 
remove_convoi(convoihandle_t cnv)457 void depot_t::remove_convoi( convoihandle_t cnv )
458 {
459 	depot_frame_t *win = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)this ));
460 	if(  win  ) {
461 		// get currently selected convoi to restore selection if not removed
462 		int icnv = win->get_icnv();
463 		convoihandle_t c = icnv > -1 ? get_convoi( icnv ) : convoihandle_t();
464 
465 		icnv = convois.index_of( cnv );
466 		convois.remove( cnv );
467 
468 		if(  c == cnv  ) {
469 			// removing currently selected, select next in list or last instead
470 			c = !convois.empty() ? convois.at( min((uint32)icnv, convois.get_count() - 1) ) : convoihandle_t();
471 		}
472 		win->activate_convoi( c );
473 	}
474 	else {
475 		convois.remove( cnv );
476 	}
477 }
478 
479 
480 // attention! this will not be used for railway depots! They will be loaded by hand ...
rdwr(loadsave_t * file)481 void depot_t::rdwr(loadsave_t *file)
482 {
483 	gebaeude_t::rdwr(file);
484 
485 	rdwr_vehikel(vehicles, file);
486 	if (file->is_version_less(81, 33)) {
487 		// wagons are stored extra, just add them to vehicles
488 		assert(file->is_loading());
489 		rdwr_vehikel(vehicles, file);
490 	}
491 }
492 
493 
rdwr_vehikel(slist_tpl<vehicle_t * > & list,loadsave_t * file)494 void depot_t::rdwr_vehikel(slist_tpl<vehicle_t *> &list, loadsave_t *file)
495 {
496 	sint32 count;
497 
498 	if(file->is_saving()) {
499 		count = list.get_count();
500 		DBG_MESSAGE("depot_t::vehikel_laden()","saving %d vehicles",count);
501 	}
502 	file->rdwr_long(count);
503 
504 	if(file->is_loading()) {
505 
506 		// no house definition for this => use a normal hut ...
507 		if(  this->get_tile()==NULL  ) {
508 			dbg->error( "depot_t::rdwr()", "tile for depot not found!" );
509 			set_tile( (*hausbauer_t::get_citybuilding_list( building_desc_t::city_res ))[0]->get_tile(0), true );
510 		}
511 
512 		DBG_MESSAGE("depot_t::vehikel_laden()","loading %d vehicles",count);
513 		for(int i=0; i<count; i++) {
514 			obj_t::typ typ = (obj_t::typ)file->rd_obj_id();
515 
516 			vehicle_t *v = NULL;
517 			const bool first = false;
518 			const bool last = false;
519 
520 			switch( typ ) {
521 				case old_automobil:
522 				case road_vehicle: v = new road_vehicle_t(file, first, last);    break;
523 				case old_waggon:
524 				case rail_vehicle:    v = new rail_vehicle_t(file, first, last);       break;
525 				case old_schiff:
526 				case water_vehicle:    v = new water_vehicle_t(file, first, last);       break;
527 				case old_aircraft:
528 				case air_vehicle: v = new air_vehicle_t(file, first, last);  break;
529 				case old_monorailwaggon:
530 				case monorail_vehicle: v = new monorail_vehicle_t(file, first, last);  break;
531 				case maglev_vehicle:   v = new maglev_vehicle_t(file, first, last);  break;
532 				case narrowgauge_vehicle: v = new narrowgauge_vehicle_t(file, first, last);  break;
533 				default:
534 					dbg->fatal("depot_t::vehikel_laden()","invalid vehicle type $%X", typ);
535 			}
536 			if(v->get_desc()) {
537 				DBG_MESSAGE("depot_t::vehikel_laden()","loaded %s", v->get_desc()->get_name());
538 				list.insert( v );
539 			}
540 			else {
541 				dbg->error("depot_t::vehikel_laden()","vehicle has no desc => ignored");
542 			}
543 		}
544 	}
545 	else {
546 		FOR(slist_tpl<vehicle_t*>, const v, list) {
547 			file->wr_obj_id(v->get_typ());
548 			v->rdwr_from_convoi(file);
549 		}
550 	}
551 }
552 
553 
554 /**
555  * @return NULL when OK, otherwise an error message
556  * @author Hj. Malthaner
557  */
is_deletable(const player_t * player)558 const char * depot_t::is_deletable(const player_t *player)
559 {
560 	if(player!=get_owner()  &&  player!=welt->get_public_player()) {
561 		return "Das Feld gehoert\neinem anderen Spieler\n";
562 	}
563 	if (!vehicles.empty()) {
564 		return "There are still vehicles\nstored in this depot!\n";
565 	}
566 
567 	FOR(slist_tpl<convoihandle_t>, const c, convois) {
568 		if (c->get_vehicle_count() > 0) {
569 			return "There are still vehicles\nstored in this depot!\n";
570 		}
571 	}
572 	return NULL;
573 }
574 
575 
get_vehicle_type(uint8 sortkey) const576 slist_tpl<vehicle_desc_t const*> const& depot_t::get_vehicle_type(uint8 sortkey) const
577 {
578 	return vehicle_builder_t::get_info(get_waytype(), sortkey);
579 }
580 
581 
get_oldest_vehicle(const vehicle_desc_t * desc)582 vehicle_t* depot_t::get_oldest_vehicle(const vehicle_desc_t* desc)
583 {
584 	vehicle_t* oldest_veh = NULL;
585 	FOR(slist_tpl<vehicle_t*>, const veh, get_vehicle_list()) {
586 		if (veh->get_desc() == desc) {
587 			if (oldest_veh == NULL ||
588 					oldest_veh->get_purchase_time() > veh->get_purchase_time()) {
589 				oldest_veh = veh;
590 			}
591 		}
592 	}
593 	return oldest_veh;
594 }
595 
596 
update_win()597 void depot_t::update_win()
598 {
599 	depot_frame_t *depot_frame = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)this ));
600 	if(depot_frame) {
601 		depot_frame->build_vehicle_lists();
602 	}
603 }
604 
605 
new_month()606 void depot_t::new_month()
607 {
608 	// since vehicles may have become obsolete
609 	update_all_win();
610 }
611 
612 
update_all_win()613 void depot_t::update_all_win()
614 {
615 	FOR(slist_tpl<depot_t*>, const d, all_depots) {
616 		d->update_win();
617 	}
618 }
619 
620 
get_max_convoi_length() const621 unsigned bahndepot_t::get_max_convoi_length() const
622 {
623 	return welt->get_settings().get_max_rail_convoi_length();
624 }
get_max_convoi_length() const625 unsigned strassendepot_t::get_max_convoi_length() const
626 {
627 	return welt->get_settings().get_max_road_convoi_length();
628 }
get_max_convoi_length() const629 unsigned schiffdepot_t::get_max_convoi_length() const
630 {
631 	return welt->get_settings().get_max_ship_convoi_length();
632 }
get_max_convoi_length() const633 unsigned airdepot_t::get_max_convoi_length() const
634 {
635 	return welt->get_settings().get_max_air_convoi_length();
636 }
637