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