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