1 #include "../simevent.h"
2 #include "../simcolor.h"
3 #include "../simconvoi.h"
4 #include "../vehicle/simvehicle.h"
5 #include "../simworld.h"
6 #include "../simdepot.h"
7 #include "../simhalt.h"
8 #include "../simskin.h"
9 #include "../boden/grund.h"
10 #include "../simfab.h"
11 #include "../simcity.h"
12 #include "minimap.h"
13 #include "schedule_gui.h"
14
15 #include "../dataobj/translator.h"
16 #include "../dataobj/settings.h"
17 #include "../dataobj/schedule.h"
18 #include "../dataobj/powernet.h"
19 #include "../dataobj/ribi.h"
20 #include "../dataobj/loadsave.h"
21
22 #include "../boden/wege/schiene.h"
23 #include "../obj/leitung2.h"
24 #include "../utils/cbuffer_t.h"
25 #include "../display/scr_coord.h"
26 #include "../display/simgraph.h"
27 #include "../display/viewport.h"
28 #include "../utils/simrandom.h"
29 #include "../player/simplay.h"
30
31 #include "../tpl/inthashtable_tpl.h"
32 #include "../tpl/slist_tpl.h"
33
34 #include <math.h>
35
36 sint32 minimap_t::max_cargo=0;
37 sint32 minimap_t::max_passed=0;
38
39 static sint32 max_waiting_change = 1;
40
41 static sint32 max_tourist_ziele = 1;
42 static sint32 max_waiting = 1;
43 static sint32 max_origin = 1;
44 static sint32 max_transfer = 1;
45 static sint32 max_service = 1;
46
47 static sint32 max_building_level = 0;
48
49 minimap_t * minimap_t::single_instance = NULL;
50 karte_ptr_t minimap_t::world;
51 minimap_t::MAP_DISPLAY_MODE minimap_t::mode = MAP_TOWN;
52 minimap_t::MAP_DISPLAY_MODE minimap_t::last_mode = MAP_TOWN;
53 bool minimap_t::is_visible = false;
54
55 #define MAX_MAP_TYPE_LAND 31
56 #define MAX_MAP_TYPE_WATER 5
57
58 // color for the land
59 static const uint8 map_type_color[MAX_MAP_TYPE_WATER+MAX_MAP_TYPE_LAND] =
60 {
61 // water level
62 97,
63 99,
64 19,
65 21,
66 23,
67 // terrain level
68 160,
69 161,
70 162,
71 163,
72 164,
73 165,
74 166,
75 167,
76 205,
77 206,
78 207,
79 172,
80 174,
81 159,
82 COL_LIGHT_ORANGE,
83 COL_TOLL,
84 156,
85 154,
86 115,
87 114,
88 113,
89 112,
90 216,
91 217,
92 218,
93 219,
94 220,
95 COL_LILAC,
96 222,
97 223,
98 224
99 };
100
101 const uint8 minimap_t::severity_color[MAX_SEVERITY_COLORS] =
102 {
103 106,
104 2,
105 85,
106 86,
107 29,
108 30,
109 COL_YELLOW,
110 71,
111 39,
112 COL_OPERATION
113 };
114
115
line_segment_t(koord s,uint8 so,koord e,uint8 eo,schedule_t * sched,player_t * p,uint8 cc,bool diagonal)116 minimap_t::line_segment_t::line_segment_t(koord s, uint8 so, koord e, uint8 eo, schedule_t* sched, player_t* p, uint8 cc, bool diagonal)
117 {
118 schedule = sched;
119 waytype = sched->get_waytype();
120 player = p;
121 colorcount = cc;
122 start_diagonal = diagonal;
123 if( s.x<e.x || (s.x==e.x && s.y<e.y) ) {
124 start = s;
125 end = e;
126 start_offset = so;
127 end_offset = eo;
128 }
129 else {
130 start = e;
131 end = s;
132 start_offset = eo;
133 end_offset = so;
134 }
135 }
136
137
138 // helper function for line segment_t
operator ==(const line_segment_t & other) const139 bool minimap_t::line_segment_t::operator==(const line_segment_t & other) const
140 {
141 return
142 start == other.start &&
143 end == other.end &&
144 player == other.player &&
145 schedule->similar( other.schedule, player );
146 }
147
148
149 // Ordering based on first start then end coordinate
operator ()(const minimap_t::line_segment_t & a,const minimap_t::line_segment_t & b) const150 bool minimap_t::LineSegmentOrdering::operator()(const minimap_t::line_segment_t& a, const minimap_t::line_segment_t& b) const
151 {
152 if( a.start.x == b.start.x ) {
153 // same start ...
154 return a.end.x < b.end.x;
155 }
156 return a.start.x < b.start.x;
157 }
158
159
160 static uint8 colore_idx = 0;
161 static inthashtable_tpl< int, slist_tpl<schedule_t *> > waypoint_hash;
162
163
164 // add the schedule to the map (if there is a valid one)
add_to_schedule_cache(convoihandle_t cnv,bool with_waypoints)165 void minimap_t::add_to_schedule_cache( convoihandle_t cnv, bool with_waypoints )
166 {
167 // make sure this is valid!
168 if( !cnv.is_bound() ) {
169 return;
170 }
171 schedule_t *schedule = cnv->get_schedule();
172 if( !show_network_load_factor ) {
173 colore_idx += 8;
174 if( colore_idx >= 208 ) {
175 colore_idx = (colore_idx % 8) + 1;
176 if( colore_idx == 7 ) {
177 colore_idx = 0;
178 }
179 }
180 }
181 else {
182 //TODO: extract common part from with schedule_list_gui_t::display()
183 int capacity = 0, load = 0; // total capacity and load of line (=sum of all conv's cap/load)
184
185 if(cnv->get_line().is_bound()) {
186 for( uint i = 0; i < cnv->get_line()->count_convoys(); i++ ) {
187 convoihandle_t const cnv_in_line = cnv->get_line()->get_convoy(i);
188 // we do not want to count the capacity of depot convois
189 if( !cnv_in_line->in_depot() ) {
190 for( unsigned j = 0; j < cnv_in_line->get_vehicle_count(); j++ ) {
191 capacity += cnv_in_line->get_vehikel(j)->get_cargo_max();
192 load += cnv_in_line->get_vehikel(j)->get_total_cargo();
193 }
194 }
195 }
196 }
197 else {
198 // we do not want to count the capacity of depot convois
199 if(!cnv->in_depot()) {
200 for(unsigned j = 0; j < cnv->get_vehicle_count(); j++) {
201 capacity += cnv->get_vehikel(j)->get_cargo_max();
202 load += cnv->get_vehikel(j)->get_total_cargo();
203 }
204 }
205 }
206
207 // we check if cap is zero, since theoretically a
208 // conv can consist of only 1 vehicle, which has no cap (eg. locomotive)
209 // and we do not like to divide by zero, do we?
210 if(capacity > 0) {
211 colore_idx = severity_color[MAX_SEVERITY_COLORS-load * MAX_SEVERITY_COLORS / capacity];
212 }
213 else {
214 colore_idx = severity_color[MAX_SEVERITY_COLORS-1];
215 }
216 }
217
218 // ok, add this schedule to map
219 // from here on we have a valid convoi
220 int stops = 0;
221 uint8 old_offset = 0, first_offset = 0, temp_offset = 0;
222 koord old_stop, first_stop, temp_stop;
223 bool last_diagonal = false;
224 const bool add_schedule = schedule->get_waytype() != air_wt;
225
226 FOR( minivec_tpl<schedule_entry_t>, cur, schedule->entries ) {
227
228 //cycle on stops
229 //try to read station's coordinates if there's a station at this schedule stop
230 halthandle_t station = haltestelle_t::get_halt( cur.pos, cnv->get_owner() );
231 if( station.is_bound() ) {
232 stop_cache.append_unique( station );
233 temp_stop = station->get_basis_pos();
234 stops ++;
235 }
236 else if( with_waypoints ) {
237 temp_stop = cur.pos.get_2d();
238 stops ++;
239 }
240 else {
241 continue;
242 }
243
244 const int key = temp_stop.x + temp_stop.y*world->get_size().x;
245 waypoint_hash.put( key );
246 // now get the offset
247 slist_tpl<schedule_t *>*pt_list = waypoint_hash.access(key);
248 if( add_schedule ) {
249 // init key
250 if( !pt_list->is_contained( schedule ) ) {
251 // not known => append
252 temp_offset = pt_list->get_count();
253 }
254 else {
255 // how many times we reached here?
256 temp_offset = pt_list->index_of( schedule );
257 }
258 }
259 else {
260 temp_offset = 0;
261 }
262
263 if( stops>1 ) {
264 last_diagonal ^= true;
265 if( (temp_stop.x-old_stop.x)*(temp_stop.y-old_stop.y) == 0 ) {
266 last_diagonal = false;
267 }
268 if( !schedule_cache.insert_unique_ordered( line_segment_t( temp_stop, temp_offset, old_stop, old_offset, schedule, cnv->get_owner(), colore_idx, last_diagonal ), LineSegmentOrdering() ) && add_schedule ) {
269 // append if added and not yet there
270 if( !pt_list->is_contained( schedule ) ) {
271 pt_list->append( schedule );
272 }
273 if( stops == 2 ) {
274 // append first stop too, when this is called for the first time
275 const int key = first_stop.x + first_stop.y*world->get_size().x;
276 waypoint_hash.put( key );
277 slist_tpl<schedule_t *>*pt_list = waypoint_hash.access(key);
278 if( !pt_list->is_contained( schedule ) ) {
279 pt_list->append( schedule );
280 }
281 }
282 }
283 old_stop = temp_stop;
284 old_offset = temp_offset;
285 }
286 else {
287 first_stop = temp_stop;
288 first_offset = temp_offset;
289 old_stop = temp_stop;
290 old_offset = temp_offset;
291 }
292 }
293
294 if( stops > 2 ) {
295 // connect to start
296 last_diagonal ^= true;
297 schedule_cache.insert_unique_ordered( line_segment_t( first_stop, first_offset, old_stop, old_offset, schedule, cnv->get_owner(), colore_idx, last_diagonal ), LineSegmentOrdering() );
298 }
299 }
300
301
302
303 // some routines for the minimap with schedules
number_to_radius(uint32 n)304 static uint32 number_to_radius( uint32 n )
305 {
306 return log2( n>>5 );
307 }
308
309
display_airport(const scr_coord_val xx,const scr_coord_val yy,const FLAGGED_PIXVAL color)310 static void display_airport( const scr_coord_val xx, const scr_coord_val yy, const FLAGGED_PIXVAL color )
311 {
312 int x = xx + 5;
313 int y = yy - 11;
314
315 if ( y < 0 ) {
316 y = 0;
317 }
318
319 const char symbol[] = {
320 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X',
321 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X',
322 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X',
323 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X',
324 'X', '.', '.', '.', 'X', 'X', 'X', '.', '.', '.', 'X',
325 'X', '.', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '.', 'X',
326 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X',
327 'X', 'X', '.', '.', '.', 'X', '.', '.', '.', 'X', 'X',
328 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X',
329 'X', '.', '.', '.', 'X', 'X', 'X', '.', '.', '.', 'X',
330 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'
331 };
332
333 for ( int i = 0; i < 11; i++ ) {
334 for ( int j = 0; j < 11; j++ ) {
335 if ( symbol[i + j * 11] == 'X' ) {
336 display_vline_wh_clip_rgb( x + i, y + j, 1, color, true );
337 }
338 }
339 }
340 }
341
display_harbor(const scr_coord_val xx,const scr_coord_val yy,const FLAGGED_PIXVAL color)342 static void display_harbor( const scr_coord_val xx, const scr_coord_val yy, const FLAGGED_PIXVAL color )
343 {
344 int x = xx + 5;
345 int y = yy - 11 + 13; //to not overwrite airline symbol
346
347 if ( y < 0 ) {
348 y = 0;
349 }
350
351 const char symbol[] = {
352 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X',
353 'X', '.', '.', '.', 'X', 'X', 'X', '.', '.', '.', 'X',
354 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X',
355 'X', '.', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '.', 'X',
356 'X', '.', '.', '.', 'X', 'X', 'X', '.', '.', '.', 'X',
357 'X', '.', '.', '.', '.', 'X', '.', '.', '.', '.', 'X',
358 'X', 'X', 'X', '.', '.', 'X', '.', '.', 'X', 'X', 'X',
359 'X', 'X', 'X', 'X', '.', 'X', '.', 'X', 'X', 'X', 'X',
360 'X', '.', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '.', 'X',
361 'X', '.', '.', 'X', 'X', 'X', 'X', 'X', '.', '.', 'X',
362 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'
363 };
364
365 for ( int i = 0; i < 11; i++ ) {
366 for ( int j = 0; j < 11; j++ ) {
367 if ( symbol[i + j * 11] == 'X' ) {
368 display_vline_wh_clip_rgb( x + i, y + j, 1, color, true );
369 }
370 }
371 }
372 }
373 // those will be replaced by pak images later ...!
374
375
display_thick_line(scr_coord_val x1,scr_coord_val y1,scr_coord_val x2,scr_coord_val y2,PIXVAL col,bool dotting,short dot_full,short dot_empty,short thickness)376 static void display_thick_line( scr_coord_val x1, scr_coord_val y1, scr_coord_val x2, scr_coord_val y2, PIXVAL col, bool dotting, short dot_full, short dot_empty, short thickness )
377 {
378 double delta_x = abs( x1 - x2 );
379 double delta_y = abs( y1 - y2 );
380
381 if( delta_x == 0.0 || delta_y/delta_x > 2.0 ) {
382 // mostly vertical
383 x1 -= thickness/2;
384 x2 -= thickness/2;
385 for( int i = 0; i < thickness; i++ ) {
386 if ( !dotting ) {
387 display_direct_line_rgb( x1 + i, y1, x2 + i, y2, col );
388 }
389 else {
390 display_direct_line_dotted_rgb( x1 + i, y1, x2 + i, y2, dot_full, dot_empty, col );
391 }
392 }
393 }
394 else if( delta_y == 0.0 || delta_x/delta_y > 2.0 ) {
395 // mostly horizontal
396 y1 -= thickness/2;
397 y2 -= thickness/2;
398 for( int i = 0; i < thickness; i++ ) {
399 if ( !dotting ) {
400 display_direct_line_rgb( x1, y1 + i, x2, y2 + i, col );
401 }
402 else {
403 display_direct_line_dotted_rgb( x1, y1 + i, x2, y2 + i, dot_full, dot_empty, col );
404 }
405 }
406 }
407 else {
408 // diagonal
409 int y_multiplier = (x1-x2)/(y1-y2) < 0 ? +1 : -1;
410 thickness = (thickness*7)/8;
411 x1 -= thickness/2;
412 x2 -= thickness/2;
413 y1 -= thickness*y_multiplier/2;
414 y2 -= thickness*y_multiplier/2;
415 for( int i = 0; i < thickness; i++ ) {
416 if ( !dotting ) {
417 display_direct_line_rgb( x1+i, y1+i*y_multiplier, x2+i, y2+i*y_multiplier, col );
418 display_direct_line_rgb( x1+i+1, y1+i*y_multiplier, x2+i+1, y2+i*y_multiplier, col );
419 }
420 else {
421 display_direct_line_dotted_rgb( x1 + i, y1 + i*y_multiplier, x2 + i, y2 + i*y_multiplier, dot_full, dot_empty, col );
422 display_direct_line_dotted_rgb( x1 + i + 1, y1 + i*y_multiplier, x2 + i + 1, y2 + i*y_multiplier, dot_full, dot_empty, col );
423 }
424 }
425 }
426 }
427
428
line_segment_draw(waytype_t type,scr_coord start,uint8 start_offset,scr_coord end,uint8 end_offset,bool diagonal,PIXVAL colore)429 static void line_segment_draw( waytype_t type, scr_coord start, uint8 start_offset, scr_coord end, uint8 end_offset, bool diagonal, PIXVAL colore )
430 {
431 // airplanes are different, so we must check for them first
432 if( type == air_wt ) {
433 // ignore offset for airplanes
434 draw_bezier_rgb( start.x, start.y, end.x, end.y, 50, 50, 50, 50, colore, 5, 5 );
435 draw_bezier_rgb( start.x + 1, start.y + 1, end.x + 1, end.y + 1, 50, 50, 50, 50, colore, 5, 5 );
436 }
437 else {
438 // add offsets
439 start.x += start_offset*3;
440 end.x += end_offset*3;
441 start.y += start_offset*3;
442 end.y += end_offset*3;
443 // due to isometric drawing, order may be swapped
444 if( start.x > end.x ) {
445 // but we need start.x <= end.x!
446 scr_coord temp = start;
447 start = end;
448 end = temp;
449 uint8 temp_offset = start_offset;
450 start_offset = end_offset;
451 end_offset = temp_offset;
452 diagonal ^= 1;
453 }
454 // now determine line style
455 uint8 thickness = 3;
456 bool dotted = false;
457 switch( type ) {
458 case monorail_wt:
459 case maglev_wt:
460 thickness = 5;
461 break;
462 case track_wt:
463 thickness = 4;
464 break;
465 case road_wt:
466 thickness = 2;
467 break;
468 case tram_wt:
469 case narrowgauge_wt:
470 thickness = 3;
471 break;
472 default:
473 thickness = 3;
474 dotted = true;
475 }
476 // start.x is always <= end.x ...
477 const int delta_y = end.y-start.y;
478 if( (start.x-end.x)*delta_y == 0 ) {
479 // horizontal/vertical line
480 display_thick_line( start.x, start.y, end.x, end.y, colore, dotted, 5, 3, thickness );
481 }
482 else {
483 // two segment
484 scr_coord mid;
485 int signum_y = delta_y/abs(delta_y);
486 // diagonal line to right bottom
487 if( delta_y > 0 ) {
488 if( end_offset && !diagonal ) {
489 // start with diagonal to avoid parallel lines
490 diagonal = true;
491 }
492 if( start_offset && diagonal ) {
493 // end with diagonal to avoid parallel lines
494 diagonal = false;
495 }
496 }
497 // now draw a two segment line
498 if( diagonal ) {
499 // start with diagonal
500 if( abs(delta_y) > end.x-start.x ) {
501 mid.x = end.x;
502 mid.y = start.y + (end.x-start.x)*signum_y;
503 }
504 else {
505 mid.x = start.x + abs(delta_y);
506 mid.y = end.y;
507 }
508 display_thick_line( start.x, start.y, mid.x, mid.y, colore, dotted, 5, 3, thickness );
509 display_thick_line( mid.x, mid.y, end.x, end.y, colore, dotted, 5, 3, thickness );
510 }
511 else {
512 // end with diagonal
513 const int delta_y = end.y-start.y;
514 if( abs(delta_y) > end.x-start.x ) {
515 mid.x = start.x;
516 mid.y = end.y - (end.x-start.x)*signum_y;
517 }
518 else {
519 mid.x = end.x - abs(delta_y);
520 mid.y = start.y;
521 }
522 display_thick_line( start.x, start.y, mid.x, mid.y, colore, dotted, 5, 3, thickness );
523 display_thick_line( mid.x, mid.y, end.x, end.y, colore, dotted, 5, 3, thickness );
524 }
525 }
526 }
527 }
528
529
530 // converts map (karte) koordinates to screen koordinates
map_to_screen_coord(const koord & k) const531 scr_coord minimap_t::map_to_screen_coord(const koord &k) const
532 {
533 assert(zoom_in ==1 || zoom_out ==1 );
534 sint32 x = (sint32)k.x * zoom_in;
535 sint32 y = (sint32)k.y * zoom_in;
536 if(isometric) {
537 // 45 rotate view
538 sint32 xrot = (sint32)world->get_size().y * zoom_in + x - y - 1;
539 y = ( x + y )/2;
540 x = xrot;
541 }
542 return scr_coord(x/zoom_out, y/zoom_out);
543 }
544
545
546 // and re-transform
screen_to_map_coord(const scr_coord & c) const547 koord minimap_t::screen_to_map_coord(const scr_coord &c) const
548 {
549 sint32 x = ((sint32)c.x*zoom_out)/zoom_in;
550 sint32 y = ((sint32)c.y*zoom_out)/zoom_in;
551 if(isometric) {
552 y *= 2;
553 x = (x + y - world->get_size().y)/2;
554 y = y - x;
555 }
556 return koord(x,y);
557 }
558
559
change_zoom_factor(bool magnify)560 bool minimap_t::change_zoom_factor(bool magnify)
561 {
562 bool zoomed = false;
563 if( magnify ) {
564 // zoom in
565 if( zoom_out > 1 ) {
566 zoom_out--;
567 zoomed = true;
568 }
569 else {
570 // check here for maximum zoom-out, otherwise there will be integer overflows
571 // with large maps as we calculate with sint16 coordinates ...
572 int max_zoom_in = min( 32767 / (2*world->get_size_max()), 16);
573 if( zoom_in < max_zoom_in ) {
574 zoom_in++;
575 zoomed = true;
576 }
577 }
578 }
579 else {
580 // zoom out
581 if( zoom_in > 1 ) {
582 zoom_in--;
583 zoomed = true;
584 }
585 else if( zoom_out < 16 ) {
586 zoom_out++;
587 zoomed = true;
588 }
589 }
590
591 if( zoomed ){
592 // recalc map size
593 calc_map_size();
594 }
595 return zoomed;
596 }
597
598
calc_severity_color(sint32 amount,sint32 max_value)599 PIXVAL minimap_t::calc_severity_color(sint32 amount, sint32 max_value)
600 {
601 if(max_value!=0) {
602 // color array goes from light blue to red
603 sint32 severity = amount * MAX_SEVERITY_COLORS / (max_value+1);
604 return color_idx_to_rgb( minimap_t::severity_color[ clamp( severity, 0, MAX_SEVERITY_COLORS-1 ) ]);
605 }
606 return color_idx_to_rgb( minimap_t::severity_color[0]);
607 }
608
609
calc_severity_color_log(sint32 amount,sint32 max_value)610 PIXVAL minimap_t::calc_severity_color_log(sint32 amount, sint32 max_value)
611 {
612 if( max_value>1 ) {
613 sint32 severity;
614 if( amount <= 0x003FFFFF ) {
615 severity = log2( (uint32)( (amount << MAX_SEVERITY_COLORS) / (max_value+1) ) );
616 }
617 else {
618 severity = (uint32)( log( (double)amount*(double)(1<<MAX_SEVERITY_COLORS)/(double)max_value) + 0.5 );
619 }
620 return color_idx_to_rgb( minimap_t::severity_color[ clamp( severity, 0, MAX_SEVERITY_COLORS-1 ) ]);
621 }
622 return color_idx_to_rgb( minimap_t::severity_color[0]);
623 }
624
625
set_map_color_clip(sint16 x,sint16 y,PIXVAL color)626 void minimap_t::set_map_color_clip( sint16 x, sint16 y, PIXVAL color )
627 {
628 if( 0<=x && (uint16)x < map_data->get_width() && 0<=y && (uint16)y < map_data->get_height() ) {
629 map_data->at( x, y ) = color;
630 }
631 }
632
633
set_map_color(koord k_,const PIXVAL color)634 void minimap_t::set_map_color(koord k_, const PIXVAL color)
635 {
636 // if map is in normal mode, set new color for map
637 // otherwise do nothing
638 // result: convois will not "paint over" special maps
639 if ( map_data==NULL || !world->is_within_limits(k_)) {
640 return;
641 }
642
643 scr_coord c = map_to_screen_coord(k_);
644 c -= cur_off;
645
646 if( isometric ) {
647 // since isometric is distorted
648 const sint32 xw = zoom_out>=2 ? 1 : 2*zoom_in;
649 // increase size at zoom_in 2, 5, 9, 11
650 const scr_coord_val mid_y = ((xw+1) / 5) + (xw / 18);
651 // center line
652 for( int x=0; x<xw; x++ ) {
653 set_map_color_clip( c.x+x, c.y+mid_y, color );
654 }
655 // lines above and below
656 if( mid_y > 0 ) {
657 scr_coord_val left = 2, right = xw-2 + ((xw>>1)&1);
658 for( scr_coord_val y_offset = 1; y_offset <= mid_y; y_offset++ ) {
659 for( int x=left; x<right; x++ ) {
660 set_map_color_clip( c.x+x, c.y+mid_y+y_offset, color );
661 set_map_color_clip( c.x+x, c.y+mid_y-y_offset, color );
662 }
663 left += 2;
664 right -= 2;
665 }
666 }
667 }
668 else {
669 for( sint32 x = max(0,c.x); x < zoom_in+c.x && (uint32)x < map_data->get_width(); x++ ) {
670 for( sint32 y = max(0,c.y); y < zoom_in+c.y && (uint32)y < map_data->get_height(); y++ ) {
671 map_data->at(x, y) = color;
672 }
673 }
674 }
675 }
676
677
678 /**
679 * calculates ground color for position relative to water height
680 * @param hoehe height of the tile
681 * @param groundwater water height
682 * @author Hj. Malthaner
683 */
calc_height_color(const sint16 hoehe,const sint16 groundwater)684 PIXVAL minimap_t::calc_height_color(const sint16 hoehe, const sint16 groundwater)
685 {
686 sint16 relative_index;
687 if( hoehe>groundwater ) {
688 // adjust index for world_maximum_height
689 relative_index = (hoehe-groundwater)*MAX_MAP_TYPE_LAND/world->get_settings().get_maximumheight();
690 if( (hoehe-groundwater)*MAX_MAP_TYPE_LAND%world->get_settings().get_maximumheight()!=0 ) {
691 // to avoid relative_index==0
692 relative_index += 1;
693 }
694 } else {
695 relative_index = hoehe-groundwater;
696 }
697 return color_idx_to_rgb(map_type_color[clamp( relative_index+MAX_MAP_TYPE_WATER-1, 0, MAX_MAP_TYPE_WATER+MAX_MAP_TYPE_LAND-1 )]);
698 }
699
700
701 /**
702 * Calculates the minimap color of a ground tile
703 * @author Hj. Malthaner
704 */
calc_ground_color(const grund_t * gr)705 PIXVAL minimap_t::calc_ground_color(const grund_t *gr)
706 {
707 PIXVAL color = color_idx_to_rgb(COL_BLACK);
708
709 #ifdef DEBUG_ROUTES
710 /* for debug purposes only ...*/
711 if(world->ist_markiert(gr)) {
712 color = color_idx_to_rgb(COL_PURPLE);
713 }else
714 #endif
715 if(gr->get_halt().is_bound()) {
716 color = COL_HALT;
717 }
718 else {
719 switch(gr->get_typ()) {
720 case grund_t::brueckenboden:
721 color = color_idx_to_rgb(MN_GREY3);
722 break;
723 case grund_t::tunnelboden:
724 color = color_idx_to_rgb(COL_BROWN);
725 break;
726 case grund_t::monorailboden:
727 color = COL_MONORAIL;
728 break;
729 case grund_t::fundament:
730 {
731 // object at zero is either factory or house (or attraction ... )
732 gebaeude_t *gb = gr->find<gebaeude_t>();
733 fabrik_t *fab = gb ? gb->get_fabrik() : NULL;
734 if(fab==NULL) {
735 color = color_idx_to_rgb(COL_GREY3);
736 }
737 else {
738 color = fab->get_color();
739 }
740 }
741 break;
742 case grund_t::wasser:
743 {
744 // object at zero is either factory or boat
745 gebaeude_t *gb = gr->find<gebaeude_t>();
746 fabrik_t *fab = gb ? gb->get_fabrik() : NULL;
747 if(fab==NULL) {
748 sint16 height = (gr->get_grund_hang()%3);
749 color = calc_height_color( world->lookup_hgt( gr->get_pos().get_2d() ) + height, world->get_water_hgt( gr->get_pos().get_2d() ) );
750 //color = color_idx_to_rgb(COL_BLUE); // water with boat?
751 }
752 else {
753 color = fab->get_color();
754 }
755 }
756 break;
757 // normal ground ...
758 default:
759 if(gr->hat_wege()) {
760 switch(gr->get_weg_nr(0)->get_waytype()) {
761 case road_wt: color = COL_ROAD; break;
762 case tram_wt:
763 case track_wt: color = COL_RAIL; break;
764 case water_wt: color = COL_CANAL; break;
765 case air_wt: color = COL_RUNWAY; break;
766 case monorail_wt:
767 default: // all other ways light red ...
768 color = 135; break;
769 break;
770 }
771 }
772 else {
773 const leitung_t* lt = gr->find<leitung_t>();
774 if(lt!=NULL) {
775 color = COL_POWERLINE;
776 }
777 else {
778 sint16 height = (gr->get_grund_hang()%3);
779 if( gr->get_hoehe() > world->get_groundwater() ) {
780 color = calc_height_color( gr->get_hoehe() + height, world->get_groundwater() );
781 }
782 else {
783 color = calc_height_color( gr->get_hoehe() + height, gr->get_hoehe() + height - 1);
784 }
785 }
786 }
787 break;
788 }
789 }
790 return color;
791 }
792
793
calc_map_pixel(const koord k)794 void minimap_t::calc_map_pixel(const koord k)
795 {
796 // no pixels visible, so noting to calculate
797 if(!is_visible) {
798 return;
799 }
800
801 // always use to uppermost ground
802 const planquadrat_t *plan=world->access(k);
803 if(plan==NULL || plan->get_boden_count()==0) {
804 return;
805 }
806 const grund_t *gr=plan->get_boden_bei(plan->get_boden_count()-1);
807
808 if( mode!=MAP_PAX_DEST && gr->get_convoi_vehicle() ) {
809 set_map_color( k, COL_VEHICLE );
810 return;
811 }
812
813 // first use ground color
814 set_map_color( k, calc_ground_color (gr) );
815
816 switch(mode&~MAP_MODE_FLAGS) {
817 // show passenger coverage
818 // display coverage
819 case MAP_PASSENGER:
820 if( plan->get_haltlist_count()>0 ) {
821 halthandle_t halt = plan->get_haltlist()[0];
822 if (halt->get_pax_enabled() && !halt->get_pax_connections().empty()) {
823 set_map_color( k, color_idx_to_rgb(halt->get_owner()->get_player_color1() + 3) );
824 }
825 }
826 break;
827
828 // show mail coverage
829 // display coverage
830 case MAP_MAIL:
831 if( plan->get_haltlist_count()>0 ) {
832 halthandle_t halt = plan->get_haltlist()[0];
833 if (halt->get_mail_enabled() && !halt->get_mail_connections().empty()) {
834 set_map_color( k, color_idx_to_rgb(halt->get_owner()->get_player_color1() + 3) );
835 }
836 }
837 break;
838
839 // show usage
840 case MAP_FREIGHT:
841 // need to init the maximum?
842 if(max_cargo==0) {
843 max_cargo = 1;
844 calc_map();
845 }
846 else if( gr->hat_wege() ) {
847 // now calc again ...
848 sint32 cargo=0;
849
850 // maximum two ways for one ground
851 const weg_t *w=gr->get_weg_nr(0);
852 if(w) {
853 cargo = w->get_statistics(WAY_STAT_GOODS);
854 const weg_t *w=gr->get_weg_nr(1);
855 if(w) {
856 cargo += w->get_statistics(WAY_STAT_GOODS);
857 }
858 if( cargo > max_cargo ) {
859 max_cargo = cargo;
860 }
861 set_map_color(k, calc_severity_color_log(cargo, max_cargo));
862 }
863 }
864 break;
865
866 // show traffic (=convois/month)
867 case MAP_TRAFFIC:
868 // need to init the maximum?
869 if( max_passed==0 ) {
870 max_passed = 1;
871 calc_map();
872 }
873 else if(gr->hat_wege()) {
874 // now calc again ...
875 sint32 passed=0;
876
877 // maximum two ways for one ground
878 const weg_t *w=gr->get_weg_nr(0);
879 if(w) {
880 passed = w->get_statistics(WAY_STAT_CONVOIS);
881 if( weg_t *w=gr->get_weg_nr(1) ) {
882 passed += w->get_statistics(WAY_STAT_CONVOIS);
883 }
884 if( passed > max_passed ) {
885 max_passed = passed;
886 }
887 set_map_color(k, calc_severity_color_log( passed, max_passed ) );
888 }
889 }
890 break;
891
892 // show tracks: white: no electricity, red: electricity, yellow: signal
893 case MAP_TRACKS:
894 // show track
895 if (gr->hat_weg(track_wt)) {
896 const schiene_t * sch = (const schiene_t *) (gr->get_weg(track_wt));
897 if(sch->is_electrified()) {
898 set_map_color(k, color_idx_to_rgb(COL_RED));
899 }
900 else {
901 set_map_color(k, color_idx_to_rgb(COL_WHITE));
902 }
903 // show signals
904 if(sch->has_sign() || sch->has_signal()) {
905 set_map_color(k, color_idx_to_rgb(COL_YELLOW));
906 }
907 }
908 break;
909
910 // show max speed (if there)
911 case MAX_SPEEDLIMIT:
912 {
913 sint32 speed=gr->get_max_speed();
914 if(speed) {
915 set_map_color(k, calc_severity_color(gr->get_max_speed(), 450));
916 }
917 }
918 break;
919
920 // find power lines
921 case MAP_POWERLINES:
922 {
923 const leitung_t* lt = gr->find<leitung_t>();
924 if(lt!=NULL) {
925 set_map_color(k, calc_severity_color((sint32)lt->get_net()->get_demand(),(sint32)lt->get_net()->get_supply()) );
926 }
927 }
928 break;
929
930 case MAP_FOREST:
931 if( gr->get_top()>1 && gr->obj_bei(gr->get_top()-1)->get_typ()==obj_t::baum ) {
932 set_map_color(k, color_idx_to_rgb(COL_GREEN) );
933 }
934 break;
935
936 case MAP_OWNER:
937 // show ownership
938 {
939 if( gr->is_halt() ) {
940 set_map_color(k, color_idx_to_rgb(gr->get_halt()->get_owner()->get_player_color1()+3));
941 }
942 else if( weg_t *weg = gr->get_weg_nr(0) ) {
943 set_map_color(k, weg->get_owner()==NULL ? color_idx_to_rgb(COL_ORANGE) : color_idx_to_rgb(weg->get_owner()->get_player_color1()+3) );
944 }
945 if( gebaeude_t *gb = gr->find<gebaeude_t>() ) {
946 if( gb->get_owner()!=NULL ) {
947 set_map_color(k, color_idx_to_rgb(gb->get_owner()->get_player_color1()+3) );
948 }
949 }
950 break;
951 }
952
953 case MAP_LEVEL:
954 if( max_building_level == 0 ) {
955 // init maximum
956 max_building_level = 1;
957 calc_map();
958 }
959 else if( gr->get_typ() == grund_t::fundament ) {
960 if( gebaeude_t *gb = gr->find<gebaeude_t>() ) {
961 if( gb->is_city_building() ) {
962 sint32 level = gb->get_tile()->get_desc()->get_level();
963 if( level > max_building_level ) {
964 max_building_level = level;
965 }
966 set_map_color(k, calc_severity_color( level, max_building_level ) );
967 }
968 }
969 }
970 break;
971
972 default:
973 break;
974 }
975 }
976
977
get_min_size() const978 scr_size minimap_t::get_min_size() const
979 {
980 return get_max_size(); //scr_size(0,0);
981 }
982
983
get_max_size() const984 scr_size minimap_t::get_max_size() const
985 {
986 scr_coord size = map_to_screen_coord( koord( world->get_size().x, 0 ) );
987 scr_coord down = map_to_screen_coord( koord( world->get_size().x, world->get_size().y ) );
988 size.y = down.y;
989 if( isometric ) {
990 size.x += zoom_in*2;
991 }
992 return scr_size(size.x, size.y);
993 }
994
995
calc_map_size()996 void minimap_t::calc_map_size()
997 {
998 set_size( get_max_size() ); // of the gui_component to adjust scroll bars
999 needs_redraw = true;
1000 }
1001
1002
calc_map()1003 void minimap_t::calc_map()
1004 {
1005 // only use bitmap size like screen size
1006 scr_size minimap_size ( min( get_size().w, new_size.w ), min( get_size().h, new_size.h ) );
1007 // actually the following line should reduce new/deletes, but does not work properly
1008 if( map_data==NULL || (sint16) map_data->get_width()!=minimap_size.w || (sint16) map_data->get_height()!=minimap_size.h ) {
1009 delete map_data;
1010 map_data = new array2d_tpl<PIXVAL> ( minimap_size.w,minimap_size.h);
1011 }
1012 cur_off = new_off;
1013 cur_size = new_size;
1014 needs_redraw = false;
1015 is_visible = true;
1016
1017 // redraw the map
1018 if( !isometric ) {
1019 koord k;
1020 koord start_off = koord( (cur_off.x*zoom_out)/zoom_in, (cur_off.y*zoom_out)/zoom_in );
1021 koord end_off = start_off+koord( ( map_data->get_width()*zoom_out)/zoom_in+1, ( map_data->get_height()*zoom_out)/zoom_in+1 );
1022 for( k.y=start_off.y; k.y<end_off.y; k.y+=zoom_out ) {
1023 for( k.x=start_off.x; k.x<end_off.x; k.x+=zoom_out ) {
1024 calc_map_pixel(k);
1025 }
1026 }
1027 }
1028 else {
1029 // always the whole map ...
1030 if(isometric) {
1031 map_data->init( color_idx_to_rgb(COL_BLACK) );
1032 }
1033 koord k;
1034 for( k.y=0; k.y < world->get_size().y; k.y++ ) {
1035 for( k.x=0; k.x < world->get_size().x; k.x++ ) {
1036 calc_map_pixel(k);
1037 }
1038 }
1039 }
1040 }
1041
1042
minimap_t()1043 minimap_t::minimap_t()
1044 {
1045 map_data = NULL;
1046 zoom_in = 1;
1047 zoom_out = 1;
1048 isometric = false;
1049 show_network_load_factor = false;
1050 mode = MAP_TOWN;
1051 selected_city = NULL;
1052 cur_off = new_off = scr_coord(0,0);
1053 cur_size = new_size = scr_size(0,0);
1054 needs_redraw = true;
1055 transport_type_showed_on_map = simline_t::line;
1056 }
1057
1058
~minimap_t()1059 minimap_t::~minimap_t()
1060 {
1061 delete map_data;
1062 }
1063
1064
get_instance()1065 minimap_t *minimap_t::get_instance()
1066 {
1067 if(single_instance == NULL) {
1068 single_instance = new minimap_t();
1069 }
1070 return single_instance;
1071 }
1072
1073
init()1074 void minimap_t::init()
1075 {
1076 delete map_data;
1077 map_data = NULL;
1078 needs_redraw = true;
1079 is_visible = false;
1080
1081 calc_map_size();
1082 max_building_level = max_cargo = max_passed = 0;
1083 max_tourist_ziele = max_waiting = max_origin = max_transfer = max_service = 1;
1084 last_schedule_counter = world->get_schedule_counter()-1;
1085 set_selected_cnv(convoihandle_t());
1086 }
1087
1088
set_display_mode(MAP_DISPLAY_MODE new_mode)1089 void minimap_t::set_display_mode(MAP_DISPLAY_MODE new_mode)
1090 {
1091 mode = new_mode;
1092 needs_redraw = true;
1093 }
1094
1095
new_month()1096 void minimap_t::new_month()
1097 {
1098 needs_redraw = true;
1099 }
1100
1101
invalidate_map_lines_cache()1102 void minimap_t::invalidate_map_lines_cache()
1103 {
1104 last_schedule_counter = world->get_schedule_counter() - 1;
1105 needs_redraw = true;
1106 }
1107
1108
1109 // these two are the only gui_container specific routines
1110
1111
1112 // handle event
infowin_event(const event_t * ev)1113 bool minimap_t::infowin_event(const event_t *ev)
1114 {
1115 scr_coord c( ev->mx, ev->my );
1116 koord k = screen_to_map_coord(c);
1117
1118 // get factory under mouse cursor
1119 last_world_pos = k;
1120
1121 // recenter
1122 if(IS_LEFTCLICK(ev) || IS_LEFTDRAG(ev)) {
1123 world->get_viewport()->set_follow_convoi( convoihandle_t() );
1124
1125 const sint8 min_hgt = world->is_within_grid_limits(k) ? world->min_hgt(k) : 0;
1126
1127 world->get_viewport()->change_world_position(koord3d(k,min_hgt));
1128 return true;
1129 }
1130
1131 return false;
1132 }
1133
1134
1135 // helper function for finding nearby factory
get_factory_near(const koord,bool enlarge) const1136 const fabrik_t* minimap_t::get_factory_near( const koord, bool enlarge ) const
1137 {
1138 const fabrik_t *fab = fabrik_t::get_fab(last_world_pos);
1139 for( int i=0; i<4 && fab==NULL; i++ ) {
1140 fab = fabrik_t::get_fab( last_world_pos+koord::nsew[i] );
1141 }
1142 if( enlarge ) {
1143 for( int i=0; i<4 && fab==NULL; i++ ) {
1144 fab = fabrik_t::get_fab( last_world_pos+koord::nsew[i]*2 );
1145 }
1146 }
1147 return fab;
1148 }
1149
1150
1151 // helper function for redraw: factory connections
draw_factory_connections(const PIXVAL colour,const scr_coord pos) const1152 const fabrik_t* minimap_t::draw_factory_connections(const PIXVAL colour, const scr_coord pos) const
1153 {
1154 const fabrik_t* const fab = get_factory_near( last_world_pos, true );
1155 if(fab) {
1156 scr_coord fabpos = map_to_screen_coord( fab->get_pos().get_2d() ) + pos;
1157 const vector_tpl<koord>& lieferziele = event_get_last_control_shift() & 1 ? fab->get_suppliers() : fab->get_lieferziele();
1158 FOR(vector_tpl<koord>, lieferziel, lieferziele) {
1159 const fabrik_t * fab2 = fabrik_t::get_fab(lieferziel);
1160 if (fab2) {
1161 const scr_coord end = map_to_screen_coord( lieferziel ) + pos;
1162 display_direct_line_rgb(fabpos.x, fabpos.y, end.x, end.y, colour);
1163 display_fillbox_wh_clip_rgb(end.x, end.y, 3, 3, ((world->get_ticks() >> 10) & 1) == 0 ? color_idx_to_rgb(COL_RED) : color_idx_to_rgb(COL_WHITE), true);
1164
1165 scr_coord boxpos = end + scr_coord(10, 0);
1166 const char * name = translator::translate(fab2->get_name());
1167 int name_width = proportional_string_width(name)+8;
1168 boxpos.x = clamp( boxpos.x, pos.x, pos.x+get_size().w-name_width );
1169 display_ddd_proportional_clip(boxpos.x, boxpos.y, name_width, 0, color_idx_to_rgb(5), color_idx_to_rgb(COL_WHITE), name, true);
1170 }
1171 }
1172 }
1173 return fab;
1174 }
1175
1176
1177 // show the schedule on the minimap
set_selected_cnv(convoihandle_t c)1178 void minimap_t::set_selected_cnv( convoihandle_t c )
1179 {
1180 current_cnv = c;
1181 schedule_cache.clear();
1182 stop_cache.clear();
1183 colore_idx = 0;
1184 add_to_schedule_cache( current_cnv, true );
1185 last_schedule_counter = world->get_schedule_counter()-1;
1186 }
1187
1188
1189 // draw the map (and the overlays!)
draw(scr_coord pos)1190 void minimap_t::draw(scr_coord pos)
1191 {
1192 // sanity check, needed for overlarge maps
1193 if( (new_off.x|new_off.y)<0 ) {
1194 new_off = cur_off;
1195 }
1196 if( (new_size.w|new_size.h)<0 ) {
1197 new_size = cur_size;
1198 }
1199
1200 if( last_mode != mode ) {
1201 // only needing update, if last mode was also not about halts ...
1202 needs_redraw = (mode^last_mode) & ~MAP_MODE_FLAGS;
1203
1204 if( (mode & MAP_LINES) == 0 || (mode^last_mode) & MAP_MODE_HALT_FLAGS ) {
1205 // rebuilt stop_cache needed
1206 stop_cache.clear();
1207 }
1208
1209 if( (mode^last_mode) & (MAP_PASSENGER|MAP_MAIL|MAP_FREIGHT|MAP_LINES) || (mode&MAP_LINES && stop_cache.empty()) ) {
1210 // rebuilt line display
1211 last_schedule_counter = world->get_schedule_counter()-1;
1212 }
1213
1214 last_mode = mode;
1215 }
1216
1217 if( needs_redraw || cur_off!=new_off || cur_size!=new_size ) {
1218 calc_map();
1219 needs_redraw = false;
1220 }
1221
1222 if( map_data==NULL) {
1223 return;
1224 }
1225
1226 if( mode & MAP_PAX_DEST && selected_city!=NULL ) {
1227 const uint32 current_pax_destinations = selected_city->get_pax_destinations_new_change();
1228 if( pax_destinations_last_change > current_pax_destinations ) {
1229 // new month started.
1230 calc_map();
1231 }
1232 else if( pax_destinations_last_change < current_pax_destinations ) {
1233 // new pax_dest in city.
1234 const sparse_tpl<PIXVAL> *pax_dests = selected_city->get_pax_destinations_new();
1235 koord pos, min, max;
1236 PIXVAL color;
1237 for( uint16 i = 0; i < pax_dests->get_data_count(); i++ ) {
1238 pax_dests->get_nonzero( i, pos, color );
1239 min = koord((pos.x*world->get_size().x)/PAX_DESTINATIONS_SIZE,
1240 (pos.y*world->get_size().y)/PAX_DESTINATIONS_SIZE);
1241 max = koord(((pos.x+1)*world->get_size().x)/PAX_DESTINATIONS_SIZE,
1242 ((pos.y+1)*world->get_size().y)/PAX_DESTINATIONS_SIZE);
1243 pos = min;
1244 do {
1245 do {
1246 set_map_color(pos, color);
1247 pos.y++;
1248 } while(pos.y < max.y);
1249 pos.x++;
1250 } while (pos.x < max.x);
1251 }
1252 }
1253 pax_destinations_last_change = selected_city->get_pax_destinations_new_change();
1254 }
1255
1256 if( (uint16)cur_size.w > map_data->get_width() ) {
1257 display_fillbox_wh_clip_rgb( pos.x+new_off.x+map_data->get_width(), new_off.y+pos.y, 32767, map_data->get_height(), color_idx_to_rgb(COL_BLACK), true);
1258 }
1259 if( (uint16)cur_size.h > map_data->get_height() ) {
1260 display_fillbox_wh_clip_rgb( pos.x+new_off.x, pos.y+new_off.y+map_data->get_height(), 32767, 32767, color_idx_to_rgb(COL_BLACK), true);
1261 }
1262 display_array_wh( cur_off.x+pos.x, new_off.y+pos.y, map_data->get_width(), map_data->get_height(), map_data->to_array());
1263
1264 if( !current_cnv.is_bound() && mode & MAP_LINES ) {
1265 vector_tpl<linehandle_t> linee;
1266
1267 if( last_schedule_counter != world->get_schedule_counter() ) {
1268 // rebuild cache
1269 last_schedule_counter = world->get_schedule_counter();
1270 schedule_cache.clear();
1271 stop_cache.clear();
1272 waypoint_hash.clear();
1273 colore_idx = 0;
1274
1275 for( int np = 0; np < MAX_PLAYER_COUNT; np++ ) {
1276 if( player_showed_on_map != -1 && player_showed_on_map != np ) {
1277 continue;
1278 }
1279 //cycle on players
1280 if( world->get_player( np ) && world->get_player( np )->simlinemgmt.get_line_count() > 0 ) {
1281
1282 world->get_player( np )->simlinemgmt.get_lines( simline_t::line, &linee );
1283 for( uint32 j = 0; j < linee.get_count(); j++ ) {
1284 //cycle on lines
1285
1286 if( transport_type_showed_on_map != simline_t::line && linee[j]->get_linetype() != transport_type_showed_on_map ) {
1287 continue;
1288 }
1289
1290 if( !is_matching_freight_catg( linee[j]->get_goods_catg_index() ) ) {
1291 continue;
1292 }
1293
1294 // ware matches; now find at least a running convoi on this line ...
1295 convoihandle_t cnv;
1296 for( uint32 k = 0; k < linee[j]->count_convoys(); k++ ) {
1297 convoihandle_t test_cnv = linee[j]->get_convoy(k);
1298 if( test_cnv.is_bound() ) {
1299 int state = test_cnv->get_state();
1300 if( state != convoi_t::INITIAL && state != convoi_t::ENTERING_DEPOT && state != convoi_t::SELF_DESTRUCT ) {
1301 cnv = test_cnv;
1302 break;
1303 }
1304 }
1305 }
1306 if( !cnv.is_bound() ) {
1307 continue;
1308 }
1309 int state = cnv->get_state();
1310 if( state != convoi_t::INITIAL && state != convoi_t::ENTERING_DEPOT && state != convoi_t::SELF_DESTRUCT ) {
1311 add_to_schedule_cache( cnv, false );
1312 }
1313 }
1314 }
1315 }
1316
1317 // now add all unbound convois
1318 player_t * required_vehicle_owner = NULL;
1319 if (player_showed_on_map != -1) {
1320 required_vehicle_owner = world->get_player(player_showed_on_map);
1321 }
1322 FOR( vector_tpl<convoihandle_t>, cnv, world->convoys() ) {
1323 if( !cnv.is_bound() || cnv->get_line().is_bound() ) {
1324 // not there or already part of a line
1325 continue;
1326 }
1327 if( required_vehicle_owner!= NULL && required_vehicle_owner != cnv->get_owner() ) {
1328 continue;
1329 }
1330 if( transport_type_showed_on_map != simline_t::line ) {
1331 if( transport_type_showed_on_map != simline_t::waytype_to_linetype(cnv->front()->get_waytype()) ) {
1332 continue;
1333 }
1334 }
1335 int state = cnv->get_state();
1336 if( state != convoi_t::INITIAL && state != convoi_t::ENTERING_DEPOT && state != convoi_t::SELF_DESTRUCT ) {
1337 if( !is_matching_freight_catg(cnv->get_goods_catg_index()) ) {
1338 continue;
1339 }
1340 add_to_schedule_cache( cnv, false );
1341 }
1342 }
1343 }
1344 /************ ATTENTION: The schedule pointers schedule in the line segments ******************
1345 ************ are invalid after this point! ******************/
1346 }
1347 //end MAP_LINES
1348
1349 bool showing_schedule = false;
1350 if( mode & MAP_LINES ) {
1351 showing_schedule = !schedule_cache.empty();
1352 }
1353 else {
1354 schedule_cache.clear();
1355 colore_idx = 0;
1356 last_schedule_counter = world->get_schedule_counter()-1;
1357 }
1358
1359 // since the schedule whitens out the background, we have to draw it first
1360 int offset = 1;
1361 koord last_start(0,0), last_end(0,0);
1362 bool diagonal = false;
1363 if( showing_schedule ) {
1364 // lighten background
1365 if( isometric ) {
1366 // isometric => lighten in three parts
1367
1368 scr_coord p1 = map_to_screen_coord( koord(0,0) );
1369 scr_coord p2 = map_to_screen_coord( koord( world->get_size().x, 0 ) );
1370 scr_coord p3 = map_to_screen_coord( koord( world->get_size().x, world->get_size().y ) );
1371 scr_coord p4 = map_to_screen_coord( koord( 0, world->get_size().y ) );
1372
1373 // top and bottom part
1374 const int toplines = min( p4.y, p2.y );
1375 for( scr_coord_val y = 0; y < toplines; y++ ) {
1376 display_blend_wh_rgb( pos.x+p1.x-2*y, pos.y+y, 4*y+4, 1, color_idx_to_rgb(COL_WHITE), 75 );
1377 display_blend_wh_rgb( pos.x+p3.x-2*y, pos.y+p3.y-y-1, 4*y+4, 1, color_idx_to_rgb(COL_WHITE), 75 );
1378 }
1379 // center area
1380 if( p1.x < p3.x ) {
1381 for( scr_coord_val y = toplines; y < p3.y-toplines; y++ ) {
1382 display_blend_wh_rgb( pos.x+(y-toplines)*2, pos.y+y, 4*toplines+4, 1, color_idx_to_rgb(COL_WHITE), 75 );
1383 }
1384 }
1385 else {
1386 for( scr_coord_val y = toplines; y < p3.y-toplines; y++ ) {
1387 display_blend_wh_rgb( pos.x+(y-toplines)*2, pos.y+p3.y-y-1, 4*toplines+4, 1, color_idx_to_rgb(COL_WHITE), 75 );
1388 }
1389 }
1390 }
1391 else {
1392 // easier with rectangular maps ...
1393 display_blend_wh_rgb( cur_off.x+pos.x, cur_off.y+pos.y, map_data->get_width(), map_data->get_height(), color_idx_to_rgb(COL_WHITE), 75 );
1394 }
1395
1396 scr_coord k1,k2;
1397 // DISPLAY STATIONS AND AIRPORTS: moved here so station spots are not overwritten by lines drawn
1398 FOR( vector_tpl<line_segment_t>, seg, schedule_cache ) {
1399
1400 uint8 color = seg.colorcount;
1401 if( event_get_last_control_shift()==2 || current_cnv.is_bound() ) {
1402 // on control / single convoi use only player colors
1403 static uint8 last_color = color;
1404 color = seg.player->get_player_color1()+1;
1405 // all lines same thickness if same color
1406 if( color == last_color ) {
1407 offset = 0;
1408 }
1409 last_color = color;
1410 }
1411 if( seg.start != last_start || seg.end != last_end ) {
1412 last_start = seg.start;
1413 k1 = map_to_screen_coord( seg.start );
1414 k1 += pos;
1415 last_end = seg.end;
1416 k2 = map_to_screen_coord( seg.end );
1417 k2 += pos;
1418 // use same diagonal for all parallel segments
1419 diagonal = seg.start_diagonal;
1420 }
1421 // and finally draw ...
1422 line_segment_draw( seg.waytype, k1, seg.start_offset*offset, k2, seg.end_offset*offset, diagonal, color_idx_to_rgb(color) );
1423 }
1424 }
1425
1426 // display station information here (even without overlay)
1427 halthandle_t display_station;
1428 // only fill cache if needed
1429 if( mode & MAP_MODE_HALT_FLAGS && stop_cache.empty() ) {
1430 if( mode & MAP_ORIGIN ) {
1431 FOR( const vector_tpl<halthandle_t>, halt, haltestelle_t::get_alle_haltestellen() ) {
1432 if( halt->get_pax_enabled() || halt->get_mail_enabled() ) {
1433 stop_cache.append( halt );
1434 }
1435 }
1436 }
1437 else if( mode & MAP_TRANSFER ) {
1438 FOR( const vector_tpl<halthandle_t>, halt, haltestelle_t::get_alle_haltestellen() ) {
1439 if( halt->is_transfer(goods_manager_t::INDEX_PAS) || halt->is_transfer(goods_manager_t::INDEX_MAIL) ) {
1440 stop_cache.append( halt );
1441 }
1442 else {
1443 // good transfer?
1444 bool transfer = false;
1445 for( int i=goods_manager_t::INDEX_NONE+1 && !transfer; i<=goods_manager_t::get_max_catg_index(); i ++ ) {
1446 transfer = halt->is_transfer( i );
1447 }
1448 if( transfer ) {
1449 stop_cache.append( halt );
1450 }
1451 }
1452 }
1453 }
1454 else if( mode&MAP_STATUS || mode&MAP_SERVICE || mode&MAP_WAITING || mode&MAP_WAITCHANGE ) {
1455 FOR( const vector_tpl<halthandle_t>, halt, haltestelle_t::get_alle_haltestellen() ) {
1456 stop_cache.append( halt );
1457 }
1458 }
1459 }
1460 // now draw stop cache
1461 // if needed to get new values
1462 sint32 new_max_waiting_change = 1;
1463 FOR( vector_tpl<halthandle_t>, station, stop_cache ) {
1464
1465 if( !station.is_bound() ) {
1466 // maybe deleted in the meanwhile
1467 continue;
1468 }
1469
1470 int radius = 0;
1471 PIXVAL color;
1472 int diagonal_dist = 0;
1473 scr_coord temp_stop = map_to_screen_coord( station->get_basis_pos() );
1474 temp_stop = temp_stop + pos;
1475
1476 if( mode & MAP_STATUS ) {
1477 color = station->get_status_farbe();
1478 radius = number_to_radius( station->get_capacity(0) );
1479 }
1480 else if( mode & MAP_SERVICE ) {
1481 const sint32 service = (sint32)station->get_finance_history( 1, HALT_CONVOIS_ARRIVED );
1482 if( service > max_service ) {
1483 max_service = service;
1484 }
1485 color = calc_severity_color_log( service, max_service );
1486 radius = log2( (uint32)( (service << 7) / max_service ) );
1487 }
1488 else if( mode & MAP_WAITING ) {
1489 const sint32 waiting = (sint32)station->get_finance_history( 0, HALT_WAITING );
1490 if( waiting > max_waiting ) {
1491 max_waiting = waiting;
1492 }
1493 color = calc_severity_color_log( waiting, max_waiting );
1494 radius = number_to_radius( waiting );
1495 }
1496 else if( mode & MAP_WAITCHANGE ) {
1497 const sint32 waiting_diff = (sint32)(station->get_finance_history( 0, HALT_WAITING ) - station->get_finance_history( 1, HALT_WAITING ));
1498 if( waiting_diff > new_max_waiting_change ) {
1499 new_max_waiting_change = waiting_diff;
1500 }
1501 if( waiting_diff < -new_max_waiting_change ) {
1502 new_max_waiting_change = -waiting_diff;
1503 }
1504 const sint32 span = max(new_max_waiting_change,max_waiting_change);
1505 const sint32 diff = waiting_diff + span;
1506 color = calc_severity_color( diff, span*2 ) ;
1507 radius = number_to_radius( abs(waiting_diff) );
1508 }
1509 else if( mode & MAP_ORIGIN ) {
1510 if( !station->get_pax_enabled() && !station->get_mail_enabled() ) {
1511 continue;
1512 }
1513 const sint32 pax_origin = (sint32)(station->get_finance_history( 1, HALT_HAPPY ) + station->get_finance_history( 1, HALT_UNHAPPY ) + station->get_finance_history( 1, HALT_NOROUTE ));
1514 if( pax_origin > max_origin ) {
1515 max_origin = pax_origin;
1516 }
1517 color = calc_severity_color_log( pax_origin, max_origin );
1518 radius = number_to_radius( pax_origin );
1519 }
1520 else if( mode & MAP_TRANSFER ) {
1521 const sint32 transfer = (sint32)(station->get_finance_history( 1, HALT_ARRIVED ) + station->get_finance_history( 1, HALT_DEPARTED ));
1522 if( transfer > max_transfer ) {
1523 max_transfer = transfer;
1524 }
1525 color = calc_severity_color_log( transfer, max_transfer );
1526 radius = number_to_radius( transfer );
1527 }
1528 else {
1529 const int stype = station->get_station_type();
1530 color = color_idx_to_rgb(station->get_owner()->get_player_color1()+3);
1531
1532 // invalid=0, loadingbay=1, railstation = 2, dock = 4, busstop = 8, airstop = 16, monorailstop = 32, tramstop = 64, maglevstop=128, narrowgaugestop=256
1533 if( stype > 0 ) {
1534 radius = 1;
1535 if( stype & ~(haltestelle_t::loadingbay | haltestelle_t::busstop | haltestelle_t::tramstop) ) {
1536 radius = 3;
1537 }
1538 }
1539 // with control, show only circles
1540 if( event_get_last_control_shift()!=2 ) {
1541 // else elongate them ...
1542 const int key = station->get_basis_pos().x + station->get_basis_pos().y * world->get_size().x;
1543 diagonal_dist = waypoint_hash.get( key ).get_count();
1544 if( diagonal_dist ) {
1545 diagonal_dist--;
1546 }
1547 diagonal_dist = (diagonal_dist*3)-1;
1548 }
1549
1550 // show the mode of transport of the station
1551 if( skinverwaltung_t::station_type ) {
1552 int icon = 0;
1553 for( int type=0; type<9; type++ ) {
1554 if( (stype>>type)&1 ) {
1555 image_id img = skinverwaltung_t::station_type->get_image_id(type);
1556 if( img!=IMG_EMPTY ) {
1557 display_color_img( img, temp_stop.x+diagonal_dist+4+(icon/2)*12, temp_stop.y+diagonal_dist+4+(icon&1)*12, station->get_owner()->get_player_nr(), false, false );
1558 icon++;
1559 }
1560 }
1561 }
1562 }
1563 else {
1564 if( stype & haltestelle_t::airstop ) {
1565 display_airport( temp_stop.x+diagonal_dist, temp_stop.y+diagonal_dist, color );
1566 }
1567
1568 if( stype & haltestelle_t::dock ) {
1569 display_harbor( temp_stop.x+diagonal_dist, temp_stop.y+diagonal_dist, color );
1570 }
1571 }
1572 }
1573
1574 // avoid too small circles when zoomed out
1575 if( zoom_in > 1 ) {
1576 radius ++;
1577 }
1578
1579 int out_radius = (radius == 0) ? 1 : radius;
1580 display_filled_circle_rgb( temp_stop.x, temp_stop.y, radius, color );
1581 display_circle_rgb( temp_stop.x, temp_stop.y, out_radius, color_idx_to_rgb(COL_BLACK) );
1582 if( diagonal_dist>0 ) {
1583 display_filled_circle_rgb( temp_stop.x+diagonal_dist, temp_stop.y+diagonal_dist, radius, color );
1584 display_circle_rgb( temp_stop.x+diagonal_dist, temp_stop.y+diagonal_dist, out_radius, color_idx_to_rgb(COL_BLACK) );
1585 for( int i=1; i < diagonal_dist; i++ ) {
1586 display_filled_circle_rgb( temp_stop.x+i, temp_stop.y+i, radius, color );
1587 }
1588 out_radius = sqrt_i32( 2*out_radius+1 );
1589 display_direct_line_rgb( temp_stop.x+out_radius, temp_stop.y-out_radius, temp_stop.x+out_radius+diagonal_dist, temp_stop.y-out_radius+diagonal_dist, color_idx_to_rgb(COL_BLACK) );
1590 display_direct_line_rgb( temp_stop.x-out_radius, temp_stop.y+out_radius, temp_stop.x-out_radius+diagonal_dist, temp_stop.y+out_radius+diagonal_dist, color_idx_to_rgb(COL_BLACK) );
1591 }
1592
1593 if( koord_distance( last_world_pos, station->get_basis_pos() ) <= 2 ) {
1594 // draw stop name with an index if close to mouse
1595 display_station = station;
1596 }
1597 }
1598 if( display_station.is_bound() ) {
1599 scr_coord temp_stop = map_to_screen_coord( display_station->get_basis_pos() );
1600 temp_stop = temp_stop + pos;
1601 display_ddd_proportional_clip( temp_stop.x + 10, temp_stop.y + 7, proportional_string_width( display_station->get_name() ) + 8, 0, color_idx_to_rgb(display_station->get_owner()->get_player_color1()+3), color_idx_to_rgb(COL_WHITE), display_station->get_name(), false );
1602 }
1603 max_waiting_change = new_max_waiting_change; // update waiting tendencies
1604
1605 // if we do not do this here, vehicles would erase the town names
1606 // ADD: if CRTL key is pressed, temporary show the name
1607 if( mode & MAP_TOWN ) {
1608 const weighted_vector_tpl<stadt_t*>& staedte = world->get_cities();
1609 const PIXVAL col = color_idx_to_rgb(showing_schedule ? COL_BLACK : COL_WHITE);
1610
1611 FOR( weighted_vector_tpl<stadt_t*>, const stadt, staedte ) {
1612 const char * name = stadt->get_name();
1613
1614 scr_coord p = map_to_screen_coord( stadt->get_pos() );
1615 p += pos;
1616 display_proportional_clip_rgb( p.x, p.y, name, ALIGN_LEFT, col, true );
1617 }
1618 }
1619
1620 // draw city limit
1621 if( mode & MAP_CITYLIMIT ) {
1622
1623 // for all cities
1624 FOR( weighted_vector_tpl<stadt_t*>, const stadt, world->get_cities() ) {
1625 koord k[4];
1626 k[0] = stadt->get_linksoben(); // top left
1627 k[2] = stadt->get_rechtsunten(); // bottom right
1628 k[1] = koord(k[0].x, k[2].y); // bottom left
1629 k[3] = koord(k[2].x, k[0].y); // top right
1630
1631 k[0] += koord(0, -1); // top left
1632 k[2] += koord(1, 0); // bottom right
1633 k[3] += koord(1, -1); // top right
1634
1635 // calculate and draw the rotated coordinates
1636 scr_coord c[4];
1637 for(uint i=0; i<lengthof(c); i++) {
1638 c[i] = map_to_screen_coord(k[i]) + pos;
1639 }
1640
1641 display_direct_line_dotted_rgb( c[0].x, c[0].y, c[1].x, c[1].y, 3, 3, color_idx_to_rgb(COL_ORANGE) );
1642 display_direct_line_dotted_rgb( c[1].x, c[1].y, c[2].x, c[2].y, 3, 3, color_idx_to_rgb(COL_ORANGE) );
1643 display_direct_line_dotted_rgb( c[2].x, c[2].y, c[3].x, c[3].y, 3, 3, color_idx_to_rgb(COL_ORANGE) );
1644 display_direct_line_dotted_rgb( c[3].x, c[3].y, c[0].x, c[0].y, 3, 3, color_idx_to_rgb(COL_ORANGE) );
1645 }
1646 }
1647
1648 // since we do iterate the tourist info list, this must be done here
1649 // find tourist spots
1650 if( mode & MAP_TOURIST ) {
1651 FOR( weighted_vector_tpl<gebaeude_t*>, const gb, world->get_attractions() ) {
1652 if( gb->get_first_tile() == gb ) {
1653 scr_coord gb_pos = map_to_screen_coord( gb->get_pos().get_2d() );
1654 gb_pos = gb_pos + pos;
1655 int const pax = gb->get_passagier_level();
1656 if( max_tourist_ziele < pax ) {
1657 max_tourist_ziele = pax;
1658 }
1659 PIXVAL color = calc_severity_color_log(gb->get_passagier_level(), max_tourist_ziele);
1660 int radius = max( (number_to_radius( pax*4 )*zoom_in)/zoom_out, 1 );
1661 display_filled_circle_rgb( gb_pos.x, gb_pos.y, radius, color );
1662 display_circle_rgb( gb_pos.x, gb_pos.y, radius, color_idx_to_rgb(COL_BLACK) );
1663 }
1664 // otherwise larger attraction will be shown more often ...
1665 }
1666 }
1667
1668 if( mode & MAP_FACTORIES ) {
1669 FOR( slist_tpl<fabrik_t*>, const f, world->get_fab_list() ) {
1670 // find top-left tile position
1671 koord3d fab_tl_pos = f->get_pos();
1672 if (grund_t *gr = world->lookup(f->get_pos())) {
1673 if (gebaeude_t* gb = gr->find<gebaeude_t>()) {
1674 fab_tl_pos = gb->get_pos() - gb->get_tile()->get_offset();
1675 }
1676 }
1677 scr_coord fab_pos = map_to_screen_coord( fab_tl_pos.get_2d() );
1678 fab_pos = fab_pos + pos;
1679 koord size = f->get_desc()->get_building()->get_size(f->get_rotate());
1680 sint16 x_size = max( 5, size.x*zoom_in );
1681 sint16 y_size = max( 5, size.y*zoom_in );
1682 display_fillbox_wh_clip_rgb( fab_pos.x-1, fab_pos.y-1, x_size+2, y_size+2, color_idx_to_rgb(COL_BLACK), false );
1683 display_fillbox_wh_clip_rgb( fab_pos.x, fab_pos.y, x_size, y_size, f->get_color(), false );
1684 }
1685 }
1686
1687 if( mode & MAP_DEPOT ) {
1688 FOR( slist_tpl<depot_t*>, const d, depot_t::get_depot_list() ) {
1689 if( d->get_owner() == world->get_active_player() ) {
1690 scr_coord depot_pos = map_to_screen_coord( d->get_pos().get_2d() );
1691 depot_pos = depot_pos + pos;
1692 // offset of one to avoid
1693 static uint8 depot_typ_to_color[19]={ COL_ORANGE, COL_YELLOW, COL_RED, 0, 0, 0, 0, 0, 0, COL_PURPLE, COL_DARK_RED, COL_DARK_ORANGE, 0, 0, 0, 0, 0, 0, COL_LIGHT_RED };
1694 display_filled_circle_rgb( depot_pos.x, depot_pos.y, 4, color_idx_to_rgb(depot_typ_to_color[d->get_typ() - obj_t::bahndepot]) );
1695 display_circle_rgb( depot_pos.x, depot_pos.y, 4, color_idx_to_rgb(COL_BLACK) );
1696 }
1697 }
1698 }
1699
1700 // zoom/resize "selection box" in map
1701 // this must be rotated by 45 degree (sin45=cos45=0,5*sqrt(2)=0.707...)
1702 const sint16 raster=get_tile_raster_width();
1703
1704 // calculate and draw the rotated coordinates
1705 koord ij = world->get_viewport()->get_world_position();
1706 const koord diff = koord( display_get_width()/(2*raster), display_get_height()/raster );
1707
1708 koord view[4];
1709 scr_coord test[4];
1710 // default coordinates - may be off if screen shows high mountains
1711 view[0] = ij + koord( -diff.y+diff.x, -diff.y-diff.x );
1712 view[1] = ij + koord( -diff.y-diff.x, -diff.y+diff.x );
1713 view[2] = ij + koord( diff.y-diff.x, diff.y+diff.x );
1714 view[3] = ij + koord( diff.y+diff.x, diff.y-diff.x );
1715
1716 // try to find tile under the four corners of the screen
1717 test[0] = scr_coord(display_get_width(),0);
1718 test[1] = scr_coord(0,0);
1719 test[2] = scr_coord(0,display_get_height());
1720 test[3] = scr_coord(display_get_width(),display_get_height());
1721
1722 for(int i=0; i<4; i++) {
1723 sint32 dummy1, dummy2;
1724 if (grund_t *gr = world->get_viewport()->get_ground_on_screen_coordinate( test[i], dummy1, dummy2 ) ) {
1725 view[i] = gr->get_pos().get_2d();
1726 }
1727 }
1728
1729 scr_coord c[4];
1730 // translate to coordinates in the minimap
1731 for( int i=0; i<4; i++ ) {
1732 c[i] = map_to_screen_coord( view[i] ) + pos;
1733 }
1734 for( int i=0; i<4; i++ ) {
1735 display_direct_line_rgb( c[i].x, c[i].y, c[(i+1)%4].x, c[(i+1)%4].y, color_idx_to_rgb(COL_YELLOW));
1736 }
1737
1738 if( !showing_schedule ) {
1739 // Add factory name tooltips and draw factory connections, if on a factory
1740 const fabrik_t* const fab = (mode & MAP_FACTORIES)
1741 ? draw_factory_connections(event_get_last_control_shift() & 1 ? color_idx_to_rgb(COL_RED) : color_idx_to_rgb(COL_WHITE), pos)
1742 : get_factory_near( last_world_pos, false );
1743
1744 if(fab) {
1745 scr_coord fabpos = map_to_screen_coord( fab->get_pos().get_2d() );
1746 scr_coord boxpos = fabpos + scr_coord(10, 0);
1747 const char * name = translator::translate(fab->get_name());
1748 int name_width = proportional_string_width(name)+8;
1749 boxpos.x = clamp( boxpos.x, 0, 0+get_size().w-name_width );
1750 boxpos += pos;
1751 display_ddd_proportional_clip(boxpos.x, boxpos.y, name_width, 0, color_idx_to_rgb(10), color_idx_to_rgb(COL_WHITE), name, true);
1752 }
1753 }
1754 }
1755
1756
set_selected_city(const stadt_t * _city)1757 void minimap_t::set_selected_city( const stadt_t* _city )
1758 {
1759 if( selected_city != _city ) {
1760 selected_city = _city;
1761 if( _city ) {
1762 pax_destinations_last_change = _city->get_pax_destinations_new_change();
1763 }
1764 calc_map();
1765 }
1766 }
1767
1768
rdwr(loadsave_t * file)1769 void minimap_t::rdwr(loadsave_t *file)
1770 {
1771 file->rdwr_short(zoom_out);
1772 file->rdwr_short(zoom_in);
1773 file->rdwr_bool(isometric);
1774 }
1775
1776
is_matching_freight_catg(const minivec_tpl<uint8> & goods_catg_index)1777 bool minimap_t::is_matching_freight_catg(const minivec_tpl<uint8> &goods_catg_index)
1778 {
1779 // does this line/convoi has a matching freight
1780 if( freight_type_group_index_showed_on_map == goods_manager_t::passengers ) {
1781 return goods_catg_index.is_contained(goods_manager_t::INDEX_PAS);
1782 }
1783 else if( freight_type_group_index_showed_on_map == goods_manager_t::mail ) {
1784 return goods_catg_index.is_contained(goods_manager_t::INDEX_MAIL);
1785 }
1786 else if( freight_type_group_index_showed_on_map == goods_manager_t::none ) {
1787 // all freights but not pax or mail
1788 for( uint8 i = 0; i < goods_catg_index.get_count(); i++ ) {
1789 if( goods_catg_index[i] > goods_manager_t::INDEX_NONE ) {
1790 return true;
1791 }
1792 }
1793 return false;
1794 }
1795 else if( freight_type_group_index_showed_on_map != NULL ) {
1796 for( uint8 i = 0; i < goods_catg_index.get_count(); i++ ) {
1797 if( goods_catg_index[i] == freight_type_group_index_showed_on_map->get_catg_index() ) {
1798 return true;
1799 }
1800 }
1801 return false;
1802 }
1803 // NULL show all but obey modes
1804 if( mode & MAP_PASSENGER ) {
1805 return goods_catg_index.is_contained(goods_manager_t::INDEX_PAS);
1806 }
1807 else if( mode & MAP_MAIL ) {
1808 return goods_catg_index.is_contained(goods_manager_t::INDEX_MAIL);
1809 }
1810 else if( mode & MAP_FREIGHT ) {
1811 // all freights but not pax or mail
1812 for( uint8 i = 0; i < goods_catg_index.get_count(); i++ ) {
1813 if( goods_catg_index[i]>2 ) {
1814 return true;
1815 }
1816 }
1817 return false;
1818 }
1819 // all true
1820 return true;
1821 }
1822