1 /*
2  * Copyright (c) 1997 - 2001 Hansj. Malthaner
3  *
4  * This file is part of the Simutrans project under the artistic license.
5  * (see license.txt)
6  */
7 
8 /*
9  * Stations for Simutrans
10  * 03.2000 moved from simfab.cc
11  *
12  * Hj. Malthaner
13  */
14 #include <algorithm>
15 
16 #include "freight_list_sorter.h"
17 
18 #include "simcity.h"
19 #include "simcolor.h"
20 #include "simconvoi.h"
21 #include "simdebug.h"
22 #include "simfab.h"
23 #include "simhalt.h"
24 #include "simintr.h"
25 #include "simline.h"
26 #include "simmem.h"
27 #include "simmesg.h"
28 #include "simplan.h"
29 #include "player/simplay.h"
30 #include "gui/simwin.h"
31 #include "simworld.h"
32 #include "simware.h"
33 
34 #include "bauer/hausbauer.h"
35 #include "bauer/goods_manager.h"
36 
37 #include "descriptor/goods_desc.h"
38 #include "descriptor/tunnel_desc.h"
39 
40 #include "boden/boden.h"
41 #include "boden/grund.h"
42 #include "boden/wasser.h"
43 #include "boden/wege/strasse.h"
44 
45 #include "dataobj/settings.h"
46 #include "dataobj/schedule.h"
47 #include "dataobj/loadsave.h"
48 #include "dataobj/translator.h"
49 #include "dataobj/environment.h"
50 
51 #include "obj/gebaeude.h"
52 #include "obj/label.h"
53 #include "obj/tunnel.h"
54 #include "obj/wayobj.h"
55 
56 #include "gui/halt_info.h"
57 #include "gui/minimap.h"
58 
59 #include "utils/simrandom.h"
60 #include "utils/simstring.h"
61 
62 #include "vehicle/simpeople.h"
63 
64 karte_ptr_t haltestelle_t::welt;
65 
66 vector_tpl<halthandle_t> haltestelle_t::alle_haltestellen;
67 
68 stringhashtable_tpl<halthandle_t> haltestelle_t::all_names;
69 
70 // hash table only used during loading
71 inthashtable_tpl<sint32,halthandle_t> *haltestelle_t::all_koords = NULL;
72 // since size_x*size_y < 0x1000000, we have just to shift the high bits
73 #define get_halt_key(k,width) ( ((k).x*(width)+(k).y) /*+ ((k).z << 25)*/ )
74 
75 uint8 haltestelle_t::status_step = 0;
76 uint8 haltestelle_t::reconnect_counter = 0;
77 
78 
79 static vector_tpl<convoihandle_t>stale_convois;
80 static vector_tpl<linehandle_t>stale_lines;
81 
82 
reset_routing()83 void haltestelle_t::reset_routing()
84 {
85 	reconnect_counter = welt->get_schedule_counter()-1;
86 }
87 
88 
step_all()89 void haltestelle_t::step_all()
90 {
91 	// tell all stale convois to reroute their goods
92 	if(  !stale_convois.empty()  ) {
93 		convoihandle_t cnv = stale_convois.pop_back();
94 		if(  cnv.is_bound()  ) {
95 			cnv->check_freight();
96 		}
97 	}
98 	// same for stale lines
99 	if(  !stale_lines.empty()  ) {
100 		linehandle_t line = stale_lines.pop_back();
101 		if(  line.is_bound()  ) {
102 			line->check_freight();
103 		}
104 	}
105 
106 	static vector_tpl<halthandle_t>::iterator iter( alle_haltestellen.begin() );
107 	if (alle_haltestellen.empty()) {
108 		return;
109 	}
110 	const uint8 schedule_counter = welt->get_schedule_counter();
111 	if (reconnect_counter != schedule_counter) {
112 		// always start with reconnection, re-routing will happen after complete reconnection
113 		status_step = RECONNECTING;
114 		reconnect_counter = schedule_counter;
115 		iter = alle_haltestellen.begin();
116 	}
117 
118 	sint16 units_remaining = 128;
119 	for (; iter != alle_haltestellen.end(); ++iter) {
120 		if (units_remaining <= 0) return;
121 
122 		// iterate until the specified number of units were handled
123 		if(  !(*iter)->step(status_step, units_remaining)  ) {
124 			// too much rerouted => needs to continue at next round!
125 			return;
126 		}
127 	}
128 
129 	if (status_step == RECONNECTING) {
130 		// reconnecting finished, compute connected components in one sweep
131 		rebuild_connected_components();
132 		// reroute in next call
133 		status_step = REROUTING;
134 	}
135 	else if (status_step == REROUTING) {
136 		status_step = 0;
137 	}
138 	iter = alle_haltestellen.begin();
139 }
140 
141 
start_load_game()142 void haltestelle_t::start_load_game()
143 {
144 	all_koords = new inthashtable_tpl<sint32,halthandle_t>;
145 }
146 
147 
end_load_game()148 void haltestelle_t::end_load_game()
149 {
150 	delete all_koords;
151 	all_koords = NULL;
152 }
153 
154 /**
155  * return an index to a halt; it is only used for old games
156  * by default create a new halt if none found
157  */
get_halt_koord_index(koord k)158 halthandle_t haltestelle_t::get_halt_koord_index(koord k)
159 {
160 	if(!welt->is_within_limits(k)) {
161 		return halthandle_t();
162 	}
163 	// check in hashtable
164 	halthandle_t h;
165 	const sint32 n = get_halt_key(koord3d(k,-128), welt->get_size().y);
166 	assert(all_koords);
167 	h = all_koords->get( n );
168 
169 	if(  !h.is_bound()  ) {
170 		// No halts found => create one
171 		h = haltestelle_t::create(k, NULL );
172 		all_koords->set( n,  h );
173 	}
174 	return h;
175 }
176 
177 
178 /* we allow only for a single stop per grund
179  * this will only return something if this stop belongs to same player or is public, or is a dock (when on water)
180  */
get_halt(const koord3d pos,const player_t * player)181 halthandle_t haltestelle_t::get_halt(const koord3d pos, const player_t *player )
182 {
183 	const grund_t *gr = welt->lookup(pos);
184 	if(gr) {
185 		if(gr->get_halt().is_bound()  &&  player_t::check_owner(player,gr->get_halt()->get_owner())  ) {
186 			return gr->get_halt();
187 		}
188 		// no halt? => we do the water check
189 		if(gr->is_water()) {
190 			// may catch bus stops close to water ...
191 			const planquadrat_t *plan = welt->access(pos.get_2d());
192 			const uint8 cnt = plan->get_haltlist_count();
193 			// first check for own stop
194 			for(  uint8 i=0;  i<cnt;  i++  ) {
195 				halthandle_t halt = plan->get_haltlist()[i];
196 				if(  halt->get_owner()==player  &&  halt->get_station_type()&dock  ) {
197 					return halt;
198 				}
199 			}
200 			// then for public stop
201 			for(  uint8 i=0;  i<cnt;  i++  ) {
202 				halthandle_t halt = plan->get_haltlist()[i];
203 				if(  halt->get_owner()==welt->get_public_player()  &&  halt->get_station_type()&dock  ) {
204 					return halt;
205 				}
206 			}
207 			// so: nothing found
208 		}
209 	}
210 	return halthandle_t();
211 }
212 
213 
get_basis_pos() const214 koord haltestelle_t::get_basis_pos() const
215 {
216 	return get_basis_pos3d().get_2d();
217 }
218 
219 
get_basis_pos3d() const220 koord3d haltestelle_t::get_basis_pos3d() const
221 {
222 	if (tiles.empty()) {
223 		return koord3d::invalid;
224 	}
225 	assert(tiles.front().grund->get_pos().get_2d() == init_pos);
226 	return tiles.front().grund->get_pos();
227 }
228 
229 
230 // returns tile closest to this coordinate
get_ground_closest_to(const koord here) const231 grund_t *haltestelle_t::get_ground_closest_to( const koord here ) const
232 {
233 	uint32 distance = 0x7FFFFFFFu;
234 	grund_t *closest = NULL;
235 	FOR(slist_tpl<tile_t>, const& i, tiles) {
236 		uint32 dist = shortest_distance( i.grund->get_pos().get_2d(), here );
237 		if(  dist < distance  ) {
238 			distance = dist;
239 			closest = i.grund;
240 			if(  distance == 0  ) {
241 				break;
242 			}
243 		}
244 	}
245 
246 	return closest;
247 }
248 
249 
250 
251 /* return the closest square that belongs to this halt
252  * @author prissi
253  */
get_next_pos(koord start) const254 koord haltestelle_t::get_next_pos( koord start ) const
255 {
256 	if(  grund_t *gr=get_ground_closest_to(start)  ) {
257 		return gr->get_pos().get_2d();
258 	}
259 	return koord::invalid;
260 }
261 
262 
263 
264 /* Calculate and set basis position of this station
265  * It is the avarage of all tiles' coordinate weighed by level of the building */
recalc_basis_pos()266 void haltestelle_t::recalc_basis_pos()
267 {
268 	sint64 cent_x, cent_y;
269 	cent_x = cent_y = 0;
270 	uint64 level_sum;
271 	level_sum = 0;
272 	FOR(slist_tpl<tile_t>, const& i, tiles) {
273 		if(  gebaeude_t* const gb = i.grund->find<gebaeude_t>()  ) {
274 			uint32 lv;
275 			lv = gb->get_tile()->get_desc()->get_level() + 1;
276 			cent_x += gb->get_pos().get_2d().x * lv;
277 			cent_y += gb->get_pos().get_2d().y * lv;
278 			level_sum += lv;
279 		}
280 	}
281 	koord cent;
282 	cent = koord((sint16)(cent_x/level_sum),(sint16)(cent_y/level_sum));
283 
284 	if ( level_sum > 0 ) {
285 		grund_t *new_center = get_ground_closest_to( cent );
286 		if(  new_center != tiles.front().grund  &&  new_center->get_text()==NULL  ) {
287 			// move the name to new center, if there is not yet a name on it
288 			new_center->set_text( tiles.front().grund->get_text() );
289 			tiles.front().grund->set_text(NULL);
290 			tiles.remove( new_center );
291 			tiles.insert( new_center );
292 			init_pos = new_center->get_pos().get_2d();
293 		}
294 	}
295 	return;
296 }
297 
298 /**
299  * Station factory method. Returns handles instead of pointers.
300  * @author Hj. Malthaner
301  */
create(koord pos,player_t * player)302 halthandle_t haltestelle_t::create(koord pos, player_t *player)
303 {
304 	haltestelle_t * p = new haltestelle_t(pos, player);
305 	return p->self;
306 }
307 
308 
309 /*
310  * removes a ground tile from a station
311  * @author prissi
312  */
remove(player_t * player,koord3d pos)313 bool haltestelle_t::remove(player_t *player, koord3d pos)
314 {
315 	grund_t *bd = welt->lookup(pos);
316 
317 	// wrong ground?
318 	if(bd==NULL) {
319 		dbg->error("haltestelle_t::remove()","illegal ground at %d,%d,%d", pos.x, pos.y, pos.z);
320 		return false;
321 	}
322 
323 	halthandle_t halt = bd->get_halt();
324 	if(!halt.is_bound()) {
325 		dbg->error("haltestelle_t::remove()","no halt at %d,%d,%d", pos.x, pos.y, pos.z);
326 		return false;
327 	}
328 
329 DBG_MESSAGE("haltestelle_t::remove()","removing segment from %d,%d,%d", pos.x, pos.y, pos.z);
330 	// otherwise there will be marked tiles left ...
331 	halt->mark_unmark_coverage(false);
332 
333 	// only try to remove connected buildings, when still in list to avoid infinite loops
334 	if(  halt->rem_grund(bd)  ) {
335 		// remove station building?
336 		gebaeude_t* gb = bd->find<gebaeude_t>();
337 		if(gb) {
338 			DBG_MESSAGE("haltestelle_t::remove()",  "removing building" );
339 			hausbauer_t::remove( player, gb );
340 			bd = NULL;	// no need to recalc image
341 			// removing the building could have destroyed this halt already
342 			if (!halt.is_bound()){
343 				return true;
344 			}
345 		}
346 	}
347 
348 	if(!halt->existiert_in_welt()) {
349 DBG_DEBUG("haltestelle_t::remove()","remove last");
350 		// all deleted?
351 DBG_DEBUG("haltestelle_t::remove()","destroy");
352 		haltestelle_t::destroy( halt );
353 	}
354 	else {
355 		halt->recalc_basis_pos();
356 	}
357 
358 	// if building was removed this is false!
359 	if(bd) {
360 		bd->calc_image();
361 		minimap_t::get_instance()->calc_map_pixel(pos.get_2d());
362 	}
363 	return true;
364 }
365 
366 
367 
368 /**
369  * Station factory method. Returns handles instead of pointers.
370  * @author Hj. Malthaner
371  */
create(loadsave_t * file)372 halthandle_t haltestelle_t::create(loadsave_t *file)
373 {
374 	haltestelle_t *p = new haltestelle_t(file);
375 	return p->self;
376 }
377 
378 
379 /**
380  * Station destruction method.
381  * @author Hj. Malthaner
382  */
destroy(halthandle_t const halt)383 void haltestelle_t::destroy(halthandle_t const halt)
384 {
385 	delete halt.get_rep();
386 }
387 
388 
389 /**
390  * Station destruction method.
391  * Da destroy() alle_haltestellen modifiziert kann kein Iterator benutzt
392  * werden! V. Meyer
393  * @author Hj. Malthaner
394  */
destroy_all()395 void haltestelle_t::destroy_all()
396 {
397 	while (!alle_haltestellen.empty()) {
398 		halthandle_t halt = alle_haltestellen.back();
399 		destroy(halt);
400 	}
401 	delete all_koords;
402 	all_koords = NULL;
403 	status_step = 0;
404 }
405 
406 
haltestelle_t(loadsave_t * file)407 haltestelle_t::haltestelle_t(loadsave_t* file)
408 {
409 	last_loading_step = welt->get_steps();
410 
411 	cargo = (vector_tpl<ware_t> **)calloc( goods_manager_t::get_max_catg_index(), sizeof(vector_tpl<ware_t> *) );
412 	all_links = new link_t[ goods_manager_t::get_max_catg_index() ];
413 
414 	status_color = SYSCOL_TEXT_UNUSED;
415 	last_status_color = color_idx_to_rgb(COL_PURPLE);
416 	last_bar_count = 0;
417 
418 	reconnect_counter = welt->get_schedule_counter()-1;
419 
420 	enables = NOT_ENABLED;
421 
422 	// @author hsiegeln
423 	sortierung = freight_list_sorter_t::by_name;
424 	resort_freight_info = true;
425 
426 	rdwr(file);
427 
428 	markers[ self.get_id() ] = current_marker;
429 
430 	alle_haltestellen.append(self);
431 }
432 
433 
haltestelle_t(koord k,player_t * player)434 haltestelle_t::haltestelle_t(koord k, player_t* player)
435 {
436 	self = halthandle_t(this);
437 	assert( !alle_haltestellen.is_contained(self) );
438 	alle_haltestellen.append(self);
439 
440 	markers[ self.get_id() ] = current_marker;
441 
442 	last_loading_step = welt->get_steps();
443 
444 	this->init_pos = k;
445 	owner = player;
446 
447 	enables = NOT_ENABLED;
448 	// force total re-routing
449 	reconnect_counter = welt->get_schedule_counter()-1;
450 	last_catg_index = 255;
451 
452 	cargo = (vector_tpl<ware_t> **)calloc( goods_manager_t::get_max_catg_index(), sizeof(vector_tpl<ware_t> *) );
453 	all_links = new link_t[ goods_manager_t::get_max_catg_index() ];
454 
455 	status_color = SYSCOL_TEXT_UNUSED;
456 	last_status_color = color_idx_to_rgb(COL_PURPLE);
457 	last_bar_count = 0;
458 
459 	sortierung = freight_list_sorter_t::by_name;
460 	init_financial_history();
461 }
462 
463 
~haltestelle_t()464 haltestelle_t::~haltestelle_t()
465 {
466 	assert(self.is_bound());
467 
468 	// first: remove halt from all lists
469 	int i=0;
470 	while(alle_haltestellen.is_contained(self)) {
471 		alle_haltestellen.remove(self);
472 		i++;
473 	}
474 	if (i != 1) {
475 		dbg->error("haltestelle_t::~haltestelle_t()", "handle %i found %i times in haltlist!", self.get_id(), i );
476 	}
477 
478 	// free name
479 	set_name(NULL);
480 
481 	// remove from ground and planquadrat (tile) haltlists
482 	koord ul(32767,32767);
483 	koord lr(0,0);
484 	while(  !tiles.empty()  ) {
485 		grund_t *gr = tiles.remove_first().grund;
486 		koord pos = gr->get_pos().get_2d();
487 		gr->set_halt( halthandle_t() );
488 		// bounding box for adjustments
489 		ul.clip_max(pos);
490 		lr.clip_min(pos);
491 	}
492 
493 	// remove from all haltlists
494 	uint16 const cov = welt->get_settings().get_station_coverage();
495 	ul.x = max(0, ul.x - cov);
496 	ul.y = max(0, ul.y - cov);
497 	lr.x = min(welt->get_size().x, lr.x + 1 + cov);
498 	lr.y = min(welt->get_size().y, lr.y + 1 + cov);
499 	for(  int y=ul.y;  y<lr.y;  y++  ) {
500 		for(  int x=ul.x;  x<lr.x;  x++  ) {
501 			planquadrat_t *plan = welt->access(x,y);
502 			if(plan->get_haltlist_count()>0) {
503 				plan->remove_from_haltlist(self);
504 			}
505 		}
506 	}
507 
508 	destroy_win( magic_halt_info + self.get_id() );
509 
510 	// finally detach handle
511 	// before it is needed for clearing up the planqudrat and tiles
512 	self.detach();
513 
514 	for(unsigned i=0; i<goods_manager_t::get_max_catg_index(); i++) {
515 		if (cargo[i]) {
516 			FOR(vector_tpl<ware_t>, const &w, *cargo[i]) {
517 				fabrik_t::update_transit(&w, false);
518 			}
519 			delete cargo[i];
520 			cargo[i] = NULL;
521 		}
522 	}
523 	free( cargo );
524 	delete[] all_links;
525 
526 	// routes may have changed without this station ...
527 	verbinde_fabriken();
528 }
529 
530 
rotate90(const sint16 y_size)531 void haltestelle_t::rotate90( const sint16 y_size )
532 {
533 	init_pos.rotate90( y_size );
534 
535 	// rotate cargo (good) destinations
536 	// iterate over all different categories
537 	for(unsigned i=0; i<goods_manager_t::get_max_catg_index(); i++) {
538 		if(cargo[i]) {
539 			vector_tpl<ware_t>& warray = *cargo[i];
540 			for (size_t j = warray.get_count(); j-- != 0;) {
541 				ware_t& ware = warray[j];
542 				if(ware.menge>0) {
543 					ware.rotate90(y_size);
544 				}
545 				else {
546 					// empty => remove
547 					warray.remove_at(j);
548 				}
549 			}
550 		}
551 	}
552 
553 	// re-linking factories
554 	verbinde_fabriken();
555 }
556 
557 
558 
get_name() const559 const char* haltestelle_t::get_name() const
560 {
561 	const char *name = "Unknown";
562 	if (tiles.empty()) {
563 		name = "Unnamed";
564 	}
565 	else {
566 		grund_t* bd = welt->lookup(get_basis_pos3d());
567 		if(bd  &&  bd->get_flag(grund_t::has_text)) {
568 			name = bd->get_text();
569 		}
570 	}
571 	return name;
572 }
573 
574 
575 
576 /**
577  * Sets the name. Creates a copy of name.
578  * @author Hj. Malthaner
579  */
set_name(const char * new_name)580 void haltestelle_t::set_name(const char *new_name)
581 {
582 	grund_t *gr = welt->lookup(get_basis_pos3d());
583 	if(gr) {
584 		if(gr->get_flag(grund_t::has_text)) {
585 			halthandle_t h = all_names.remove(gr->get_text());
586 			if(h!=self) {
587 				DBG_MESSAGE("haltestelle_t::set_name()","removing name %s already used!",gr->get_text());
588 			}
589 		}
590 		if(!gr->find<label_t>()) {
591 			gr->set_text( new_name );
592 			if(new_name  &&  all_names.set(gr->get_text(),self).is_bound() ) {
593  				DBG_MESSAGE("haltestelle_t::set_name()","name %s already used!",new_name);
594 			}
595 		}
596 		halt_info_t *const info_frame = dynamic_cast<halt_info_t *>( win_get_magic( magic_halt_info + self.get_id() ) );
597 		if(  info_frame  ) {
598 			info_frame->set_name( get_name() );
599 		}
600 	}
601 }
602 
603 
604 // returns the number of % in a printf string ....
count_printf_param(const char * str)605 static int count_printf_param( const char *str )
606 {
607 	int count = 0;
608 	while( *str!=0  ) {
609 		if(  *str=='%'  ) {
610 			str++;
611 			if(  *str!='%'  ) {
612 				count++;
613 			}
614 		}
615 		str ++;
616 	}
617 	return count;
618 }
619 
620 
621 // creates stops with unique! names
create_name(koord const k,char const * const typ)622 char* haltestelle_t::create_name(koord const k, char const* const typ)
623 {
624 	int const lang = welt->get_settings().get_name_language_id();
625 	stadt_t *stadt = welt->find_nearest_city(k);
626 	const char *stop = translator::translate(typ,lang);
627 	cbuffer_t buf;
628 
629 	// this fails only, if there are no towns at all!
630 	if(stadt==NULL) {
631 		for(  uint32 i=1;  i<65536;  i++  ) {
632 			// get a default name
633 			buf.printf( translator::translate("land stop %i %s",lang), i, stop );
634 			if(  !all_names.get(buf).is_bound()  ) {
635 				return strdup(buf);
636 			}
637 			buf.clear();
638 		}
639 		return strdup("Unnamed");
640 	}
641 
642 	// now we have a city
643 	const char *city_name = stadt->get_name();
644 	sint16 li_gr = stadt->get_linksoben().x - 2;
645 	sint16 re_gr = stadt->get_rechtsunten().x + 2;
646 	sint16 ob_gr = stadt->get_linksoben().y - 2;
647 	sint16 un_gr = stadt->get_rechtsunten().y + 2;
648 
649 	// strings for intown / outside of town
650 	const bool inside = (li_gr < k.x  &&  re_gr > k.x  &&  ob_gr < k.y  &&  un_gr > k.y);
651 	const bool suburb = !inside  &&  (li_gr - 6 < k.x  &&  re_gr + 6 > k.x  &&  ob_gr - 6 < k.y  &&  un_gr + 6 > k.y);
652 
653 	if (!welt->get_settings().get_numbered_stations()) {
654 		static const koord next_building[24] = {
655 			koord( 0, -1), // north
656 			koord( 1,  0), // east
657 			koord( 0,  1), // south
658 			koord(-1,  0), // west
659 			koord( 1, -1), // northeast
660 			koord( 1,  1), // southeast
661 			koord(-1,  1), // southwest
662 			koord(-1, -1), // northwest
663 			koord( 0, -2), // double nswo
664 			koord( 2,  0),
665 			koord( 0,  2),
666 			koord(-2,  0),
667 			koord( 1, -2), // all the remaining 3s
668 			koord( 2, -1),
669 			koord( 2,  1),
670 			koord( 1,  2),
671 			koord(-1,  2),
672 			koord(-2,  1),
673 			koord(-2, -1),
674 			koord(-1, -2),
675 			koord( 2, -2), // and now all buildings with distance 4
676 			koord( 2,  2),
677 			koord(-2,  2),
678 			koord(-2, -2)
679 		};
680 
681 		// standard names:
682 		// order: factory, attraction, direction, normal name
683 		// prissi: first we try a factory name
684 
685 		// is there a translation for factory defined?
686 		const char *fab_base_text = "%s factory %s %s";
687 		const char *fab_base = translator::translate(fab_base_text,lang);
688 		if(  fab_base_text != fab_base  ) {
689 			slist_tpl<fabrik_t *>fabs;
690 			if (self.is_bound()) {
691 				// first factories (so with same distance, they have priority)
692 				int this_distance = 999;
693 				FOR(slist_tpl<fabrik_t*>, const f, get_fab_list()) {
694 					int distance = koord_distance(f->get_pos().get_2d(), k);
695 					if(  distance < this_distance  ) {
696 						fabs.insert(f);
697 						this_distance = distance;
698 					}
699 					else {
700 						fabs.append(f);
701 					}
702 				}
703 			}
704 			else {
705 				// since the distance are presorted, we can just append for a good choice ...
706 				for(  int test=0;  test<24;  test++  ) {
707 					fabrik_t *fab = fabrik_t::get_fab(k+next_building[test]);
708 					if(fab  &&  fabs.is_contained(fab)) {
709 						fabs.append(fab);
710 					}
711 				}
712 			}
713 
714 			// are there fabs?
715 			FOR(slist_tpl<fabrik_t*>, const f, fabs) {
716 				// with factories
717 				buf.printf(fab_base, city_name, f->get_name(), stop);
718 				if(  !all_names.get(buf).is_bound()  ) {
719 					return strdup(buf);
720 				}
721 				buf.clear();
722 			}
723 		}
724 
725 		// no fabs or all names used up already
726 		// is there a translation for buildings defined?
727 		const char *building_base_text = "%s building %s %s";
728 		const char *building_base = translator::translate(building_base_text,lang);
729 		if(  building_base_text != building_base  ) {
730 			// check for other special building (townhall, monument, tourist attraction)
731 			for (int i=0; i<24; i++) {
732 				grund_t *gr = welt->lookup_kartenboden( next_building[i] + k);
733 				if(gr==NULL  ||  gr->get_typ()!=grund_t::fundament) {
734 					// no building here
735 					continue;
736 				}
737 				// since closes coordinates are tested first, we do not need to not sort this
738 				const char *building_name = NULL;
739 				const gebaeude_t* gb = gr->find<gebaeude_t>();
740 				if(gb==NULL) {
741 					// field may have foundations but no building
742 					continue;
743 				}
744 				// now we have a building here
745 				if (gb->is_monument()) {
746 					building_name = translator::translate(gb->get_name(),lang);
747 				}
748 				else if (gb->is_townhall() ||
749 					gb->get_tile()->get_desc()->get_type() == building_desc_t::attraction_land || // land attraction
750 					gb->get_tile()->get_desc()->get_type() == building_desc_t::attraction_city) { // town attraction
751 					building_name = make_single_line_string(translator::translate(gb->get_tile()->get_desc()->get_name(),lang), 2);
752 				}
753 				else {
754 					// normal town house => not suitable for naming
755 					continue;
756 				}
757 				// now we have a name: try it
758 				buf.printf( building_base, city_name, building_name, stop );
759 				if(  !all_names.get(buf).is_bound()  ) {
760 					return strdup(buf);
761 				}
762 				buf.clear();
763 			}
764 		}
765 
766 		// if there are street names, use them
767 		if(  inside  ||  suburb  ) {
768 			const vector_tpl<char*>& street_names( translator::get_street_name_list() );
769 			// make sure we do only ONE random call regardless of how many names are available (to avoid desyncs in network games)
770 			if(  const uint32 count = street_names.get_count()  ) {
771 				uint32 idx = simrand( count );
772 				static const uint32 some_primes[] = { 19, 31, 109, 199, 409, 571, 631, 829, 1489, 1999, 2341, 2971, 3529, 4621, 4789, 7039, 7669, 8779, 9721 };
773 				// find prime that does not divide count
774 				uint32 offset = 1;
775 				for(uint8 i=0; i<lengthof(some_primes); i++) {
776 					if (count % some_primes[i]!=0) {
777 						offset = some_primes[i];
778 						break;
779 					}
780 				}
781 				// as count % offset != 0 we are guaranteed to test all street names
782 				for(uint32 i=0; i<count; i++) {
783 					buf.clear();
784 					if (cbuffer_t::check_format_strings("%s %s", street_names[idx])) {
785 						buf.printf( street_names[idx], city_name, stop );
786 						if(  !all_names.get(buf).is_bound()  ) {
787 							return strdup(buf);
788 						}
789 					}
790 					idx = (idx+offset) % count;
791 				}
792 				buf.clear();
793 			}
794 			else {
795 				/* the one random call to avoid desyncs */
796 				simrand(5);
797 			}
798 		}
799 
800 		// still all names taken => then try the normal naming scheme ...
801 		char numbername[10];
802 		if(inside) {
803 			strcpy( numbername, "0center" );
804 		} else if(suburb) {
805 			// close to the city we use a different scheme, with suburbs
806 			strcpy( numbername, "0suburb" );
807 		}
808 		else {
809 			strcpy( numbername, "0extern" );
810 		}
811 
812 		const char *dirname = NULL;
813 		static const char *diagonal_name[4] = { "nordwest", "nordost", "suedost", "suedwest" };
814 		static const char *direction_name[4] = { "nord", "ost", "sued", "west" };
815 
816 		uint8 const rot = welt->get_settings().get_rotation();
817 		if (k.y < ob_gr  ||  (inside  &&  k.y*3 < (un_gr+ob_gr+ob_gr))  ) {
818 			if (k.x < li_gr) {
819 				dirname = diagonal_name[(4 - rot) % 4];
820 			}
821 			else if (k.x > re_gr) {
822 				dirname = diagonal_name[(5 - rot) % 4];
823 			}
824 			else {
825 				dirname = direction_name[(4 - rot) % 4];
826 			}
827 		} else if (k.y > un_gr  ||  (inside  &&  k.y*3 > (un_gr+un_gr+ob_gr))  ) {
828 			if (k.x < li_gr) {
829 				dirname = diagonal_name[(3 - rot) % 4];
830 			}
831 			else if (k.x > re_gr) {
832 				dirname = diagonal_name[(6 - rot) % 4];
833 			}
834 			else {
835 				dirname = direction_name[(6 - rot) % 4];
836 			}
837 		} else {
838 			if (k.x <= stadt->get_pos().x) {
839 				dirname = direction_name[(3 - rot) % 4];
840 			}
841 			else {
842 				dirname = direction_name[(5 - rot) % 4];
843 			}
844 		}
845 		dirname = translator::translate(dirname,lang);
846 
847 		// Try everything to get a unique name
848 		while(true) {
849 			// well now try them all from "0..." over "9..." to "A..." to "Z..."
850 			for(  int i=0;  i<10+26;  i++  ) {
851 				numbername[0] = i<10 ? '0'+i : 'A'+i-10;
852 				const char *base_name = translator::translate(numbername,lang);
853 				if(base_name==numbername) {
854 					// not translated ... try next
855 					continue;
856 				}
857 				// allow for names without direction
858 				uint8 count_s = count_printf_param( base_name );
859 				if(count_s==3) {
860 					if (cbuffer_t::check_format_strings("%s %s %s", base_name) ) {
861 						// ok, try this name, if free ...
862 						buf.printf( base_name, city_name, dirname, stop );
863 					}
864 				}
865 				else {
866 					if (cbuffer_t::check_format_strings("%s %s", base_name) ) {
867 						// ok, try this name, if free ...
868 						buf.printf( base_name, city_name, stop );
869 					}
870 				}
871 				if(  buf.len()>0  &&  !all_names.get(buf).is_bound()  ) {
872 					return strdup(buf);
873 				}
874 				buf.clear();
875 			}
876 			// here we did not find a suitable name ...
877 			// ok, no suitable city names, try the suburb ones ...
878 			if(  strcmp(numbername+1,"center")==0  ) {
879 				strcpy( numbername, "0suburb" );
880 			}
881 			// ok, no suitable suburb names, try the external ones (if not inside city) ...
882 			else if(  strcmp(numbername+1,"suburb")==0  &&  !inside  ) {
883 				strcpy( numbername, "0extern" );
884 			}
885 			else {
886 				// no suitable unique name found at all ...
887 				break;
888 			}
889 		}
890 	}
891 
892 	/* so far we did not found a matching station name
893 	 * as a last resort, we will try numbered names
894 	 * (or the user requested this anyway)
895 	 */
896 
897 	// strings for intown / outside of town
898 	const char *base_name = translator::translate( inside ? "%s city %d %s" : "%s land %d %s", lang);
899 
900 	// finally: is there a stop with this name already?
901 	for(  uint32 i=1;  i<65536;  i++  ) {
902 		buf.printf( base_name, city_name, i, stop );
903 		if(  !all_names.get(buf).is_bound()  ) {
904 			return strdup(buf);
905 		}
906 		buf.clear();
907 	}
908 
909 	// emergency measure: But before we should run out of handles anyway ...
910 	assert(0);
911 	return strdup("Unnamed");
912 }
913 
914 
915 
916 // add convoi to loading
request_loading(convoihandle_t cnv)917 void haltestelle_t::request_loading( convoihandle_t cnv )
918 {
919 	if(  !loading_here.is_contained( cnv )  ) {
920 		loading_here.append( cnv );
921 	}
922 	if(  last_loading_step != welt->get_steps()  ) {
923 		last_loading_step = welt->get_steps();
924 		// now iterate over all convois
925 		for(  slist_tpl<convoihandle_t>::iterator i = loading_here.begin(), end = loading_here.end();  i != end;  ) {
926 			convoihandle_t const c = *i;
927 			if (c.is_bound() && c->get_state() == convoi_t::LOADING) {
928 				// now we load into convoi
929 				c->hat_gehalten(self);
930 			}
931 			if (c.is_bound() && c->get_state() == convoi_t::LOADING) {
932 				++i;
933 			}
934 			else {
935 				i = loading_here.erase( i );
936 			}
937 		}
938 	}
939 }
940 
941 
942 
step(uint8 what,sint16 & units_remaining)943 bool haltestelle_t::step(uint8 what, sint16 &units_remaining)
944 {
945 	switch(what) {
946 		case RECONNECTING:
947 			units_remaining -= (rebuild_connections()/256)+2;
948 			break;
949 		case REROUTING:
950 			if(  !reroute_goods(units_remaining)  ) {
951 				return false;
952 			}
953 			recalc_status();
954 			break;
955 		default:
956 			break;
957 	}
958 	return true;
959 }
960 
961 
962 
963 /**
964  * Called every month
965  * @author Hj. Malthaner
966  */
new_month()967 void haltestelle_t::new_month()
968 {
969 	if(  welt->get_active_player()==owner  &&  status_color==color_idx_to_rgb(COL_RED)  ) {
970 		cbuffer_t buf;
971 		buf.printf( translator::translate("%s\nis crowded."), get_name() );
972 		welt->get_message()->add_message(buf, get_basis_pos(),message_t::full, PLAYER_FLAG|owner->get_player_nr(), IMG_EMPTY );
973 		enables &= (PAX|POST|WARE);
974 	}
975 
976 	// hsiegeln: roll financial history
977 	for (int j = 0; j<MAX_HALT_COST; j++) {
978 		for (int k = MAX_MONTHS-1; k>0; k--) {
979 			financial_history[k][j] = financial_history[k-1][j];
980 		}
981 		financial_history[0][j] = 0;
982 	}
983 	// number of waiting should be constant ...
984 	financial_history[0][HALT_WAITING] = financial_history[1][HALT_WAITING];
985 }
986 
987 
988 
989 /**
990  * Called after schedule calculation of all stations is finished
991  * will distribute the goods to changed routes (if there are any)
992  * returns true upon completion
993  * @author Hj. Malthaner
994  */
reroute_goods(sint16 & units_remaining)995 bool haltestelle_t::reroute_goods(sint16 &units_remaining)
996 {
997 	if(  last_catg_index==255  ) {
998 		last_catg_index = 0;
999 	}
1000 
1001 	for(  ; last_catg_index<goods_manager_t::get_max_catg_index(); last_catg_index++) {
1002 
1003 		if(  units_remaining<=0  ) {
1004 			return false;
1005 		}
1006 
1007 		if(cargo[last_catg_index]) {
1008 
1009 			// first: clean out the array
1010 			vector_tpl<ware_t> * warray = cargo[last_catg_index];
1011 			vector_tpl<ware_t> * new_warray = new vector_tpl<ware_t>(warray->get_count());
1012 
1013 			for (size_t j = warray->get_count(); j-- != 0;) {
1014 				ware_t & ware = (*warray)[j];
1015 
1016 				if(ware.menge==0) {
1017 					continue;
1018 				}
1019 
1020 				// since also the factory halt list is added to the ground, we can use just this ...
1021 				if(  welt->access(ware.get_zielpos())->is_connected(self)  ) {
1022 					// we are already there!
1023 					if(  ware.to_factory  ) {
1024 						liefere_an_fabrik(ware);
1025 					}
1026 					continue;
1027 				}
1028 
1029 				// add to new array
1030 				new_warray->append( ware );
1031 			}
1032 
1033 			// delete, if nothing connects here
1034 			if(  new_warray->empty()  ) {
1035 				if(  all_links[last_catg_index].connections.empty()  ) {
1036 					// no connections from here => delete
1037 					delete new_warray;
1038 					new_warray = NULL;
1039 				}
1040 			}
1041 
1042 			// replace the array
1043 			delete cargo[last_catg_index];
1044 			cargo[last_catg_index] = new_warray;
1045 
1046 			// if something left
1047 			// re-route goods to adapt to changes in world layout,
1048 			// remove all goods whose destination was removed from the map
1049 			if (cargo[last_catg_index] && !cargo[last_catg_index]->empty()) {
1050 
1051 				vector_tpl<ware_t> &warray = *cargo[last_catg_index];
1052 				uint32 last_goods_index = 0;
1053 				units_remaining -= warray.get_count();
1054 				while(  last_goods_index<warray.get_count()  ) {
1055 					search_route_resumable(warray[last_goods_index]);
1056 					if(  warray[last_goods_index].get_ziel()==halthandle_t()  ) {
1057 						// remove invalid destinations
1058 						fabrik_t::update_transit( &warray[last_goods_index], false);
1059 						warray.remove_at(last_goods_index);
1060 					}
1061 					else {
1062 						++last_goods_index;
1063 					}
1064 				}
1065 			}
1066 		}
1067 	}
1068 	// likely the display must be updated after this
1069 	resort_freight_info = true;
1070 	last_catg_index = 255;	// all categories are rerouted
1071 	return true;	// all updated ...
1072 }
1073 
1074 
1075 
1076 /*
1077  * connects a factory to a halt
1078  */
verbinde_fabriken()1079 void haltestelle_t::verbinde_fabriken()
1080 {
1081 	// unlink all
1082 	FOR(slist_tpl<fabrik_t*>, const f, fab_list) {
1083 		f->unlink_halt(self);
1084 	}
1085 	fab_list.clear();
1086 
1087 	// then reconnect
1088 	FOR(slist_tpl<tile_t>, const& i, tiles) {
1089 		koord const p = i.grund->get_pos().get_2d();
1090 
1091 		uint16 const cov = welt->get_settings().get_station_coverage();
1092 		FOR(vector_tpl<fabrik_t*>, const fab, fabrik_t::sind_da_welche(p - koord(cov, cov), p + koord(cov, cov))) {
1093 			if(!fab_list.is_contained(fab)) {
1094 				// water factories can only connect to docks
1095 				if(  fab->get_desc()->get_placement() != factory_desc_t::Water  ||  (station_type & dock) > 0  ) {
1096 					// do no link to oil rigs via stations ...
1097 					fab_list.insert(fab);
1098 					fab->link_halt(self);
1099 				}
1100 			}
1101 		}
1102 	}
1103 }
1104 
1105 
1106 
1107 /*
1108  * removes factory to a halt
1109  */
remove_fabriken(fabrik_t * fab)1110 void haltestelle_t::remove_fabriken(fabrik_t *fab)
1111 {
1112 	fab_list.remove(fab);
1113 }
1114 
1115 
1116 /**
1117  * Rebuilds the list of connections to directly reachable halts
1118  * Returns the number of stops considered
1119  * @author Hj. Malthaner
1120  */
1121 #define WEIGHT_WAIT (8)
1122 #define WEIGHT_HALT (1)
1123 // the minimum weight of a connection from a transfer halt
1124 #define WEIGHT_MIN (WEIGHT_WAIT+WEIGHT_HALT)
rebuild_connections()1125 sint32 haltestelle_t::rebuild_connections()
1126 {
1127 	// Knightly : halts which either immediately precede or succeed self halt in serving schedules
1128 	static vector_tpl<halthandle_t> consecutive_halts[256];
1129 	// Dwachs : halts which either immediately precede or succeed self halt in currently processed schedule
1130 	static vector_tpl<halthandle_t> consecutive_halts_schedule[256];
1131 	// remember max number of consecutive halts for one schedule
1132 	uint8 max_consecutive_halts_schedule[256];
1133 	MEMZERON(max_consecutive_halts_schedule, goods_manager_t::get_max_catg_index());
1134 	// Knightly : previous halt supporting the ware categories of the serving line
1135 	static halthandle_t previous_halt[256];
1136 
1137 	// Hajo: first, remove all old entries
1138 	for(  uint8 i=0;  i<goods_manager_t::get_max_catg_index();  i++  ){
1139 		all_links[i].clear();
1140 		consecutive_halts[i].clear();
1141 	}
1142 	resort_freight_info = true;	// might result in error in routing
1143 
1144 	last_catg_index = 255;	// must reroute everything
1145 	sint32 connections_searched = 0;
1146 
1147 // DBG_MESSAGE("haltestelle_t::rebuild_destinations()", "Adding new table entries");
1148 
1149 	const player_t *owner;
1150 	schedule_t *schedule;
1151 	const minivec_tpl<uint8> *goods_catg_index;
1152 
1153 	minivec_tpl<uint8> supported_catg_index(32);
1154 
1155 	/*
1156 	 * In the first loops:
1157 	 * lines==true => search for lines
1158 	 * After this:
1159 	 * lines==false => search for single convoys without lines
1160 	 */
1161 	bool lines = true;
1162 	uint32 current_index = 0;
1163 	while(  lines  ||  current_index < registered_convoys.get_count()  ) {
1164 
1165 		// Now, collect the "schedule", "owner" and "add_catg_index" from line resp. convoy.
1166 		if(  lines  ) {
1167 			if(  current_index >= registered_lines.get_count()  ) {
1168 				// We have looped over all lines.
1169 				lines = false;
1170 				current_index = 0;	// Knightly : start over for registered lineless convoys
1171 				continue;
1172 			}
1173 
1174 			const linehandle_t line = registered_lines[current_index];
1175 			++current_index;
1176 
1177 			owner = line->get_owner();
1178 			schedule = line->get_schedule();
1179 			goods_catg_index = &line->get_goods_catg_index();
1180 		}
1181 		else {
1182 			const convoihandle_t cnv = registered_convoys[current_index];
1183 			++current_index;
1184 
1185 			owner = cnv->get_owner();
1186 			schedule = cnv->get_schedule();
1187 			goods_catg_index = &cnv->get_goods_catg_index();
1188 		}
1189 
1190 		// find the index from which to start processing
1191 		uint8 start_index = 0;
1192 		while(  start_index < schedule->get_count()  &&  get_halt( schedule->entries[start_index].pos, owner ) != self  ) {
1193 			++start_index;
1194 		}
1195 		++start_index;	// the next index after self halt; it's okay to be out-of-range
1196 
1197 		// determine goods category indices supported by this halt
1198 		supported_catg_index.clear();
1199 		FOR(minivec_tpl<uint8>, const catg_index, *goods_catg_index) {
1200 			if(  is_enabled(catg_index)  ) {
1201 				supported_catg_index.append(catg_index);
1202 				previous_halt[catg_index] = self;
1203 				consecutive_halts_schedule[catg_index].clear();
1204 			}
1205 		}
1206 
1207 		if(  supported_catg_index.empty()  ) {
1208 			// this halt does not support the goods categories handled by the line/lineless convoy
1209 			continue;
1210 		}
1211 
1212 		INT_CHECK("simhalt.cc 612");
1213 
1214 		// now we add the schedule to the connection array
1215 		uint16 aggregate_weight = WEIGHT_WAIT;
1216 		for(  uint8 j=0;  j<schedule->get_count();  ++j  ) {
1217 
1218 			halthandle_t current_halt = get_halt(schedule->entries[(start_index+j)%schedule->get_count()].pos, owner );
1219 			if(  !current_halt.is_bound()  ) {
1220 				// ignore way points
1221 				continue;
1222 			}
1223 			if(  current_halt == self  ) {
1224 				// Knightly : check for consecutive halts which precede self halt
1225 				FOR(minivec_tpl<uint8>, const catg_index, supported_catg_index) {
1226 					if(  previous_halt[catg_index]!=self  ) {
1227 						consecutive_halts[catg_index].append_unique(previous_halt[catg_index]);
1228 						consecutive_halts_schedule[catg_index].append_unique(previous_halt[catg_index]);
1229 						previous_halt[catg_index] = self;
1230 					}
1231 				}
1232 				// reset aggregate weight
1233 				aggregate_weight = WEIGHT_WAIT;
1234 				continue;
1235 			}
1236 
1237 			aggregate_weight += WEIGHT_HALT;
1238 
1239 			FOR(minivec_tpl<uint8>, const catg_index, supported_catg_index) {
1240 				if(  current_halt->is_enabled(catg_index)  ) {
1241 					// Knightly : check for consecutive halts which succeed self halt
1242 					if(  previous_halt[catg_index] == self  ) {
1243 						consecutive_halts[catg_index].append_unique(current_halt);
1244 						consecutive_halts_schedule[catg_index].append_unique(current_halt);
1245 					}
1246 					previous_halt[catg_index] = current_halt;
1247 
1248 					// either add a new connection or update the weight of an existing connection where necessary
1249 					connection_t *const existing_connection = all_links[catg_index].connections.insert_unique_ordered( connection_t( current_halt, aggregate_weight ), connection_t::compare );
1250 					if(  existing_connection  &&  aggregate_weight<existing_connection->weight  ) {
1251 						existing_connection->weight = aggregate_weight;
1252 					}
1253 				}
1254 			}
1255 		}
1256 
1257 		FOR(minivec_tpl<uint8>, const catg_index, supported_catg_index) {
1258 			if(  consecutive_halts_schedule[catg_index].get_count() > max_consecutive_halts_schedule[catg_index]  ) {
1259 				max_consecutive_halts_schedule[catg_index] = consecutive_halts_schedule[catg_index].get_count();
1260 			}
1261 		}
1262 		connections_searched += schedule->get_count();
1263 	}
1264 	for(  uint8 i=0;  i<goods_manager_t::get_max_catg_index();  i++  ){
1265 		if(  !consecutive_halts[i].empty()  ) {
1266 			if(  consecutive_halts[i].get_count() == max_consecutive_halts_schedule[i]  ) {
1267 				// one schedule reaches all consecutive halts -> this is not transfer halt
1268 			}
1269 			else {
1270 				all_links[i].is_transfer = true;
1271 			}
1272 		}
1273 	}
1274 	return connections_searched;
1275 }
1276 
1277 
rebuild_linked_connections()1278 void haltestelle_t::rebuild_linked_connections()
1279 {
1280 	vector_tpl<halthandle_t> all; // all halts connected to this halt
1281 	for(  uint8 i=0;  i<goods_manager_t::get_max_catg_index();  i++  ){
1282 		vector_tpl<connection_t>& connections = all_links[i].connections;
1283 
1284 		FOR(vector_tpl<connection_t>, &c, connections) {
1285 			all.append_unique( c.halt );
1286 		}
1287 	}
1288 	FOR(vector_tpl<halthandle_t>, h, all) {
1289 		h->rebuild_connections();
1290 	}
1291 }
1292 
1293 
fill_connected_component(uint8 catg_idx,uint16 comp)1294 void haltestelle_t::fill_connected_component(uint8 catg_idx, uint16 comp)
1295 {
1296 	if (all_links[catg_idx].catg_connected_component != UNDECIDED_CONNECTED_COMPONENT) {
1297 		// already connected
1298 		return;
1299 	}
1300 	all_links[catg_idx].catg_connected_component = comp;
1301 
1302 	FOR(vector_tpl<connection_t>, &c, all_links[catg_idx].connections) {
1303 		c.halt->fill_connected_component(catg_idx, comp);
1304 		// cache the is_transfer value
1305 		c.is_transfer = c.halt->is_transfer(catg_idx);
1306 	}
1307 }
1308 
1309 
rebuild_connected_components()1310 void haltestelle_t::rebuild_connected_components()
1311 {
1312 	for(uint8 catg_idx = 0; catg_idx<goods_manager_t::get_max_catg_index(); catg_idx++) {
1313 		FOR(vector_tpl<halthandle_t>, halt, alle_haltestellen) {
1314 			if (halt->all_links[catg_idx].catg_connected_component == UNDECIDED_CONNECTED_COMPONENT) {
1315 				// start recursion
1316 				halt->fill_connected_component(catg_idx, halt.get_id());
1317 			}
1318 		}
1319 	}
1320 }
1321 
1322 
is_connected(halthandle_t halt,uint8 catg_index) const1323 sint8 haltestelle_t::is_connected(halthandle_t halt, uint8 catg_index) const
1324 {
1325 	if (!halt.is_bound()) {
1326 		return 0; // not connected
1327 	}
1328 	const link_t& linka =       all_links[catg_index];
1329 	const link_t& linkb = halt->all_links[catg_index];
1330 	if (linka.connections.empty()  ||  linkb.connections.empty()) {
1331 		return 0; // empty connections -> not connected
1332 	}
1333 	if (linka.catg_connected_component == UNDECIDED_CONNECTED_COMPONENT  ||  linkb.catg_connected_component == UNDECIDED_CONNECTED_COMPONENT) {
1334 		return -1; // undecided - try later
1335 	}
1336 	// now check whether both halts are in the same component
1337 	return linka.catg_connected_component == linkb.catg_connected_component ? 1 : 0;
1338 }
1339 
1340 
1341 /**
1342  * Data for route searching
1343  */
1344 haltestelle_t::halt_data_t haltestelle_t::halt_data[65536];
1345 binary_heap_tpl<haltestelle_t::route_node_t> haltestelle_t::open_list;
1346 uint8 haltestelle_t::markers[65536];
1347 uint8 haltestelle_t::current_marker = 0;
1348 /**
1349  * Data for resumable route search
1350  */
1351 halthandle_t haltestelle_t::last_search_origin;
1352 uint8 haltestelle_t::last_search_ware_catg_idx = 255;
1353 /**
1354  * This routine tries to find a route for a good packet (ware)
1355  * it will be called for
1356  *  - new goods (either from simcity.cc or simfab.cc)
1357  *  - goods that transfer and cannot be joined with other goods
1358  *  - during re-routing
1359  * Therefore this routine eats up most of the performance in
1360  * later games. So all changes should be done with this in mind!
1361  *
1362  * If no route is found, ziel and zwischenziel are unbound handles.
1363  * If next_to_ziel in not NULL, it will get the koordinate of the stop
1364  * previous to target. Can be used to create passengers/mail back the
1365  * same route back
1366  *
1367  * if USE_ROUTE_SLIST_TPL is defined, the list template will be used.
1368  * However, this is about 50% slower.
1369  *
1370  * @author Hj. Malthaner/prissi/gerw/Knightly
1371  */
search_route(const halthandle_t * const start_halts,const uint16 start_halt_count,const bool no_routing_over_overcrowding,ware_t & ware,ware_t * const return_ware)1372 int haltestelle_t::search_route( const halthandle_t *const start_halts, const uint16 start_halt_count, const bool no_routing_over_overcrowding, ware_t &ware, ware_t *const return_ware )
1373 {
1374 	const uint8 ware_catg_idx = ware.get_desc()->get_catg_index();
1375 	const uint8 ware_idx = ware.get_desc()->get_index();
1376 
1377 	// since also the factory halt list is added to the ground, we can use just this ...
1378 	const planquadrat_t *const plan = welt->access( ware.get_zielpos() );
1379 	const halthandle_t *const halt_list = plan->get_haltlist();
1380 	// but we can only use a subset of these
1381 	static vector_tpl<halthandle_t> end_halts(16);
1382 	end_halts.clear();
1383 	// target halts are in these connected components
1384 	// we start from halts only in the same components
1385 	static vector_tpl<uint16> end_conn_comp(16);
1386 	end_conn_comp.clear();
1387 	// if one target halt is undefined, we have to start search from all halts
1388 	bool end_conn_comp_undefined = false;
1389 
1390 	for( uint32 h=0;  h<plan->get_haltlist_count();  ++h ) {
1391 		halthandle_t halt = halt_list[h];
1392 		if(  halt.is_bound()  &&  halt->is_enabled(ware_catg_idx)  ) {
1393 			// check if this is present in the list of start halts
1394 			for(  uint16 s=0;  s<start_halt_count;  ++s  ) {
1395 				if(  halt==start_halts[s]  ) {
1396 					// destination halt is also a start halt -> within walking distance
1397 					ware.set_ziel( start_halts[s] );
1398 					ware.set_zwischenziel( halthandle_t() );
1399 					if(  return_ware  ) {
1400 						return_ware->set_ziel( start_halts[s] );
1401 						return_ware->set_zwischenziel( halthandle_t() );
1402 					}
1403 					return ROUTE_WALK;
1404 				}
1405 			}
1406 			end_halts.append(halt);
1407 
1408 			// check connected component of target halt
1409 			uint16 endhalt_conn_comp = halt->all_links[ware_catg_idx].catg_connected_component;
1410 			if (endhalt_conn_comp == UNDECIDED_CONNECTED_COMPONENT) {
1411 				// undefined: all start halts are probably connected to this target
1412 				end_conn_comp_undefined = true;
1413 			}
1414 			else {
1415 				// store connected component
1416 				if (!end_conn_comp_undefined) {
1417 					end_conn_comp.append_unique( endhalt_conn_comp );
1418 				}
1419 			}
1420 		}
1421 	}
1422 
1423 	if(  end_halts.empty()  ) {
1424 		// no end halt found
1425 		ware.set_ziel( halthandle_t() );
1426 		ware.set_zwischenziel( halthandle_t() );
1427 		if(  return_ware  ) {
1428 			return_ware->set_ziel( halthandle_t() );
1429 			return_ware->set_zwischenziel( halthandle_t() );
1430 		}
1431 		return NO_ROUTE;
1432 	}
1433 	// invalidate search history
1434 	last_search_origin = halthandle_t();
1435 
1436 	// set current marker
1437 	++current_marker;
1438 	if(  current_marker==0  ) {
1439 		MEMZERON(markers, halthandle_t::get_size());
1440 		current_marker = 1u;
1441 	}
1442 
1443 	// initialisations for end halts => save some checking inside search loop
1444 	FOR(vector_tpl<halthandle_t>, const e, end_halts) {
1445 		uint16 const halt_id = e.get_id();
1446 		halt_data[ halt_id ].best_weight = 65535u;
1447 		halt_data[ halt_id ].destination = 1u;
1448 		halt_data[ halt_id ].depth       = 1u; // to distinct them from start halts
1449 		markers[ halt_id ] = current_marker;
1450 	}
1451 
1452 	uint16 const max_transfers = welt->get_settings().get_max_transfers();
1453 	uint16 const max_hops      = welt->get_settings().get_max_hops();
1454 	uint16 allocation_pointer = 0;
1455 	uint16 best_destination_weight = 65535u;		// best weight among all destinations
1456 
1457 	open_list.clear();
1458 
1459 	uint32 overcrowded_nodes = 0;
1460 	// initialise the origin node(s)
1461 	for(  ;  allocation_pointer<start_halt_count;  ++allocation_pointer  ) {
1462 		halthandle_t start_halt = start_halts[allocation_pointer];
1463 
1464 		uint16 start_conn_comp = start_halt->all_links[ware_catg_idx].catg_connected_component;
1465 
1466 		if (!end_conn_comp_undefined   &&  start_conn_comp != UNDECIDED_CONNECTED_COMPONENT  &&  !end_conn_comp.is_contained( start_conn_comp  )){
1467 			// this start halt will not lead to any target
1468 			continue;
1469 		}
1470 		open_list.insert( route_node_t(start_halt, 0) );
1471 
1472 		halt_data_t & start_data = halt_data[ start_halt.get_id() ];
1473 		start_data.best_weight = 65535u;
1474 		start_data.destination = 0;
1475 		start_data.depth       = 0;
1476 		start_data.overcrowded = false; // start halt overcrowding is handled by routines calling this one
1477 		start_data.transfer    = halthandle_t();
1478 
1479 		markers[ start_halt.get_id() ] = current_marker;
1480 	}
1481 
1482 	// here the normal routing with overcrowded stops is done
1483 	while (!open_list.empty())
1484 	{
1485 		if(  overcrowded_nodes == open_list.get_count()  ) {
1486 			// all unexplored routes go over overcrowded stations
1487 			return ROUTE_OVERCROWDED;
1488 		}
1489 
1490 		// take node out of open list
1491 		route_node_t current_node = open_list.pop();
1492 		// do not use aggregate_weight as it is _not_ the weight of the current_node
1493 		// there might be a heuristic weight added
1494 
1495 		const uint16 current_halt_id = current_node.halt.get_id();
1496 		halt_data_t & current_halt_data = halt_data[ current_halt_id ];
1497 		overcrowded_nodes -= current_halt_data.overcrowded;
1498 
1499 		if(  current_halt_data.destination  ) {
1500 			// destination found
1501 			ware.set_ziel( current_node.halt );
1502 			assert(current_halt_data.transfer.get_id() != 0);
1503 			if(  return_ware  ) {
1504 				// next transfer for the reverse route
1505 				// if the end halt and its connections contain more than one transfer halt then
1506 				// the transfer halt may not be the last transfer of the forward route
1507 				// (the re-routing will happen in haltestelle_t::fetch_goods)
1508 				return_ware->set_zwischenziel(current_halt_data.transfer);
1509 				// count the connected transfer halts (including end halt)
1510 				uint8 t = current_node.halt->is_transfer(ware_catg_idx);
1511 				FOR(vector_tpl<connection_t>, const& i, current_node.halt->all_links[ware_catg_idx].connections) {
1512 					if (t > 1) {
1513 						break;
1514 					}
1515 					t += i.halt.is_bound() && i.is_transfer;
1516 				}
1517 				return_ware->set_zwischenziel(  t<=1  ?  current_halt_data.transfer  : halthandle_t());
1518 			}
1519 			// find the next transfer
1520 			halthandle_t transfer_halt = current_node.halt;
1521 			while(  halt_data[ transfer_halt.get_id() ].depth > 1   ) {
1522 				transfer_halt = halt_data[ transfer_halt.get_id() ].transfer;
1523 			}
1524 			ware.set_zwischenziel(transfer_halt);
1525 			if(  return_ware  ) {
1526 				// return ware's destination halt is the start halt of the forward trip
1527 				assert( halt_data[ transfer_halt.get_id() ].transfer.get_id() );
1528 				return_ware->set_ziel( halt_data[ transfer_halt.get_id() ].transfer );
1529 			}
1530 			return current_halt_data.overcrowded ? ROUTE_OVERCROWDED : ROUTE_OK;
1531 		}
1532 
1533 		// check if the current halt is already in closed list
1534 		if(  current_halt_data.best_weight==0  ) {
1535 			// shortest path to the current halt has already been found earlier
1536 			continue;
1537 		}
1538 
1539 		if(  current_halt_data.depth > max_transfers  ) {
1540 			// maximum transfer limit is reached -> do not add reachable halts to open list
1541 			continue;
1542 		}
1543 
1544 		FOR(vector_tpl<connection_t>, const& current_conn, current_node.halt->all_links[ware_catg_idx].connections) {
1545 
1546 			// halt may have been deleted or joined => test if still valid
1547 			if(  !current_conn.halt.is_bound()  ) {
1548 				// removal seems better though ...
1549 				continue;
1550 			}
1551 
1552 			// since these are pre-calculated, they should be always pointing to a valid ground
1553 			// (if not, we were just under construction, and will be fine after 16 steps)
1554 			const uint16 reachable_halt_id = current_conn.halt.get_id();
1555 
1556 			if(  markers[ reachable_halt_id ]!=current_marker  ) {
1557 				// Case : not processed before
1558 
1559 				// indicate that this halt has been processed
1560 				markers[ reachable_halt_id ] = current_marker;
1561 
1562 				if(  current_conn.halt.is_bound()  &&  current_conn.is_transfer  &&  allocation_pointer<max_hops  ) {
1563 					// Case : transfer halt
1564 					const uint16 total_weight = current_halt_data.best_weight + current_conn.weight;
1565 
1566 					if(  total_weight < best_destination_weight  ) {
1567 						const bool overcrowded_transfer = no_routing_over_overcrowding  &&  ( current_halt_data.overcrowded  ||  current_conn.halt->is_overcrowded( ware_idx ) );
1568 
1569 						halt_data[ reachable_halt_id ].best_weight = total_weight;
1570 						halt_data[ reachable_halt_id ].destination = 0;
1571 						halt_data[ reachable_halt_id ].depth       = current_halt_data.depth + 1u;
1572 						halt_data[ reachable_halt_id ].transfer    = current_node.halt;
1573 						halt_data[ reachable_halt_id ].overcrowded = overcrowded_transfer;
1574 						overcrowded_nodes                         += overcrowded_transfer;
1575 
1576 						allocation_pointer++;
1577 						// as the next halt is not a destination add WEIGHT_MIN
1578 						open_list.insert( route_node_t(current_conn.halt, total_weight + WEIGHT_MIN) );
1579 					}
1580 					else {
1581 						// Case: non-optimal transfer halt -> put in closed list
1582 						halt_data[ reachable_halt_id ].best_weight = 0;
1583 					}
1584 				}
1585 				else {
1586 					// Case: halt is removed / no transfer halt -> put in closed list
1587 					halt_data[ reachable_halt_id ].best_weight = 0;
1588 				}
1589 
1590 			}	// if not processed before
1591 			else if(  halt_data[ reachable_halt_id ].best_weight!=0  &&  halt_data[ reachable_halt_id ].depth>0) {
1592 				// Case : processed before but not in closed list : that is, in open list
1593 				//			--> can only be destination halt or transfer halt
1594 				//			    or start halt (filter the latter out with the condition depth>0)
1595 
1596 				uint16 total_weight = current_halt_data.best_weight + current_conn.weight;
1597 
1598 				if(  total_weight<halt_data[ reachable_halt_id ].best_weight  &&  total_weight<best_destination_weight  &&  allocation_pointer<max_hops  ) {
1599 					// new weight is lower than lowest weight --> create new node and update halt data
1600 					const bool overcrowded_transfer = no_routing_over_overcrowding  &&  ( current_halt_data.overcrowded  ||  ( !halt_data[reachable_halt_id].destination  &&  current_conn.halt->is_overcrowded( ware_idx ) ) );
1601 
1602 					halt_data[ reachable_halt_id ].best_weight = total_weight;
1603 					// no need to update destination, as halt nature (as destination or transfer) will not change
1604 					halt_data[ reachable_halt_id ].depth       = current_halt_data.depth + 1u;
1605 					halt_data[ reachable_halt_id ].transfer    = current_node.halt;
1606 					halt_data[ reachable_halt_id ].overcrowded = overcrowded_transfer;
1607 					overcrowded_nodes                         += overcrowded_transfer;
1608 
1609 					if(  halt_data[reachable_halt_id].destination  ) {
1610 						best_destination_weight = total_weight;
1611 					}
1612 					else {
1613 						// as the next halt is not a destination add WEIGHT_MIN
1614 						total_weight += WEIGHT_MIN;
1615 					}
1616 
1617 					allocation_pointer++;
1618 					open_list.insert( route_node_t(current_conn.halt, total_weight) );
1619 				}
1620 			}	// else if not in closed list
1621 		}	// for each connection entry
1622 
1623 		// indicate that the current halt is in closed list
1624 		current_halt_data.best_weight = 0;
1625 	}
1626 
1627 	// if the loop ends, nothing was found
1628 	ware.set_ziel( halthandle_t() );
1629 	ware.set_zwischenziel( halthandle_t() );
1630 	if(  return_ware  ) {
1631 		return_ware->set_ziel( halthandle_t() );
1632 		return_ware->set_zwischenziel( halthandle_t() );
1633 	}
1634 	return NO_ROUTE;
1635 }
1636 
1637 
search_route_resumable(ware_t & ware)1638 void haltestelle_t::search_route_resumable(  ware_t &ware   )
1639 {
1640 	const uint8 ware_catg_idx = ware.get_desc()->get_catg_index();
1641 
1642 	// continue search if start halt and good category did not change
1643 	const bool resume_search = last_search_origin == self  &&  ware_catg_idx == last_search_ware_catg_idx;
1644 
1645 	if (!resume_search) {
1646 		last_search_origin = self;
1647 		last_search_ware_catg_idx = ware_catg_idx;
1648 		open_list.clear();
1649 		// set current marker
1650 		++current_marker;
1651 		if(  current_marker==0  ) {
1652 			MEMZERON(markers, halthandle_t::get_size());
1653 			current_marker = 1u;
1654 		}
1655 	}
1656 
1657 	// remember destination nodes, to reset them before returning
1658 	static vector_tpl<uint16> dest_indices(16);
1659 	dest_indices.clear();
1660 
1661 	uint16 best_destination_weight = 65535u;
1662 
1663 	// reset next transfer and destination halt to null -> if they remain null after search, no route can be found
1664 	ware.set_ziel( halthandle_t() );
1665 	ware.set_zwischenziel( halthandle_t() );
1666 	// find suitable destination halts for the ware packet's target position
1667 	const planquadrat_t *const plan = welt->access( ware.get_zielpos() );
1668 	const halthandle_t *const halt_list = plan->get_haltlist();
1669 
1670 	// check halt list for presence of current halt
1671 	for( uint8 h = 0;  h<plan->get_haltlist_count(); ++h  ) {
1672 		if (halt_list[h] == self) {
1673 			// a destination halt is the same as the current halt -> no route searching is necessary
1674 			ware.set_ziel( self );
1675 			return;
1676 		}
1677 	}
1678 
1679 	// check explored connection
1680 	if (resume_search) {
1681 		for( uint8 h=0;  h<plan->get_haltlist_count();  ++h  ) {
1682 			const halthandle_t halt = halt_list[h];
1683 			if (markers[ halt.get_id() ]==current_marker  &&  halt_data[ halt.get_id() ].best_weight < best_destination_weight  &&  halt.is_bound()) {
1684 				best_destination_weight = halt_data[ halt.get_id() ].best_weight;
1685 				ware.set_ziel( halt );
1686 				ware.set_zwischenziel( halt_data[ halt.get_id() ].transfer );
1687 			}
1688 		}
1689 		// for all halts with halt_data.weight < explored_weight one of the best routes is found
1690 		const uint16 explored_weight = open_list.empty()  ? 65535u : open_list.front().aggregate_weight;
1691 
1692 		if (best_destination_weight <= explored_weight  &&  best_destination_weight < 65535u) {
1693 			// we explored best route to this destination in last run
1694 			// (any other not yet explored connection will have larger weight)
1695 			// no need to search route for this ware
1696 			return;
1697 		}
1698 	}
1699 	// we start in this connected component
1700 	uint16 const conn_comp = all_links[ ware_catg_idx ].catg_connected_component;
1701 
1702 	// find suitable destination halt(s), if any
1703 	for( uint8 h=0;  h<plan->get_haltlist_count();  ++h  ) {
1704 		const halthandle_t halt = halt_list[h];
1705 		if(  halt.is_bound()  &&  halt->is_enabled(ware_catg_idx)  ) {
1706 
1707 			// test for connected component
1708 			uint16 const dest_comp = halt->all_links[ ware_catg_idx ].catg_connected_component;
1709 			if (dest_comp != UNDECIDED_CONNECTED_COMPONENT  &&  conn_comp != UNDECIDED_CONNECTED_COMPONENT  &&  conn_comp != dest_comp) {
1710 				continue;
1711 			}
1712 
1713 			// initialisations for destination halts => save some checking inside search loop
1714 			if(  markers[ halt.get_id() ]!=current_marker  ) {
1715 				// first time -> initialise marker and all halt data
1716 				markers[ halt.get_id() ] = current_marker;
1717 				halt_data[ halt.get_id() ].best_weight = 65535u;
1718 				halt_data[ halt.get_id() ].destination = true;
1719 			}
1720 			else {
1721 				// initialised before -> only update destination bit set
1722 				halt_data[ halt.get_id() ].destination = true;
1723 			}
1724 			dest_indices.append(halt.get_id());
1725 		}
1726 	}
1727 
1728 	if(  dest_indices.empty()  ) {
1729 		// no destination halt found or current halt is the same as (all) the destination halt(s)
1730 		return;
1731 	}
1732 
1733 	uint16 const max_transfers = welt->get_settings().get_max_transfers();
1734 	uint16 const max_hops      = welt->get_settings().get_max_hops();
1735 
1736 	static uint16 allocation_pointer;
1737 	if (!resume_search) {
1738 		// initialise the origin node
1739 		allocation_pointer = 1u;
1740 		open_list.insert( route_node_t(self, 0) );
1741 
1742 		halt_data_t & start_data = halt_data[ self.get_id() ];
1743 		start_data.best_weight = 0;
1744 		start_data.destination = 0;
1745 		start_data.depth       = 0;
1746 		start_data.transfer    = halthandle_t();
1747 
1748 		markers[ self.get_id() ] = current_marker;
1749 	}
1750 
1751 	while(  !open_list.empty()  ) {
1752 
1753 		if (best_destination_weight <= open_list.front().aggregate_weight) {
1754 			// best route to destination found already
1755 			break;
1756 		}
1757 
1758 		route_node_t current_node = open_list.pop();
1759 
1760 		const uint16 current_halt_id = current_node.halt.get_id();
1761 		const uint16 current_weight = current_node.aggregate_weight;
1762 		halt_data_t & current_halt_data = halt_data[ current_halt_id ];
1763 
1764 		// check if the current halt is already in closed list (or removed)
1765 		if(  !current_node.halt.is_bound()  ) {
1766 			continue;
1767 		}
1768 		else if(  current_halt_data.best_weight < current_weight) {
1769 			// shortest path to the current halt has already been found earlier
1770 			// assert(markers[ current_halt_id ]==current_marker);
1771 			continue;
1772 		}
1773 		else {
1774 			// no need to update weight, as it is already the right one
1775 			// assert(current_halt_data.best_weight == current_weight);
1776 		}
1777 
1778 		if(  current_halt_data.destination  ) {
1779 			// destination found
1780 			ware.set_ziel( current_node.halt );
1781 			ware.set_zwischenziel( current_halt_data.transfer );
1782 			// update best_destination_weight to leave loop due to first check above
1783 			best_destination_weight = current_weight;
1784 			// if this destination halt is not a transfer halt -> do not proceed to process its reachable halt(s)
1785 			if(  !current_node.halt->is_transfer(ware_catg_idx)  ) {
1786 				continue;
1787 			}
1788 		}
1789 
1790 		// not start halt, not transfer halt -> do not expand further
1791 		if(  current_halt_data.depth > 0   &&  !current_node.halt->is_transfer(ware_catg_idx)  ) {
1792 			continue;
1793 		}
1794 
1795 		if(  current_halt_data.depth > max_transfers  ) {
1796 			// maximum transfer limit is reached -> do not add reachable halts to open list
1797 			continue;
1798 		}
1799 
1800 		FOR(vector_tpl<connection_t>, const& current_conn, current_node.halt->all_links[ware_catg_idx].connections) {
1801 			const uint16 reachable_halt_id = current_conn.halt.get_id();
1802 
1803 			const uint16 total_weight = current_weight + current_conn.weight;
1804 
1805 			if(  !current_conn.halt.is_bound()  ) {
1806 				// Case: halt removed -> make sure we never visit it again
1807 				markers[ reachable_halt_id ] = current_marker;
1808 				halt_data[ reachable_halt_id ].best_weight = 0;
1809 			}
1810 			else if(  markers[ reachable_halt_id ]!=current_marker  ) {
1811 				// Case : not processed before and not destination
1812 
1813 				// indicate that this halt has been processed
1814 				markers[ reachable_halt_id ] = current_marker;
1815 
1816 				// update data
1817 				halt_data[ reachable_halt_id ].best_weight = total_weight;
1818 				halt_data[ reachable_halt_id ].destination = false; // reset necessary if this was set by search_route
1819 				halt_data[ reachable_halt_id ].depth       = current_halt_data.depth + 1u;
1820 				halt_data[ reachable_halt_id ].transfer    = current_halt_data.transfer.get_id() ? current_halt_data.transfer : current_conn.halt;
1821 
1822 				if(  current_conn.is_transfer  &&  allocation_pointer<max_hops  ) {
1823 					// Case : transfer halt
1824 					allocation_pointer++;
1825 					open_list.insert( route_node_t(current_conn.halt, total_weight) );
1826 				}
1827 			}	// if not processed before
1828 			else {
1829 				// Case : processed before (or destination halt)
1830 				//        -> need to check whether we can reach it with smaller weight
1831 				if(  total_weight<halt_data[ reachable_halt_id ].best_weight  ) {
1832 					// new weight is lower than lowest weight --> update halt data
1833 
1834 					halt_data[ reachable_halt_id ].best_weight = total_weight;
1835 					halt_data[ reachable_halt_id ].transfer    = current_halt_data.transfer.get_id() ? current_halt_data.transfer : current_conn.halt;
1836 
1837 					// for transfer/destination nodes create new node
1838 					if ( (halt_data[ reachable_halt_id ].destination  ||  current_conn.is_transfer )  &&  allocation_pointer<max_hops ) {
1839 						halt_data[ reachable_halt_id ].depth = current_halt_data.depth + 1u;
1840 						allocation_pointer++;
1841 						open_list.insert( route_node_t(current_conn.halt, total_weight) );
1842 					}
1843 				}
1844 			}	// else processed before
1845 		}	// for each connection entry
1846 	}
1847 
1848 	// clear destinations since we may want to do another search with the same current_marker
1849 	FOR(vector_tpl<uint16>, const i, dest_indices) {
1850 		halt_data[i].destination = false;
1851 		if (halt_data[i].best_weight == 65535u) {
1852 			// not processed -> reset marker
1853 			--markers[i];
1854 		}
1855 	}
1856 }
1857 
1858 
1859 /**
1860  * Found route and station uncrowded
1861  * @author Hj. Malthaner
1862  */
add_pax_happy(int n)1863 void haltestelle_t::add_pax_happy(int n)
1864 {
1865 	book(n, HALT_HAPPY);
1866 	recalc_status();
1867 }
1868 
1869 
1870 /**
1871  * Station in walking distance
1872  * @author Hj. Malthaner
1873  */
add_pax_walked(int n)1874 void haltestelle_t::add_pax_walked(int n)
1875 {
1876 	book(n, HALT_WALKED);
1877 }
1878 
1879 
1880 /**
1881  * Station crowded
1882  * @author Hj. Malthaner
1883  */
add_pax_unhappy(int n)1884 void haltestelle_t::add_pax_unhappy(int n)
1885 {
1886 	book(n, HALT_UNHAPPY);
1887 	recalc_status();
1888 }
1889 
1890 
1891 /**
1892  * Found no route
1893  * @author Hj. Malthaner
1894  */
add_pax_no_route(int n)1895 void haltestelle_t::add_pax_no_route(int n)
1896 {
1897 	book(n, HALT_NOROUTE);
1898 }
1899 
1900 
1901 
liefere_an_fabrik(const ware_t & ware) const1902 void haltestelle_t::liefere_an_fabrik(const ware_t& ware) const
1903 {
1904 	fabrik_t *const factory = fabrik_t::get_fab(ware.get_zielpos() );
1905 	if(  factory  ) {
1906 		factory->liefere_an(ware.get_desc(), ware.menge);
1907 	}
1908 }
1909 
1910 
1911 /* retrieves a ware packet for any destination in the list
1912  * needed, if the factory in question wants to remove something
1913  */
recall_ware(ware_t & w,uint32 menge)1914 bool haltestelle_t::recall_ware( ware_t& w, uint32 menge )
1915 {
1916 	w.menge = 0;
1917 	vector_tpl<ware_t> *warray = cargo[w.get_desc()->get_catg_index()];
1918 	if(warray!=NULL) {
1919 		FOR(vector_tpl<ware_t>, & tmp, *warray) {
1920 			// skip empty entries
1921 			if(tmp.menge==0  ||  w.get_index()!=tmp.get_index()  ||  w.get_zielpos()!=tmp.get_zielpos()) {
1922 				continue;
1923 			}
1924 
1925 			// not too much?
1926 			if(tmp.menge > menge) {
1927 				// not all can be loaded
1928 				tmp.menge -= menge;
1929 				w.menge = menge;
1930 			}
1931 			else {
1932 				// leave an empty entry => joining will more often work
1933 				w.menge = tmp.menge;
1934 				tmp.menge = 0;
1935 			}
1936 			book(w.menge, HALT_ARRIVED);
1937 			fabrik_t::update_transit( &w, false );
1938 			resort_freight_info = true;
1939 			return true;
1940 		}
1941 	}
1942 	// nothing to take out
1943 	return false;
1944 }
1945 
1946 
1947 
fetch_goods(slist_tpl<ware_t> & load,const goods_desc_t * good_category,uint32 requested_amount,const vector_tpl<halthandle_t> & destination_halts)1948 void haltestelle_t::fetch_goods( slist_tpl<ware_t> &load, const goods_desc_t *good_category, uint32 requested_amount, const vector_tpl<halthandle_t>& destination_halts)
1949 {
1950 	// prissi: first iterate over the next stop, then over the ware
1951 	// might be a little slower, but ensures that passengers to nearest stop are served first
1952 	// this allows for separate high speed and normal service
1953 	vector_tpl<ware_t> *warray = cargo[good_category->get_catg_index()];
1954 
1955 	if(  warray  &&  !warray->empty()  ) {
1956 		for(  uint32 i=0; i < destination_halts.get_count();  i++  ) {
1957 			halthandle_t plan_halt = destination_halts[i];
1958 
1959 				// The random offset will ensure that all goods have an equal chance to be loaded.
1960 				uint32 offset = simrand(warray->get_count());
1961 				for(  uint32 i=0;  i<warray->get_count();  i++  ) {
1962 					ware_t &tmp = (*warray)[ i+offset ];
1963 
1964 					// prevent overflow (faster than division)
1965 					if(  i+offset+1>=warray->get_count()  ) {
1966 						offset -= warray->get_count();
1967 					}
1968 
1969 					// skip empty entries
1970 					if(tmp.menge==0) {
1971 						continue;
1972 					}
1973 
1974 					// goods without route -> returning passengers/mail
1975 					if(  !tmp.get_zwischenziel().is_bound()  ) {
1976 						search_route_resumable(tmp);
1977 						if (!tmp.get_ziel().is_bound()) {
1978 							// no route anymore
1979 							tmp.menge = 0;
1980 							continue;
1981 						}
1982 					}
1983 
1984 					// compatible car and right target stop?
1985 					if(  tmp.get_zwischenziel()==plan_halt  ) {
1986 						if(  plan_halt->is_overcrowded( tmp.get_index() )  ) {
1987 							if (welt->get_settings().is_avoid_overcrowding() && tmp.get_ziel() != plan_halt) {
1988 								// do not go for transfer to overcrowded transfer stop
1989 								continue;
1990 							}
1991 						}
1992 
1993 						// not too much?
1994 						ware_t neu(tmp);
1995 						if(  tmp.menge > requested_amount  ) {
1996 							// not all can be loaded
1997 							neu.menge = requested_amount;
1998 							tmp.menge -= requested_amount;
1999 							requested_amount = 0;
2000 						}
2001 						else {
2002 							requested_amount -= tmp.menge;
2003 							// leave an empty entry => joining will more often work
2004 							tmp.menge = 0;
2005 						}
2006 						load.insert(neu);
2007 
2008 						book(neu.menge, HALT_DEPARTED);
2009 						resort_freight_info = true;
2010 
2011 						if (requested_amount==0) {
2012 							return;
2013 						}
2014 					}
2015 				}
2016 
2017 				// nothing there to load
2018 		}
2019 	}
2020 }
2021 
2022 
2023 
get_ware_summe(const goods_desc_t * wtyp) const2024 uint32 haltestelle_t::get_ware_summe(const goods_desc_t *wtyp) const
2025 {
2026 	int sum = 0;
2027 	const vector_tpl<ware_t> * warray = cargo[wtyp->get_catg_index()];
2028 	if(warray!=NULL) {
2029 		FOR(vector_tpl<ware_t>, const& i, *warray) {
2030 			if (wtyp->get_index() == i.get_index()) {
2031 				sum += i.menge;
2032 			}
2033 		}
2034 	}
2035 	return sum;
2036 }
2037 
2038 
get_ware_fuer_zielpos(const goods_desc_t * wtyp,const koord zielpos) const2039 uint32 haltestelle_t::get_ware_fuer_zielpos(const goods_desc_t *wtyp, const koord zielpos) const
2040 {
2041 	const vector_tpl<ware_t> * warray = cargo[wtyp->get_catg_index()];
2042 	if(warray!=NULL) {
2043 		FOR(vector_tpl<ware_t>, const& ware, *warray) {
2044 			if(wtyp->get_index()==ware.get_index()  &&  ware.get_zielpos()==zielpos) {
2045 				return ware.menge;
2046 			}
2047 		}
2048 	}
2049 	return 0;
2050 }
2051 
2052 
get_ware_fuer_zwischenziel(const goods_desc_t * wtyp,const halthandle_t zwischenziel) const2053 uint32 haltestelle_t::get_ware_fuer_zwischenziel(const goods_desc_t *wtyp, const halthandle_t zwischenziel) const
2054 {
2055 	uint32 sum = 0;
2056 	const vector_tpl<ware_t> * warray = cargo[wtyp->get_catg_index()];
2057 	if(warray!=NULL) {
2058 		for(unsigned i=0;  i<warray->get_count();  i++ ) {
2059 			const ware_t &ware = (*warray)[i];
2060 			if(wtyp->get_index()==ware.get_index()  &&  ware.get_zwischenziel()==zwischenziel) {
2061 				sum += ware.menge;
2062 			}
2063 		}
2064 	}
2065 	return sum;
2066 }
2067 
2068 
vereinige_waren(const ware_t & ware)2069 bool haltestelle_t::vereinige_waren(const ware_t &ware)
2070 {
2071 	// pruefen ob die ware mit bereits wartender ware vereinigt werden kann
2072 	vector_tpl<ware_t> * warray = cargo[ware.get_desc()->get_catg_index()];
2073 	if(warray!=NULL) {
2074 		FOR(vector_tpl<ware_t>, & tmp, *warray) {
2075 			// join packets with same destination
2076 			if(ware.same_destination(tmp)) {
2077 				if(  ware.get_zwischenziel().is_bound()  &&  ware.get_zwischenziel()!=self  ) {
2078 					// update route if there is newer route
2079 					tmp.set_zwischenziel( ware.get_zwischenziel() );
2080 				}
2081 				tmp.menge += ware.menge;
2082 				resort_freight_info = true;
2083 				return true;
2084 			}
2085 		}
2086 	}
2087 	return false;
2088 }
2089 
2090 
2091 
2092 // put the ware into the internal storage
2093 // take care of all allocation necessary
add_ware_to_halt(ware_t ware)2094 void haltestelle_t::add_ware_to_halt(ware_t ware)
2095 {
2096 	// now we have to add the ware to the stop
2097 	vector_tpl<ware_t> * warray = cargo[ware.get_desc()->get_catg_index()];
2098 	if(warray==NULL) {
2099 		// this type was not stored here before ...
2100 		warray = new vector_tpl<ware_t>(4);
2101 		cargo[ware.get_desc()->get_catg_index()] = warray;
2102 	}
2103 	// the ware will be put into the first entry with menge==0
2104 	resort_freight_info = true;
2105 	FOR(vector_tpl<ware_t>, & i, *warray) {
2106 		if (i.menge == 0) {
2107 			i = ware;
2108 			return;
2109 		}
2110 	}
2111 	// here, if no free entries found
2112 	warray->append(ware);
2113 }
2114 
2115 
2116 
2117 /* same as liefere an, but there will be no route calculated,
2118  * since it hase be calculated just before
2119  * (execption: route contains us as intermediate stop)
2120  * @author prissi
2121  */
starte_mit_route(ware_t ware)2122 uint32 haltestelle_t::starte_mit_route(ware_t ware)
2123 {
2124 	if(ware.get_ziel()==self) {
2125 		if(  ware.to_factory  ) {
2126 			// muss an factory geliefert werden
2127 			liefere_an_fabrik(ware);
2128 		}
2129 		// already there: finished (may be happen with overlapping areas and returning passengers)
2130 		return ware.menge;
2131 	}
2132 
2133 	// no valid next stops? Or we are the next stop?
2134 	if(ware.get_zwischenziel()==self) {
2135 		dbg->error("haltestelle_t::starte_mit_route()","route cannot contain us as first transfer stop => recalc route!");
2136 		if(  search_route( &self, 1u, false, ware )==NO_ROUTE  ) {
2137 			// no route found?
2138 			dbg->error("haltestelle_t::starte_mit_route()","no route found!");
2139 			return ware.menge;
2140 		}
2141 	}
2142 
2143 	// passt das zu bereits wartender ware ?
2144 	if(vereinige_waren(ware)) {
2145 		// dann sind wir schon fertig;
2146 		return ware.menge;
2147 	}
2148 
2149 	// add to internal storage
2150 	add_ware_to_halt(ware);
2151 
2152 	return ware.menge;
2153 }
2154 
2155 
2156 
2157 /* Receives ware and tries to route it further on
2158  * if no route is found, it will be removed
2159  * @author prissi
2160  */
liefere_an(ware_t ware)2161 uint32 haltestelle_t::liefere_an(ware_t ware)
2162 {
2163 	// no valid next stops?
2164 	if(!ware.get_ziel().is_bound()  ||  !ware.get_zwischenziel().is_bound()) {
2165 		// write a log entry and discard the goods
2166 dbg->warning("haltestelle_t::liefere_an()","%d %s delivered to %s have no longer a route to their destination!", ware.menge, translator::translate(ware.get_name()), get_name() );
2167 		return ware.menge;
2168 	}
2169 
2170 	// did we arrived?
2171 	if(  welt->access(ware.get_zielpos())->is_connected(self)  ) {
2172 		if(  ware.to_factory  ) {
2173 			// muss an factory geliefert werden
2174 			liefere_an_fabrik(ware);
2175 		}
2176 		else if(  ware.get_desc() == goods_manager_t::passengers  ) {
2177 			// arriving passenger may create pedestrians
2178 			if(  welt->get_settings().get_show_pax()  ) {
2179 				int menge = ware.menge;
2180 				FOR( slist_tpl<tile_t>, const& i, tiles ) {
2181 					if (menge <= 0) {
2182 						break;
2183 					}
2184 					menge = generate_pedestrians(i.grund->get_pos(), menge);
2185 				}
2186 				INT_CHECK("simhalt 938");
2187 			}
2188 		}
2189 		return ware.menge;
2190 	}
2191 
2192 	// do we have already something going in this direction here?
2193 	if(  vereinige_waren(ware)  ) {
2194 		return ware.menge;
2195 	}
2196 
2197 	// not near enough => we need to do a re-routing
2198 	halthandle_t old_target = ware.get_ziel();
2199 
2200 	search_route_resumable(ware);
2201 	if (!ware.get_ziel().is_bound()) {
2202 		// target halt no longer there => delete and remove from fab in transit
2203 		fabrik_t::update_transit( &ware, false );
2204 		return ware.menge;
2205 	}
2206 	// try to join with existing freight only if target has changed
2207 	if(old_target != ware.get_ziel()  &&  vereinige_waren(ware)) {
2208 		return ware.menge;
2209 	}
2210 	// add to internal storage
2211 	add_ware_to_halt(ware);
2212 
2213 	return ware.menge;
2214 }
2215 
2216 
2217 /**
2218  * @param buf the buffer to fill
2219  * @return Goods description text (buf)
2220  * @author Hj. Malthaner
2221  */
get_freight_info(cbuffer_t & buf)2222 void haltestelle_t::get_freight_info(cbuffer_t & buf)
2223 {
2224 	if(resort_freight_info) {
2225 		// resort only inf absolutely needed ...
2226 		resort_freight_info = false;
2227 		buf.clear();
2228 
2229 		for(unsigned i=0; i<goods_manager_t::get_max_catg_index(); i++) {
2230 			const vector_tpl<ware_t> * warray = cargo[i];
2231 			if(warray) {
2232 				freight_list_sorter_t::sort_freight(*warray, buf, (freight_list_sorter_t::sort_mode_t)sortierung, NULL, "waiting");
2233 			}
2234 		}
2235 	}
2236 }
2237 
2238 
2239 
get_short_freight_info(cbuffer_t & buf) const2240 void haltestelle_t::get_short_freight_info(cbuffer_t & buf) const
2241 {
2242 	bool got_one = false;
2243 
2244 	for(unsigned int i=0; i<goods_manager_t::get_count(); i++) {
2245 		const goods_desc_t *wtyp = goods_manager_t::get_info(i);
2246 		if(gibt_ab(wtyp)) {
2247 
2248 			// ignore goods with sum=zero
2249 			const int summe=get_ware_summe(wtyp);
2250 			if(summe>0) {
2251 
2252 				if(got_one) {
2253 					buf.append(", ");
2254 				}
2255 
2256 				buf.printf("%d%s %s", summe, translator::translate(wtyp->get_mass()), translator::translate(wtyp->get_name()));
2257 
2258 				got_one = true;
2259 			}
2260 		}
2261 	}
2262 
2263 	if(got_one) {
2264 		buf.append(" ");
2265 		buf.append(translator::translate("waiting"));
2266 		buf.append("\n");
2267 	}
2268 	else {
2269 		buf.append(translator::translate("no goods waiting"));
2270 		buf.append("\n");
2271 	}
2272 }
2273 
2274 
2275 
open_info_window()2276 void haltestelle_t::open_info_window()
2277 {
2278 	create_win( new halt_info_t(self), w_info, magic_halt_info + self.get_id() );
2279 }
2280 
2281 
calc_maintenance() const2282 sint64 haltestelle_t::calc_maintenance() const
2283 {
2284 	sint64 maintenance = 0;
2285 	FOR(  slist_tpl<tile_t>,  const& i,  tiles  ) {
2286 		if(  gebaeude_t* const gb = i.grund->find<gebaeude_t>()  ) {
2287 			maintenance += welt->get_settings().maint_building * gb->get_tile()->get_desc()->get_level();
2288 		}
2289 	}
2290 	return maintenance;
2291 }
2292 
2293 
2294 
2295 // changes this to a public transfer exchange stop
change_owner(player_t * player)2296 void haltestelle_t::change_owner( player_t *player )
2297 {
2298 	// check if already public
2299 	if(  owner == player  ) {
2300 		return;
2301 	}
2302 
2303 	// process every tile of stop
2304 	slist_tpl<halthandle_t> joining;
2305 	FOR(slist_tpl<tile_t>, const& i, tiles) {
2306 
2307 		grund_t* const gr = i.grund;
2308 		if(  gebaeude_t* gb = gr->find<gebaeude_t>()  ) {
2309 			// change ownership
2310 			player_t *gbplayer =gb->get_owner();
2311 			gb->set_owner(player);
2312 			gb->set_flag(obj_t::dirty);
2313 			sint64 const monthly_costs = welt->get_settings().maint_building * gb->get_tile()->get_desc()->get_level();
2314 			waytype_t const costs_type = gb->get_waytype();
2315 			player_t::add_maintenance(gbplayer, -monthly_costs, costs_type);
2316 			player_t::add_maintenance(player, monthly_costs, costs_type);
2317 
2318 			// cost is computed as cst_make_public_months
2319 			sint64 const cost = -welt->scale_with_month_length(monthly_costs * welt->get_settings().cst_make_public_months);
2320 			player_t::book_construction_costs(gbplayer, cost, get_basis_pos(), costs_type);
2321 			player_t::book_construction_costs(player, -cost, koord::invalid, costs_type);
2322 		}
2323 
2324 		// change way ownership
2325 		for(  int j=0;  j<2;  j++  ) {
2326 			if(  weg_t *w=gr->get_weg_nr(j)  ) {
2327 				// change ownership of way...
2328 				player_t *wplayer = w->get_owner();
2329 				if(  player!=wplayer  ) {
2330 					w->set_owner( welt->get_public_player() );
2331 					w->set_flag(obj_t::dirty);
2332 					sint32 cost = w->get_desc()->get_maintenance();
2333 					// of tunnel...
2334 					if(  tunnel_t *t=gr->find<tunnel_t>()  ) {
2335 						t->set_owner( welt->get_public_player() );
2336 						t->set_flag(obj_t::dirty);
2337 						cost = t->get_desc()->get_maintenance();
2338 					}
2339 					waytype_t const financetype = w->get_desc()->get_finance_waytype();
2340 					player_t::add_maintenance( wplayer, -cost, financetype);
2341 					player_t::add_maintenance( welt->get_public_player(), cost, financetype);
2342 					// multiplayer notification message
2343 					if(  player != welt->get_public_player()  &&  env_t::networkmode  ) {
2344 						cbuffer_t buf;
2345 						buf.printf( translator::translate("(%s) now public way."), w->get_pos().get_str() );
2346 						welt->get_message()->add_message( buf, w->get_pos().get_2d(), message_t::ai, PLAYER_FLAG|player->get_player_nr(), IMG_EMPTY );
2347 					}
2348 					cost = -welt->scale_with_month_length(cost * welt->get_settings().cst_make_public_months);
2349 					player_t::book_construction_costs(wplayer, cost, koord::invalid, financetype);
2350 				}
2351 			}
2352 		}
2353 
2354 		// make way object public if any suitable
2355 		for(  uint8 i = 1;  i < gr->get_top();  i++  ) {
2356 			if(  wayobj_t *const wo = obj_cast<wayobj_t>(gr->obj_bei(i))  ) {
2357 				player_t *woplayer = wo->get_owner();
2358 				if(  player!=woplayer  ) {
2359 					sint32 const cost = wo->get_desc()->get_maintenance();
2360 					// change ownership
2361 					wo->set_owner( player );
2362 					wo->set_flag(obj_t::dirty);
2363 					waytype_t const financetype = wo->get_desc()->get_waytype();
2364 					player_t::add_maintenance( woplayer, -cost, financetype);
2365 					player_t::add_maintenance( player, cost, financetype);
2366 					player_t::book_construction_costs( woplayer, cost, koord::invalid, financetype);
2367 				}
2368 			}
2369 		}
2370 	}
2371 
2372 	// now finally change owner
2373 	owner = player;
2374 	rebuild_connections();
2375 	rebuild_linked_connections();
2376 
2377 	// tell the world of it ...
2378 	if(  player == welt->get_public_player()  &&  env_t::networkmode  ) {
2379 		cbuffer_t buf;
2380 		buf.printf( translator::translate("%s at (%i,%i) now public stop."), get_name(), get_basis_pos().x, get_basis_pos().y );
2381 		welt->get_message()->add_message( buf, get_basis_pos(), message_t::ai, PLAYER_FLAG|player->get_player_nr(), IMG_EMPTY );
2382 	}
2383 }
2384 
2385 
2386 // merge stop
merge_halt(halthandle_t halt_merged)2387 void haltestelle_t::merge_halt( halthandle_t halt_merged )
2388 {
2389 	if(  halt_merged ==  self  ||  !halt_merged.is_bound()  ) {
2390 		return;
2391 	}
2392 
2393 	halt_merged->change_owner( owner );
2394 
2395 	// add statistics
2396 	for(  int month=0;  month<MAX_MONTHS;  month++  ) {
2397 		for(  int type=0;  type<MAX_HALT_COST;  type++  ) {
2398 			financial_history[month][type] += halt_merged->financial_history[month][type];
2399 			halt_merged->financial_history[month][type] = 0;
2400 		}
2401 	}
2402 
2403 	slist_tpl<tile_t> tiles_to_join;
2404 	FOR(slist_tpl<tile_t>, const& i, halt_merged->get_tiles()) {
2405 		tiles_to_join.append(i);
2406 	}
2407 
2408 	while(  !tiles_to_join.empty()  ) {
2409 
2410 		// ATTENTION: Anz convoi reservation to this tile will be lost!
2411 		grund_t *gr = tiles_to_join.remove_first().grund;
2412 
2413 		// transfer tiles to us
2414 		halt_merged->rem_grund(gr);
2415 		add_grund(gr);
2416 	}
2417 
2418 	assert(!halt_merged->existiert_in_welt());
2419 
2420 	// transfer goods
2421 	halt_merged->transfer_goods(self);
2422 	destroy(halt_merged);
2423 
2424 	recalc_basis_pos();
2425 
2426 	// also rebuild our connections
2427 	recalc_station_type();
2428 	rebuild_connections();
2429 	rebuild_linked_connections();
2430 }
2431 
2432 
transfer_goods(halthandle_t halt)2433 void haltestelle_t::transfer_goods(halthandle_t halt)
2434 {
2435 	if (!self.is_bound() || !halt.is_bound()) {
2436 		return;
2437 	}
2438 	// transfer goods to halt
2439 	for(uint8 i=0; i<goods_manager_t::get_max_catg_index(); i++) {
2440 		const vector_tpl<ware_t> * warray = cargo[i];
2441 		if (warray) {
2442 			FOR(vector_tpl<ware_t>, const& j, *warray) {
2443 				halt->add_ware_to_halt(j);
2444 			}
2445 			delete cargo[i];
2446 			cargo[i] = NULL;
2447 		}
2448 	}
2449 }
2450 
2451 
2452 // private helper function for recalc_station_type()
add_to_station_type(grund_t * gr)2453 void haltestelle_t::add_to_station_type( grund_t *gr )
2454 {
2455 	// init in any case ...
2456 	if(  tiles.empty()  ) {
2457 		capacity[0] = 0;
2458 		capacity[1] = 0;
2459 		capacity[2] = 0;
2460 		enables &= CROWDED;	// clear flags
2461 		station_type = invalid;
2462 	}
2463 
2464 	const gebaeude_t* gb = gr->find<gebaeude_t>();
2465 	const building_desc_t *desc=gb?gb->get_tile()->get_desc():NULL;
2466 
2467 	if(  gr->is_water()  &&  gb  ) {
2468 		// may happen around oil rigs and so on
2469 		station_type |= dock;
2470 		// for water factories
2471 		if(desc) {
2472 			// enabled the matching types
2473 			enables |= desc->get_enabled();
2474 			if (welt->get_settings().is_separate_halt_capacities()) {
2475 				if(desc->get_enabled()&1) {
2476 					capacity[0] += desc->get_capacity();
2477 				}
2478 				if(desc->get_enabled()&2) {
2479 					capacity[1] += desc->get_capacity();
2480 				}
2481 				if(desc->get_enabled()&4) {
2482 					capacity[2] += desc->get_capacity();
2483 				}
2484 			}
2485 			else {
2486 				// no separate capacities: sum up all
2487 				capacity[0] += desc->get_capacity();
2488 				capacity[2] = capacity[1] = capacity[0];
2489 			}
2490 		}
2491 		return;
2492 	}
2493 
2494 	if(  desc==NULL  ) {
2495 		// no desc, but solid ground?!?
2496 		dbg->error("haltestelle_t::get_station_type()","ground belongs to halt but no desc?");
2497 		return;
2498 	}
2499 
2500 	// there is only one loading bay ...
2501 	switch (desc->get_type()) {
2502 		case building_desc_t::dock:
2503 		case building_desc_t::flat_dock: station_type |= dock;         break;
2504 
2505 		// two ways on ground can only happen for tram tracks on streets, there buses and trams can stop
2506 		case building_desc_t::generic_stop:
2507 			switch (desc->get_extra()) {
2508 				case road_wt:
2509 					station_type |= (desc->get_enabled()&3)!=0 ? busstop : loadingbay;
2510 					if (gr->has_two_ways()) { // tram track on street
2511 						station_type |= tramstop;
2512 					}
2513 					break;
2514 				case water_wt:       station_type |= dock;            break;
2515 				case air_wt:         station_type |= airstop;         break;
2516 				case monorail_wt:    station_type |= monorailstop;    break;
2517 				case track_wt:       station_type |= railstation;     break;
2518 				case tram_wt:
2519 					station_type |= tramstop;
2520 					if (gr->has_two_ways()) { // tram track on street
2521 						station_type |= (desc->get_enabled()&3)!=0 ? busstop : loadingbay;
2522 					}
2523 					break;
2524 				case maglev_wt:      station_type |= maglevstop;      break;
2525 				case narrowgauge_wt: station_type |= narrowgaugestop; break;
2526 				default: ;
2527 			}
2528 			break;
2529 		default: ;
2530 	}
2531 
2532 	// enabled the matching types
2533 	enables |= desc->get_enabled();
2534 	if (welt->get_settings().is_separate_halt_capacities()) {
2535 		if(desc->get_enabled()&1) {
2536 			capacity[0] += desc->get_capacity();
2537 		}
2538 		if(desc->get_enabled()&2) {
2539 			capacity[1] += desc->get_capacity();
2540 		}
2541 		if(desc->get_enabled()&4) {
2542 			capacity[2] += desc->get_capacity();
2543 		}
2544 	}
2545 	else {
2546 		// no separate capacities: sum up all
2547 		capacity[0] += desc->get_capacity();
2548 		capacity[2] = capacity[1] = capacity[0];
2549 	}
2550 }
2551 
2552 /*
2553  * recalculated the station type(s)
2554  * since it iterates over all ground, this is better not done too often, because line management and station list
2555  * queries this information regularly; Thus, we do this, when adding new ground
2556  * This recalculates also the capacity from the building levels ...
2557  * @author Weber/prissi
2558  */
recalc_station_type()2559 void haltestelle_t::recalc_station_type()
2560 {
2561 	capacity[0] = 0;
2562 	capacity[1] = 0;
2563 	capacity[2] = 0;
2564 	enables &= CROWDED;	// clear flags
2565 	station_type = invalid;
2566 
2567 	// iterate over all tiles
2568 	FOR(slist_tpl<tile_t>, const& i, tiles) {
2569 		grund_t* const gr = i.grund;
2570 		add_to_station_type( gr );
2571 	}
2572 	// and set halt info again
2573 	FOR(slist_tpl<tile_t>, const& i, tiles) {
2574 		i.grund->set_halt( self );
2575 	}
2576 	recalc_status();
2577 }
2578 
2579 
2580 
generate_pedestrians(koord3d pos,int count)2581 int haltestelle_t::generate_pedestrians(koord3d pos, int count)
2582 {
2583 	pedestrian_t::generate_pedestrians_at(pos, count);
2584 	for(int i=0; i<4 && count>0; i++) {
2585 		pedestrian_t::generate_pedestrians_at(pos+koord::nsew[i], count);
2586 	}
2587 	return count;
2588 }
2589 
2590 // necessary to load pre0.99.13 savegames
warenziel_rdwr(loadsave_t * file)2591 void warenziel_rdwr(loadsave_t *file)
2592 {
2593 	koord ziel;
2594 	ziel.rdwr(file);
2595 	char tn[256];
2596 	file->rdwr_str(tn, lengthof(tn));
2597 }
2598 
2599 
rdwr(loadsave_t * file)2600 void haltestelle_t::rdwr(loadsave_t *file)
2601 {
2602 	xml_tag_t h( file, "haltestelle_t" );
2603 
2604 	sint32 owner_n;
2605 	koord3d k;
2606 
2607 	// will restore halthandle_t after loading
2608 	if(file->is_version_atleast(110, 6)) {
2609 		if(file->is_saving()) {
2610 			uint16 halt_id = self.is_bound() ? self.get_id() : 0;
2611 			file->rdwr_short(halt_id);
2612 		}
2613 		else {
2614 			uint16 halt_id;
2615 			file->rdwr_short(halt_id);
2616 			self.set_id(halt_id);
2617 			self = halthandle_t(this, halt_id);
2618 		}
2619 	}
2620 	else {
2621 		if (file->is_loading()) {
2622 			self = halthandle_t(this);
2623 		}
2624 	}
2625 
2626 	if(file->is_saving()) {
2627 		owner_n = welt->sp2num( owner );
2628 	}
2629 
2630 	if(file->is_version_less(99, 8)) {
2631 		init_pos.rdwr( file );
2632 	}
2633 	file->rdwr_long(owner_n);
2634 
2635 	if(file->is_version_less(88, 6)) {
2636 		bool dummy;
2637 		file->rdwr_bool(dummy); // pax
2638 		file->rdwr_bool(dummy); // mail
2639 		file->rdwr_bool(dummy);	// ware
2640 	}
2641 
2642 	if(file->is_loading()) {
2643 		owner = welt->get_player(owner_n);
2644 		k.rdwr( file );
2645 		while(k!=koord3d::invalid) {
2646 			grund_t *gr = welt->lookup(k);
2647 			if(!gr) {
2648 				dbg->error("haltestelle_t::rdwr()", "invalid position %s", k.get_str() );
2649 				gr = welt->lookup_kartenboden(k.get_2d());
2650 				dbg->error("haltestelle_t::rdwr()", "setting to %s", gr->get_pos().get_str() );
2651 			}
2652 			// during loading and saving halts will be referred by their base position
2653 			// so we may already be defined ...
2654 			if(gr->get_halt().is_bound()) {
2655 				dbg->warning( "haltestelle_t::rdwr()", "bound to ground twice at (%i,%i)!", k.x, k.y );
2656 			}
2657 			// prissi: now check, if there is a building -> we allow no longer ground without building!
2658 			const gebaeude_t* gb = gr->find<gebaeude_t>();
2659 			const building_desc_t *desc=gb?gb->get_tile()->get_desc():NULL;
2660 			if(desc) {
2661 				add_grund( gr, false /*do not relink factories now*/ );
2662 				// verbinde_fabriken will be called in finish_rd
2663 			}
2664 			else {
2665 				dbg->warning("haltestelle_t::rdwr()", "will no longer add ground without building at %s!", k.get_str() );
2666 			}
2667 			k.rdwr( file );
2668 		}
2669 	}
2670 	else {
2671 		FOR(slist_tpl<tile_t>, const& i, tiles) {
2672 			k = i.grund->get_pos();
2673 			k.rdwr( file );
2674 		}
2675 		k = koord3d::invalid;
2676 		k.rdwr( file );
2677 	}
2678 
2679 	init_pos = tiles.empty() ? koord::invalid : tiles.front().grund->get_pos().get_2d();
2680 	if(file->is_saving()) {
2681 		const char *s;
2682 		for(unsigned i=0; i<goods_manager_t::get_max_catg_index(); i++) {
2683 			vector_tpl<ware_t> *warray = cargo[i];
2684 			if(warray) {
2685 				s = "y";	// needs to be non-empty
2686 				file->rdwr_str(s);
2687 				if(  file->is_version_less(112, 3)  ) {
2688 					uint16 count = warray->get_count();
2689 					file->rdwr_short(count);
2690 				}
2691 				else {
2692 					uint32 count = warray->get_count();
2693 					file->rdwr_long(count);
2694 				}
2695 				FOR(vector_tpl<ware_t>, & ware, *warray) {
2696 					ware.rdwr(file);
2697 				}
2698 			}
2699 		}
2700 		s = "";
2701 		file->rdwr_str(s);
2702 	}
2703 	else {
2704 		// restoring all goods in the station
2705 		char s[256];
2706 		file->rdwr_str(s, lengthof(s));
2707 		while(*s) {
2708 			uint32 count;
2709 			if(  file->is_version_less(112, 3)  ) {
2710 				uint16 scount;
2711 				file->rdwr_short(scount);
2712 				count = scount;
2713 			}
2714 			else {
2715 				file->rdwr_long(count);
2716 			}
2717 			if(count>0) {
2718 				for(  uint32 i = 0;  i < count;  i++  ) {
2719 					// add to internal storage (use this function, since the old categories were different)
2720 					ware_t ware(file);
2721 					if(  ware.get_desc()  &&  ware.menge>0  &&  welt->is_within_limits(ware.get_zielpos())  ) {
2722 						add_ware_to_halt(ware);
2723 						// restore in-transit information
2724 						fabrik_t::update_transit( &ware, true );
2725 					}
2726 					else if(  ware.menge>0  ) {
2727 						if(  ware.get_desc()  ) {
2728 							dbg->error( "haltestelle_t::rdwr()", "%i of %s to %s ignored!", ware.menge, ware.get_name(), ware.get_zielpos().get_str() );
2729 						}
2730 						else {
2731 							dbg->error( "haltestelle_t::rdwr()", "%i of unknown to %s ignored!", ware.menge, ware.get_zielpos().get_str() );
2732 						}
2733 					}
2734 				}
2735 			}
2736 			file->rdwr_str(s, lengthof(s));
2737 		}
2738 
2739 		// old games save the list with stations
2740 		// however, we have to rebuilt them anyway for the new format
2741 		if(file->is_version_less(99, 13)) {
2742 			uint16 count;
2743 			file->rdwr_short(count);
2744 			for(int i=0; i<count; i++) {
2745 				warenziel_rdwr(file);
2746 			}
2747 		}
2748 
2749 	}
2750 
2751 	if(  file->is_version_atleast(111, 1)  ) {
2752 		for (int j = 0; j<MAX_HALT_COST; j++) {
2753 			for (size_t k = MAX_MONTHS; k-- != 0;) {
2754 				file->rdwr_longlong(financial_history[k][j]);
2755 			}
2756 		}
2757 	}
2758 	else {
2759 		// old history did not know about walked pax
2760 		for (int j = 0; j<7; j++) {
2761 			for (size_t k = MAX_MONTHS; k-- != 0;) {
2762 				file->rdwr_longlong(financial_history[k][j]);
2763 			}
2764 		}
2765 		for (size_t k = MAX_MONTHS; k-- != 0;) {
2766 			financial_history[k][HALT_WALKED] = 0;
2767 		}
2768 	}
2769 }
2770 
2771 
2772 
finish_rd()2773 void haltestelle_t::finish_rd()
2774 {
2775 	verbinde_fabriken();
2776 
2777 	stale_convois.clear();
2778 	stale_lines.clear();
2779 	// fix good destination coordinates
2780 	for(unsigned i=0; i<goods_manager_t::get_max_catg_index(); i++) {
2781 		if(cargo[i]) {
2782 			vector_tpl<ware_t> * warray = cargo[i];
2783 			FOR(vector_tpl<ware_t>, & j, *warray) {
2784 				j.finish_rd(welt);
2785 			}
2786 			// merge identical entries (should only happen with old games)
2787 			for(unsigned j=0; j<warray->get_count(); j++) {
2788 				if(  (*warray)[j].menge==0  ) {
2789 					continue;
2790 				}
2791 				for(unsigned k=j+1; k<warray->get_count(); k++) {
2792 					if(  (*warray)[k].menge>0  &&  (*warray)[j].same_destination( (*warray)[k] )  ) {
2793 						(*warray)[j].menge += (*warray)[k].menge;
2794 						(*warray)[k].menge = 0;
2795 					}
2796 				}
2797 			}
2798 		}
2799 	}
2800 
2801 	// what kind of station here?
2802 	recalc_station_type();
2803 
2804 	// handle name for old stations which don't exist in kartenboden
2805 	grund_t* bd = welt->lookup(get_basis_pos3d());
2806 	if(bd!=NULL  &&  !bd->get_flag(grund_t::has_text) ) {
2807 		// restore label and bridges
2808 		grund_t* bd_old = welt->lookup_kartenboden(get_basis_pos());
2809 		if(bd_old) {
2810 			// transfer name (if there)
2811 			const char *name = bd->get_text();
2812 			if(name) {
2813 				set_name( name );
2814 				bd_old->set_text( NULL );
2815 			}
2816 			else {
2817 				set_name( "Unknown" );
2818 			}
2819 		}
2820 	}
2821 	else {
2822 		const char *current_name = bd->get_text();
2823 		if(  all_names.get(current_name).is_bound()  &&  fabrik_t::get_fab(get_basis_pos())==NULL  ) {
2824 			// try to get a new name ...
2825 			const char *new_name;
2826 			if(  station_type & airstop  ) {
2827 				new_name = create_name( get_basis_pos(), "Airport" );
2828 			}
2829 			else if(  station_type & dock  ) {
2830 				new_name = create_name( get_basis_pos(), "Dock" );
2831 			}
2832 			else if(  station_type & (railstation|monorailstop|maglevstop|narrowgaugestop)  ) {
2833 				new_name = create_name( get_basis_pos(), "BF" );
2834 			}
2835 			else {
2836 				new_name = create_name( get_basis_pos(), "H" );
2837 			}
2838 			dbg->warning("haltestelle_t::set_name()","name already used: \'%s\' -> \'%s\'", current_name, new_name );
2839 			bd->set_text( new_name );
2840 			current_name = new_name;
2841 		}
2842 		all_names.set( current_name, self );
2843 	}
2844 	recalc_status();
2845 	reconnect_counter = welt->get_schedule_counter()-1;
2846 }
2847 
2848 
2849 
book(sint64 amount,int cost_type)2850 void haltestelle_t::book(sint64 amount, int cost_type)
2851 {
2852 	assert(cost_type <= MAX_HALT_COST);
2853 	financial_history[0][cost_type] += amount;
2854 }
2855 
2856 
2857 
init_financial_history()2858 void haltestelle_t::init_financial_history()
2859 {
2860 	for (int j = 0; j<MAX_HALT_COST; j++)
2861 	{
2862 		for (size_t k = MAX_MONTHS; k-- != 0;) {
2863 			financial_history[k][j] = 0;
2864 		}
2865 	}
2866 }
2867 
2868 
2869 
2870 /**
2871  * Calculates a status color for status bars
2872  * @author Hj. Malthaner
2873  */
recalc_status()2874 void haltestelle_t::recalc_status()
2875 {
2876 	status_color = color_idx_to_rgb(financial_history[0][HALT_CONVOIS_ARRIVED] > 0 ? COL_GREEN : COL_YELLOW);
2877 
2878 	// since the status is ordered ...
2879 	uint8 status_bits = 0;
2880 
2881 	MEMZERO(overcrowded);
2882 
2883 	uint64 total_sum = 0;
2884 	if(get_pax_enabled()) {
2885 		const uint32 max_ware = get_capacity(0);
2886 		total_sum += get_ware_summe(goods_manager_t::passengers);
2887 		if(total_sum>max_ware) {
2888 			overcrowded[0] |= 1;
2889 		}
2890 		if(get_pax_unhappy() > 40 ) {
2891 			status_bits = (total_sum>max_ware+200 || (total_sum>max_ware  &&  get_pax_unhappy()>200)) ? 2 : 1;
2892 		}
2893 		else if(total_sum>max_ware) {
2894 			status_bits = total_sum>max_ware+200 ? 2 : 1;
2895 		}
2896 	}
2897 
2898 	if(get_mail_enabled()) {
2899 		const uint32 max_ware = get_capacity(1);
2900 		const uint32 mail = get_ware_summe(goods_manager_t::mail);
2901 		total_sum += mail;
2902 		if(mail>max_ware) {
2903 			status_bits |= mail>max_ware+200 ? 2 : 1;
2904 			overcrowded[0] |= 2;
2905 		}
2906 	}
2907 
2908 	// now for all goods
2909 	if(status_color!=color_idx_to_rgb(COL_RED)  &&  get_ware_enabled()) {
2910 		const uint8  count = goods_manager_t::get_count();
2911 		const uint32 max_ware = get_capacity(2);
2912 		for(  uint32 i = 3;  i < count;  i++  ) {
2913 			goods_desc_t const* const wtyp = goods_manager_t::get_info(i);
2914 			const uint32 ware_sum = get_ware_summe(wtyp);
2915 			total_sum += ware_sum;
2916 			if(ware_sum>max_ware) {
2917 				status_bits |= ware_sum > max_ware + 32 /*|| enables & CROWDED*/ ? 2 : 1; // for now report only serious overcrowding on transfer stops
2918 				overcrowded[wtyp->get_index()/8] |= 1<<(wtyp->get_index()%8);
2919 			}
2920 		}
2921 	}
2922 
2923 	// take the worst color for status
2924 	if(  status_bits  ) {
2925 		status_color = color_idx_to_rgb(status_bits&2 ? COL_RED : COL_ORANGE);
2926 	}
2927 	else {
2928 		status_color = color_idx_to_rgb((financial_history[0][HALT_WAITING]+financial_history[0][HALT_DEPARTED] == 0) ? COL_YELLOW : COL_GREEN);
2929 	}
2930 
2931 	financial_history[0][HALT_WAITING] = total_sum;
2932 }
2933 
2934 
2935 
2936 /**
2937  * Draws some nice colored bars giving some status information
2938  * @author Hj. Malthaner
2939  */
display_status(KOORD_VAL xpos,KOORD_VAL ypos)2940 void haltestelle_t::display_status(KOORD_VAL xpos, KOORD_VAL ypos)
2941 {
2942 	// ignore freight that cannot reach to this station
2943 	sint16 count = 0;
2944 	for(  uint16 i = 0;  i < goods_manager_t::get_count();  i++  ) {
2945 		if(  i == 2  ) {
2946 			continue; // ignore freight none
2947 		}
2948 		if(  gibt_ab( goods_manager_t::get_info(i) )  ) {
2949 			count++;
2950 		}
2951 	}
2952 	if(  count != last_bar_count  ) {
2953 		// bars will shift x positions, mark entire station bar region dirty
2954 		KOORD_VAL max_bar_height = 0;
2955 		for(  sint16 i = 0;  i < last_bar_count;  i++  ) {
2956 			if(  last_bar_height[i] > max_bar_height  ) {
2957 				max_bar_height = last_bar_height[i];
2958 			}
2959 		}
2960 		const KOORD_VAL x = xpos - (last_bar_count * 4 - get_tile_raster_width()) / 2;
2961 		mark_rect_dirty_wc( x - 1 - 4, ypos - 11 - max_bar_height - 6, x + last_bar_count * 4 + 12 - 2, ypos - 11 );
2962 
2963 		// reset bar heights for new count
2964 		last_bar_height.clear();
2965 		last_bar_height.resize( count );
2966 		for(  sint16 i = 0;  i < count;  i++  ) {
2967 			last_bar_height.append(0);
2968 		}
2969 		last_bar_count = count;
2970 	}
2971 
2972 	ypos -= 11;
2973 	xpos -= (count * 4 - get_tile_raster_width()) / 2;
2974 	const KOORD_VAL x = xpos;
2975 
2976 	sint16 bar_height_index = 0;
2977 	uint32 max_capacity;
2978 	for(  uint8 i = 0;  i < goods_manager_t::get_count();  i++  ) {
2979 		if(  i == 2  ) {
2980 			continue; // ignore freight none
2981 		}
2982 		const goods_desc_t *wtyp = goods_manager_t::get_info(i);
2983 		if(  gibt_ab( wtyp )  ) {
2984 			if(  i < 2  ) {
2985 				max_capacity = get_capacity(i);
2986 			}
2987 			else {
2988 				max_capacity = get_capacity(2);
2989 			}
2990 			const uint32 sum = get_ware_summe( wtyp );
2991 			uint32 v = min( sum, max_capacity );
2992 			if(  max_capacity > 512  ) {
2993 				v = 2 + (v * 128) / max_capacity;
2994 			}
2995 			else {
2996 				v = (v / 4) + 2;
2997 			}
2998 
2999 			display_fillbox_wh_clip_rgb( xpos, ypos - v - 1, 1, v, color_idx_to_rgb(COL_GREY4), false);
3000 			display_fillbox_wh_clip_rgb( xpos + 1, ypos - v - 1, 2, v, wtyp->get_color(), false);
3001 			display_fillbox_wh_clip_rgb( xpos + 3, ypos - v - 1, 1, v, color_idx_to_rgb(COL_GREY1), false);
3002 
3003 			// Hajo: show up arrow for capped values
3004 			if(  sum > max_capacity  ) {
3005 				display_fillbox_wh_clip_rgb( xpos + 1, ypos - v - 6, 2, 4, color_idx_to_rgb(COL_WHITE), false);
3006 				display_fillbox_wh_clip_rgb( xpos, ypos - v - 5, 4, 1, color_idx_to_rgb(COL_WHITE), false);
3007 				v += 5; // for marking dirty
3008 			}
3009 
3010 			if(  last_bar_height[bar_height_index] != (KOORD_VAL)v  ) {
3011 				if(  (KOORD_VAL)v > last_bar_height[bar_height_index]  ) {
3012 					// bar will be longer, mark new height dirty
3013 					mark_rect_dirty_wc( xpos, ypos - v - 1, xpos + 3, ypos - 1);
3014 				}
3015 				else {
3016 					// bar will be shorter, mark old height dirty
3017 					mark_rect_dirty_wc( xpos, ypos - last_bar_height[bar_height_index] - 1, xpos + 3, ypos - 1);
3018 				}
3019 				last_bar_height[bar_height_index] = v;
3020 			}
3021 
3022 			bar_height_index++;
3023 			xpos += 4;
3024 		}
3025 	}
3026 
3027 	// status color box below
3028 	bool dirty = false;
3029 	if(  get_status_farbe() != last_status_color  ) {
3030 		last_status_color = get_status_farbe();
3031 		dirty = true;
3032 	}
3033 	display_fillbox_wh_clip_rgb( x - 1 - 4, ypos, count * 4 + 12 - 2, 4, get_status_farbe(), dirty );
3034 }
3035 
3036 
3037 
add_grund(grund_t * gr,bool relink_factories)3038 bool haltestelle_t::add_grund(grund_t *gr, bool relink_factories)
3039 {
3040 	assert(gr!=NULL);
3041 
3042 	// new halt?
3043 	if(  tiles.is_contained(gr)  ) {
3044 		return false;
3045 	}
3046 
3047 	koord pos = gr->get_pos().get_2d();
3048 	add_to_station_type( gr );
3049 	gr->set_halt( self );
3050 	tiles.append( gr );
3051 
3052 	// add to hashtable
3053 	if (all_koords) {
3054 		sint32 n = get_halt_key( gr->get_pos(), welt->get_size().y );
3055 		all_koords->set( n, self );
3056 	}
3057 
3058 	// appends this to the ground
3059 	// after that, the surrounding ground will know of this station
3060 	bool insert_unsorted = !relink_factories;
3061 	uint16 const cov = welt->get_settings().get_station_coverage();
3062 	for (int y = -cov; y <= cov; y++) {
3063 		for (int x = -cov; x <= cov; x++) {
3064 			koord p=pos+koord(x,y);
3065 			planquadrat_t *plan = welt->access(p);
3066 			if(plan) {
3067 				plan->add_to_haltlist( self, insert_unsorted);
3068 				plan->get_kartenboden()->set_flag(grund_t::dirty);
3069 			}
3070 		}
3071 	}
3072 
3073 	// since suddenly other factories may be connect to us too
3074 	if (relink_factories) {
3075 		verbinde_fabriken();
3076 	}
3077 
3078 	// check if we have to register line(s) and/or lineless convoy(s) which serve this halt
3079 	vector_tpl<linehandle_t> check_line(0);
3080 
3081 	// public halt: must iterate over all players lines / convoys
3082 	bool public_halt = get_owner() == welt->get_public_player();
3083 
3084 	uint8 const pl_min = public_halt ? 0                : get_owner()->get_player_nr();
3085 	uint8 const pl_max = public_halt ? MAX_PLAYER_COUNT : get_owner()->get_player_nr()+1;
3086 	// iterate over all lines (public halt: all lines, other: only player's lines)
3087 	for(  uint8 i=pl_min;  i<pl_max;  i++  ) {
3088 		if(  player_t *player = welt->get_player(i)  ) {
3089 			player->simlinemgmt.get_lines(simline_t::line, &check_line);
3090 			FOR(  vector_tpl<linehandle_t>, const j, check_line  ) {
3091 				// only add unknown lines
3092 				if(  !registered_lines.is_contained(j)  &&  j->count_convoys() > 0  ) {
3093 					FOR(  minivec_tpl<schedule_entry_t>, const& k, j->get_schedule()->entries  ) {
3094 						if(  get_halt(k.pos, player) == self  ) {
3095 							registered_lines.append(j);
3096 							break;
3097 						}
3098 					}
3099 				}
3100 			}
3101 		}
3102 	}
3103 	// Knightly : iterate over all convoys
3104 	FOR(vector_tpl<convoihandle_t>, const cnv, welt->convoys()) {
3105 		// only check lineless convoys which have matching ownership and which are not yet registered
3106 		if(  !cnv->get_line().is_bound()  &&  (public_halt  ||  cnv->get_owner()==get_owner())  &&  !registered_convoys.is_contained(cnv)  ) {
3107 			if(  const schedule_t *const schedule = cnv->get_schedule()  ) {
3108 				FOR(minivec_tpl<schedule_entry_t>, const& k, schedule->entries) {
3109 					if (get_halt(k.pos, cnv->get_owner()) == self) {
3110 						registered_convoys.append(cnv);
3111 						break;
3112 					}
3113 				}
3114 			}
3115 		}
3116 	}
3117 
3118 	// This entire loop is just for the assertion below.
3119 	// Consider deleting the assertion --neroden
3120 	bool grund_is_where_it_should_be = false;
3121 	const planquadrat_t* plan = welt->access(pos);
3122 	for(  uint8 i=0;  i < plan->get_boden_count();  i++  ) {
3123 		const grund_t* found_gr = plan->get_boden_bei(i);
3124 		if (found_gr == gr) {
3125 			grund_is_where_it_should_be = true;
3126 			break;
3127 		}
3128 	}
3129 	if (  !grund_is_where_it_should_be || gr->get_halt() != self || !gr->is_halt()  ) {
3130 		dbg->error( "haltestelle_t::add_grund()", "no ground added to (%s)", gr->get_pos().get_str() );
3131 	}
3132 	init_pos = tiles.front().grund->get_pos().get_2d();
3133 	welt->set_schedule_counter();
3134 
3135 	return true;
3136 }
3137 
3138 
3139 
rem_grund(grund_t * gr)3140 bool haltestelle_t::rem_grund(grund_t *gr)
3141 {
3142 	// namen merken
3143 	if(!gr) {
3144 		return false;
3145 	}
3146 
3147 	slist_tpl<tile_t>::iterator i = std::find(tiles.begin(), tiles.end(), gr);
3148 	if (i == tiles.end()) {
3149 		// was not part of station => do nothing
3150 		dbg->error("haltestelle_t::rem_grund()","removed illegal ground from halt");
3151 		return false;
3152 	}
3153 
3154 	// first tile => remove name from this tile ...
3155 	char buf[256];
3156 	const char* station_name_to_transfer = NULL;
3157 	if (i == tiles.begin() && i->grund->get_name()) {
3158 		tstrncpy(buf, get_name(), lengthof(buf));
3159 		station_name_to_transfer = buf;
3160 		set_name(NULL);
3161 	}
3162 
3163 	// now remove tile from list
3164 	tiles.erase(i);
3165 	welt->set_schedule_counter();
3166 	init_pos = tiles.empty() ? koord::invalid : tiles.front().grund->get_pos().get_2d();
3167 
3168 	// re-add name
3169 	if (station_name_to_transfer != NULL  &&  !tiles.empty()) {
3170 		label_t *lb = tiles.front().grund->find<label_t>();
3171 		delete lb;
3172 		set_name( station_name_to_transfer );
3173 	}
3174 
3175 	bool remove_halt = true;
3176 	planquadrat_t *pl = welt->access( gr->get_pos().get_2d() );
3177 	if(pl) {
3178 		// no longer present on this level
3179 		gr->set_halt(halthandle_t());
3180 		// still connected elsewhere?
3181 		for(unsigned i=0;  i<pl->get_boden_count();  i++  ) {
3182 			if(pl->get_boden_bei(i)->get_halt()==self) {
3183 				// still connected with other ground => do not remove from plan ...
3184 				remove_halt = false;
3185 				break;
3186 			}
3187 		}
3188 	}
3189 
3190 	if (remove_halt) {
3191 		// otherwise remove from plan ...
3192 		if (pl) {
3193 			pl->get_kartenboden()->set_flag(grund_t::dirty);
3194 		}
3195 
3196 		uint16 const cov = welt->get_settings().get_station_coverage();
3197 		for (int y = -cov; y <= cov; y++) {
3198 			for (int x = -cov; x <= cov; x++) {
3199 				planquadrat_t *pl = welt->access( gr->get_pos().get_2d()+koord(x,y) );
3200 				if(pl) {
3201 					pl->remove_from_haltlist(self);
3202 					pl->get_kartenboden()->set_flag(grund_t::dirty);
3203 				}
3204 			}
3205 		}
3206 
3207 		// factory reach may have been changed ...
3208 		verbinde_fabriken();
3209 	}
3210 
3211 	// needs to be done, if this was a dock
3212 	recalc_station_type();
3213 
3214 	// remove lines eventually
3215 	for(  size_t j = registered_lines.get_count();  j-- != 0;  ) {
3216 		bool ok = false;
3217 		FOR(  minivec_tpl<schedule_entry_t>, const& k, registered_lines[j]->get_schedule()->entries  ) {
3218 			if(  get_halt(k.pos, registered_lines[j]->get_owner()) == self  ) {
3219 				ok = true;
3220 				break;
3221 			}
3222 		}
3223 		// need removal?
3224 		if(!ok) {
3225 			stale_lines.append_unique( registered_lines[j] );
3226 			registered_lines.remove_at(j);
3227 		}
3228 	}
3229 
3230 	// Knightly : remove registered lineless convoys as well
3231 	for(  size_t j = registered_convoys.get_count();  j-- != 0;  ) {
3232 		bool ok = false;
3233 		FOR(  minivec_tpl<schedule_entry_t>, const& k, registered_convoys[j]->get_schedule()->entries  ) {
3234 			if(  get_halt(k.pos, registered_convoys[j]->get_owner()) == self  ) {
3235 				ok = true;
3236 				break;
3237 			}
3238 		}
3239 		// need removal?
3240 		if(  !ok  ) {
3241 			stale_convois.append_unique( registered_convoys[j] );
3242 			registered_convoys.remove_at(j);
3243 		}
3244 	}
3245 
3246 	return true;
3247 }
3248 
3249 
3250 
existiert_in_welt() const3251 bool haltestelle_t::existiert_in_welt() const
3252 {
3253 	return !tiles.empty();
3254 }
3255 
3256 
3257 /* marks a coverage area
3258  * @author prissi
3259  */
mark_unmark_coverage(const bool mark) const3260 void haltestelle_t::mark_unmark_coverage(const bool mark) const
3261 {
3262 	// iterate over all tiles
3263 	uint16 const cov = welt->get_settings().get_station_coverage();
3264 	koord  const size(cov * 2 + 1, cov * 2 + 1);
3265 	FOR(slist_tpl<tile_t>, const& i, tiles) {
3266 		welt->mark_area(i.grund->get_pos() - size / 2, size, mark);
3267 	}
3268 }
3269 
3270 
3271 
3272 /* Find a tile where this type of vehicle could stop
3273  * @author prissi
3274  */
find_matching_position(const waytype_t w) const3275 const grund_t *haltestelle_t::find_matching_position(const waytype_t w) const
3276 {
3277 	// iterate over all tiles
3278 	FOR(slist_tpl<tile_t>, const& i, tiles) {
3279 		if (i.grund->hat_weg(w)) {
3280 			return i.grund;
3281 		}
3282 	}
3283 	return NULL;
3284 }
3285 
3286 
3287 
3288 /* checks, if there is an unoccupied loading bay for this kind of thing
3289  * @author prissi
3290  */
find_free_position(const waytype_t w,convoihandle_t cnv,const obj_t::typ d) const3291 bool haltestelle_t::find_free_position(const waytype_t w,convoihandle_t cnv,const obj_t::typ d) const
3292 {
3293 	// iterate over all tiles
3294 	FOR(slist_tpl<tile_t>, const& i, tiles) {
3295 		if (i.reservation == cnv || !i.reservation.is_bound()) {
3296 			// not reserved
3297 			grund_t* const gr = i.grund;
3298 			assert(gr);
3299 			// found a stop for this waytype but without object d ...
3300 			if(gr->hat_weg(w)  &&  gr->suche_obj(d)==NULL) {
3301 				// not occupied
3302 				return true;
3303 			}
3304 		}
3305 	}
3306 	return false;
3307 }
3308 
3309 
3310 /* reserves a position (caution: railblocks work differently!
3311  * @author prissi
3312  */
reserve_position(grund_t * gr,convoihandle_t cnv)3313 bool haltestelle_t::reserve_position(grund_t *gr,convoihandle_t cnv)
3314 {
3315 	slist_tpl<tile_t>::iterator i = std::find(tiles.begin(), tiles.end(), gr);
3316 	if (i != tiles.end()) {
3317 		if (i->reservation == cnv) {
3318 //DBG_MESSAGE("haltestelle_t::reserve_position()","gr=%d,%d already reserved by cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id());
3319 			return true;
3320 		}
3321 		// not reserved
3322 		if (!i->reservation.is_bound()) {
3323 			grund_t* gr = i->grund;
3324 			if(gr) {
3325 				// found a stop for this waytype but without object d ...
3326 				vehicle_t const& v = *cnv->front();
3327 				if (gr->hat_weg(v.get_waytype()) && !gr->suche_obj(v.get_typ())) {
3328 					// not occupied
3329 //DBG_MESSAGE("haltestelle_t::reserve_position()","success for gr=%i,%i cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id());
3330 					i->reservation = cnv;
3331 					return true;
3332 				}
3333 			}
3334 		}
3335 	}
3336 //DBG_MESSAGE("haltestelle_t::reserve_position()","failed for gr=%i,%i, cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id());
3337 	return false;
3338 }
3339 
3340 
3341 /* frees a reserved  position (caution: railblocks work differently!
3342  * @author prissi
3343  */
unreserve_position(grund_t * gr,convoihandle_t cnv)3344 bool haltestelle_t::unreserve_position(grund_t *gr, convoihandle_t cnv)
3345 {
3346 	slist_tpl<tile_t>::iterator i = std::find(tiles.begin(), tiles.end(), gr);
3347 	if (i != tiles.end()) {
3348 		if (i->reservation == cnv) {
3349 			i->reservation = convoihandle_t();
3350 			return true;
3351 		}
3352 	}
3353 DBG_MESSAGE("haltestelle_t::unreserve_position()","failed for gr=%p",gr);
3354 	return false;
3355 }
3356 
3357 
3358 /* can a convoi reserve this position?
3359  * @author prissi
3360  */
is_reservable(const grund_t * gr,convoihandle_t cnv) const3361 bool haltestelle_t::is_reservable(const grund_t *gr, convoihandle_t cnv) const
3362 {
3363 	FOR(slist_tpl<tile_t>, const& i, tiles) {
3364 		if (gr == i.grund) {
3365 			if (i.reservation == cnv) {
3366 DBG_MESSAGE("haltestelle_t::is_reservable()","gr=%d,%d already reserved by cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id());
3367 				return true;
3368 			}
3369 			// not reserved
3370 			if (!i.reservation.is_bound()) {
3371 				// found a stop for this waytype but without object d ...
3372 				vehicle_t const& v = *cnv->front();
3373 				if (gr->hat_weg(v.get_waytype()) && !gr->suche_obj(v.get_typ())) {
3374 					// not occupied
3375 					return true;
3376 				}
3377 			}
3378 			return false;
3379 		}
3380 	}
3381 DBG_MESSAGE("haltestelle_t::reserve_position()","failed for gr=%i,%i, cnv=%d",gr->get_pos().x,gr->get_pos().y,cnv.get_id());
3382 	return false;
3383 }
3384 
3385 
3386 /* deletes factory references so map rotation won't segfault
3387 */
release_factory_links()3388 void haltestelle_t::release_factory_links()
3389 {
3390 	FOR(slist_tpl<fabrik_t*>, const f, fab_list) {
3391 		f->unlink_halt(self);
3392 	}
3393 	fab_list.clear();
3394 }
3395 
3396 /* check if the station given is covered by this station */
is_halt_covered(const halthandle_t & halt) const3397 bool haltestelle_t::is_halt_covered(const halthandle_t &halt) const
3398 {
3399 	uint16 const cov = welt->get_settings().get_station_coverage();
3400 	FOR(slist_tpl<tile_t>, const& i, halt->get_tiles()) {
3401 		if(  gebaeude_t* const gb = i.grund->find<gebaeude_t>()  ) {
3402 			if ( koord_distance( gb->get_pos().get_2d(), get_next_pos( gb->get_pos().get_2d() )) <= cov ) {
3403 				return true;
3404 			}
3405 		}
3406 	}
3407 	return false;
3408 }
3409