1 /*
2  * Base class for grounds in simutrans.
3  * by Hj. Malthaner
4  */
5 
6 #include <string.h>
7 
8 #include "../simcolor.h"
9 #include "../simconst.h"
10 #include "../simdebug.h"
11 #include "../simdepot.h"
12 #include "../display/simgraph.h"
13 #include "../display/viewport.h"
14 #include "../simhalt.h"
15 #include "../display/simimg.h"
16 #include "../player/simplay.h"
17 #include "../gui/simwin.h"
18 #include "../simworld.h"
19 
20 #include "../bauer/wegbauer.h"
21 
22 #include "../descriptor/ground_desc.h"
23 #include "../descriptor/building_desc.h"
24 #include "../descriptor/crossing_desc.h"
25 #include "../descriptor/tunnel_desc.h"
26 #include "../descriptor/way_desc.h"
27 
28 #include "../dataobj/freelist.h"
29 #include "../dataobj/loadsave.h"
30 #include "../dataobj/translator.h"
31 #include "../dataobj/environment.h"
32 
33 #include "../obj/baum.h"
34 #include "../obj/bruecke.h"
35 #include "../obj/crossing.h"
36 #include "../obj/groundobj.h"
37 #include "../obj/label.h"
38 #include "../obj/leitung2.h"	// for construction of new ways ...
39 #include "../obj/roadsign.h"
40 #include "../obj/signal.h"
41 #include "../obj/tunnel.h"
42 #include "../obj/wayobj.h"
43 
44 #include "../gui/ground_info.h"
45 #include "../gui/minimap.h"
46 
47 #include "../tpl/inthashtable_tpl.h"
48 
49 #include "../utils/cbuffer_t.h"
50 
51 #include "../vehicle/simpeople.h"
52 
53 #include "wege/kanal.h"
54 #include "wege/maglev.h"
55 #include "wege/monorail.h"
56 #include "wege/narrowgauge.h"
57 #include "wege/runway.h"
58 #include "wege/schiene.h"
59 #include "wege/strasse.h"
60 #include "wege/weg.h"
61 
62 #include "fundament.h"
63 #include "grund.h"
64 #include "tunnelboden.h"
65 #include "wasser.h"
66 
67 
68 /**
69  * Pointer to the world of this ground. Static to conserve space.
70  * Change to instance variable once more than one world is available.
71  * @author Hj. Malthaner
72  */
73 karte_ptr_t grund_t::welt;
74 volatile bool grund_t::show_grid = false;
75 
76 uint8 grund_t::offsets[4]={0,1,2/*illegal!*/,2};
77 
78 sint8 grund_t::underground_level = 127;
79 uint8 grund_t::underground_mode = ugm_none;
80 
81 
82 // ---------------------- text handling from here ----------------------
83 
84 
85 /**
86  * Table of ground texts
87  * @author Hj. Malthaner
88  */
89 static inthashtable_tpl<uint64, char*> ground_texts;
90 
91 #define get_ground_text_key(k) ( (uint64)(k).x + ((uint64)(k).y << 16) + ((uint64)(k).z << 32) )
92 
93 // and the reverse operation
94 #define get_ground_koord3d_key(key) koord3d( (key) & 0x00007FFF, ((key)>>16) & 0x00007fff, (key)>>32 )
95 
set_text(const char * text)96 void grund_t::set_text(const char *text)
97 {
98 	if (text==NULL  &&  !get_flag(has_text)) {
99 		// no text to delete
100 		return;
101 	}
102 	const uint64 n = get_ground_text_key(pos);
103 	if(  text  ) {
104 		char *new_text = strdup(text);
105 		free(ground_texts.remove(n));
106 		ground_texts.put(n, new_text);
107 		set_flag(has_text);
108 	}
109 	else if(  get_flag(has_text)  ) {
110 		char *txt=ground_texts.remove(n);
111 		free(txt);
112 		clear_flag(has_text);
113 	}
114 	set_flag(dirty);
115 	welt->set_dirty();
116 }
117 
118 
get_text() const119 const char *grund_t::get_text() const
120 {
121 	const char *result = 0;
122 	if(  get_flag(has_text)  ) {
123 		result = ground_texts.get( get_ground_text_key(pos) );
124 		if(result==NULL) {
125 			return "undef";
126 		}
127 		assert(result);
128 	}
129 	return result;
130 }
131 
132 
text_farbe() const133 FLAGGED_PIXVAL grund_t::text_farbe() const
134 {
135 	// if this ground belongs to a halt, the color should reflect the halt owner, not the ground owner!
136 	// Now, we use the color of label_t owner
137 	if(is_halt()  &&  find<label_t>()==NULL) {
138 		// only halt label
139 		const halthandle_t halt = get_halt();
140 		const player_t *player=halt->get_owner();
141 		if(player) {
142 			return PLAYER_FLAG|color_idx_to_rgb(player->get_player_color1()+4);
143 		}
144 	}
145 	// else color according to current owner
146 	else if(obj_bei(0)) {
147 		const player_t *player = obj_bei(0)->get_owner(); // for cityhall
148 		const label_t* l = find<label_t>();
149 		if(l) {
150 			player = l->get_owner();
151 		}
152 		if(player) {
153 			return PLAYER_FLAG|color_idx_to_rgb(player->get_player_color1()+4);
154 		}
155 	}
156 
157 	return SYSCOL_TEXT_HIGHLIGHT;
158 }
159 
160 
161 // ---------------- init, rdwr, and destruct from here ---------------------
162 
163 
operator new(size_t s)164 void* grund_t::operator new(size_t s)
165 {
166 	return freelist_t::gimme_node(s);
167 }
168 
169 
operator delete(void * p,size_t s)170 void grund_t::operator delete(void* p, size_t s)
171 {
172 	return freelist_t::putback_node(s, p);
173 }
174 
175 
rdwr(loadsave_t * file)176 void grund_t::rdwr(loadsave_t *file)
177 {
178 	koord k = pos.get_2d();
179 
180 	// water saves its correct height => no need to save grid heights anymore
181 	sint8 z = welt->lookup_hgt( k ); // save grid height for water tiles - including partial water tiles
182 	sint8 z_w = welt->get_water_hgt( k );
183 	if(  !(get_typ() == grund_t::boden  ||  get_typ() == grund_t::wasser) || pos.z > z_w || z > z_w  ) {
184 		z = pos.z; // all other tiles save ground height
185 	}
186 
187 	planquadrat_t *plan = welt->access( k );
188 	uint8 climate_data = plan->get_climate() + (plan->get_climate_corners() << 4);
189 
190 	xml_tag_t g( file, "grund_t" );
191 	if(file->is_version_less(101, 0)) {
192 		pos.rdwr(file);
193 		z_w = welt->get_groundwater();
194 	}
195 	else if(  file->is_version_less(112, 7)  ) {
196 		file->rdwr_byte(z);
197 		pos.z = get_typ() == grund_t::wasser ? welt->get_groundwater() : z;
198 		z_w = welt->get_groundwater();
199 	}
200 	else {
201 		file->rdwr_byte(z);
202 		file->rdwr_byte(z_w);
203 		if(  file->is_loading()  &&  !is_water()  &&  !welt->lookup_kartenboden( k )  &&  z < z_w  ) {
204 			// partially in water, restore correct ground height while keeping grid height
205 			// if kartenboden doesn't exist we will become it
206 			pos.z = z_w;
207 		}
208 		else if(  file->is_loading()  ) {
209 			pos.z = get_typ() == grund_t::wasser ? z_w : z;
210 		}
211 		file->rdwr_byte(climate_data);
212 		plan->set_climate((climate)(climate_data & 7));
213 		plan->set_climate_corners((climate_data >> 4));
214 	}
215 
216 	if(  file->is_loading()  &&  file->is_version_less(112, 7)  ) {
217 		// convert heights from old single height saved game - water already at correct height
218 		pos.z = get_typ() == grund_t::wasser ? pos.z : pos.z * env_t::pak_height_conversion_factor;
219 		z = z * env_t::pak_height_conversion_factor;
220 	}
221 
222 	if(file->is_saving()) {
223 		const char *text = get_text();
224 		file->rdwr_str(text);
225 	}
226 	else {
227 		const char *text = 0;
228 		file->rdwr_str(text);
229 		if(text) {
230 			set_text(text);
231 			guarded_free(const_cast<char *>(text));
232 		}
233 	}
234 
235 	if(file->is_version_less(99, 7)) {
236 		bool label;
237 		file->rdwr_bool(label);
238 		if(label) {
239 			objlist.add( new label_t(pos, welt->get_player(0), get_text() ) );
240 		}
241 	}
242 
243 	sint8 owner_n=-1;
244 	if(file->is_version_less(99, 5)) {
245 		file->rdwr_byte(owner_n);
246 	}
247 
248 	if(file->is_version_atleast(88, 9)) {
249 		uint8 sl = slope;
250 		if(  file->is_version_less(112, 7)  &&  file->is_saving()  ) {
251 			// truncate double slopes to single slopes, better than nothing
252 			sl = min( corner_sw(slope), 1 ) + min( corner_se(slope), 1 ) * 2 + min( corner_ne(slope), 1 ) * 4 + min( corner_nw(slope), 1 ) * 8;
253 		}
254 		file->rdwr_byte(sl);
255 		if(  file->is_loading()  ) {
256 			slope = sl;
257 		}
258 	}
259 	else {
260 		// safe init for old version
261 		slope = 0;
262 	}
263 
264 	if(  file->is_loading()  ) {
265 		if(  file->is_version_less(112, 7)  ) {
266 			// convert slopes from old single height saved game
267 			slope = (scorner_sw(slope) + scorner_se(slope) * 3 + scorner_ne(slope) * 9 + scorner_nw(slope) * 27) * env_t::pak_height_conversion_factor;
268 		}
269 		if(  !ground_desc_t::double_grounds  ) {
270 			// truncate double slopes to single slopes
271 			slope = min( corner_sw(slope), 1 ) + min( corner_se(slope), 1 ) * 3 + min( corner_ne(slope), 1 ) * 9 + min( corner_nw(slope), 1 ) * 27;
272 		}
273 	}
274 
275 	// restore grid
276 	if(  file->is_loading()  ) {
277 		// for south/east map edges we need to restore more than one point
278 		if(  pos.x == welt->get_size().x-1  &&  pos.y == welt->get_size().y-1  ) {
279 			sint8 z_southeast = z;
280 			if(  get_typ() == grund_t::wasser  &&  z_southeast > z_w  ) {
281 				z_southeast = z_w;
282 			}
283 			else {
284 				z_southeast += corner_se(slope);
285 			}
286 			welt->set_grid_hgt( k + koord(1,1), z_southeast );
287 		}
288 		if(  pos.x == welt->get_size().x-1  ) {
289 			sint8 z_east = z;
290 			if(  get_typ() == grund_t::wasser  &&  z_east > z_w  ) {
291 				z_east = z_w;
292 			}
293 			else {
294 				z_east += corner_ne(slope);
295 			}
296 			welt->set_grid_hgt( k + koord(1,0), z_east );
297 		}
298 		if(  pos.y == welt->get_size().y-1  ) {
299 			sint8 z_south = z;
300 			if(  get_typ() == grund_t::wasser  &&  z_south > z_w  ) {
301 				z_south = z_w;
302 			}
303 			else {
304 				z_south += corner_sw(slope);
305 			}
306 			welt->set_grid_hgt( k + koord(0,1), z_south );
307 		}
308 
309 		if(  get_typ() == grund_t::wasser  &&  z > z_w  ) {
310 			z = z_w;
311 		}
312 		else {
313 			z += corner_nw(slope);
314 		}
315 		welt->set_grid_hgt( k, z );
316 		welt->set_water_hgt( k, z_w );
317 	}
318 
319 	// loading ways from here on
320 	if(file->is_loading()) {
321 		waytype_t wtyp;
322 		int i = -1;
323 		do {
324 			wtyp = (waytype_t)file->rd_obj_id();
325 			weg_t *weg = NULL;
326 
327 			if(++i < 2) {
328 				switch(wtyp) {
329 					default:
330 #if MSG_LEVEL
331 						if(  wtyp != invalid_wt  ) {
332 							dbg->error( "grund_t::rdwr()", "invalid waytype %i!", (int)wtyp );
333 							wtyp = invalid_wt;
334 						}
335 #endif
336 						break;
337 
338 					case road_wt:
339 						weg = new strasse_t(file);
340 						break;
341 
342 					case monorail_wt:
343 						weg = new monorail_t(file);
344 						break;
345 
346 					case maglev_wt:
347 						weg = new maglev_t(file);
348 						break;
349 
350 					case narrowgauge_wt:
351 						weg = new narrowgauge_t(file);
352 						break;
353 
354 					case track_wt: {
355 						schiene_t *sch = new schiene_t(file);
356 						if(sch->get_desc()->get_wtyp()==monorail_wt) {
357 							dbg->warning("grund_t::rdwr()", "converting railroad to monorail at (%i,%i)",get_pos().x, get_pos().y);
358 							// compatibility code: Convert to monorail
359 							monorail_t *w= new monorail_t();
360 							w->set_desc(sch->get_desc());
361 							w->set_max_speed(sch->get_max_speed());
362 							w->set_ribi(sch->get_ribi_unmasked());
363 							delete sch;
364 							weg = w;
365 						}
366 						else {
367 							weg = sch;
368 						}
369 					} break;
370 
371 					case tram_wt:
372 						weg = new schiene_t(file);
373 						if(weg->get_desc()->get_styp()!=type_tram) {
374 							weg->set_desc(way_builder_t::weg_search(tram_wt,weg->get_max_speed(),0,type_tram));
375 						}
376 						break;
377 
378 					case water_wt:
379 						// ignore old type dock ...
380 						if(file->is_version_atleast(87, 0)) {
381 							weg = new kanal_t(file);
382 						}
383 						else {
384 							uint8 d8;
385 							sint16 d16;
386 							sint32 d32;
387 
388 							file->rdwr_byte(d8);
389 							file->rdwr_short(d16);
390 							file->rdwr_long(d32);
391 							file->rdwr_long(d32);
392 							file->rdwr_long(d32);
393 							file->rdwr_long(d32);
394 							DBG_MESSAGE("grund_t::rdwr()","at (%i,%i) dock ignored",get_pos().x, get_pos().y);
395 						}
396 						break;
397 
398 					case air_wt:
399 						weg = new runway_t(file);
400 						break;
401 				}
402 
403 				if(weg) {
404 					if(get_typ()==fundament) {
405 						// remove this (but we can not correct the other ways, since possibly not yet loaded)
406 						dbg->error("grund_t::rdwr()","removing way from foundation at %i,%i",pos.x,pos.y);
407 						// we do not delete them, to keep maintenance costs correct
408 					}
409 					else {
410 						assert((flags&has_way2)==0);	// maximum two ways on one tile ...
411 						weg->set_pos(pos);
412 						if(owner_n!=-1) {
413 							weg->set_owner(welt->get_player(owner_n));
414 						}
415 						objlist.add(weg);
416 						if(flags&has_way1) {
417 							flags |= has_way2;
418 						}
419 						flags |= has_way1;
420 					}
421 				}
422 			}
423 		} while(wtyp != invalid_wt);
424 
425 		flags |= dirty;
426 	}
427 	else {
428 		// saving all ways ...
429 		if (weg_t* const w = get_weg_nr(0)) {
430 			file->wr_obj_id(w->get_waytype());
431 			w->rdwr(file);
432 		}
433 		if (weg_t* const w = get_weg_nr(1)) {
434 			file->wr_obj_id(w->get_waytype());
435 			w->rdwr(file);
436 		}
437 		file->wr_obj_id(-1);   // Way end
438 	}
439 
440 	// all objects on this tile
441 	objlist.rdwr(file, get_pos());
442 
443 	// need to add a crossing for old games ...
444 	if (file->is_loading()  &&  ist_uebergang()  &&  !find<crossing_t>(2)) {
445 		const crossing_desc_t *cr_desc = crossing_logic_t::get_crossing( ((weg_t *)obj_bei(0))->get_waytype(), ((weg_t *)obj_bei(1))->get_waytype(), ((weg_t *)obj_bei(0))->get_max_speed(), ((weg_t *)obj_bei(1))->get_max_speed(), 0 );
446 		if(cr_desc==0) {
447 			dbg->fatal("crossing_t::crossing_t()","requested for waytypes %i and %i but nothing defined!", ((weg_t *)obj_bei(0))->get_waytype(), ((weg_t *)obj_bei(1))->get_waytype() );
448 		}
449 		crossing_t *cr = new crossing_t(obj_bei(0)->get_owner(), pos, cr_desc, ribi_t::is_straight_ns(get_weg(cr_desc->get_waytype(1))->get_ribi_unmasked()) );
450 		objlist.add( cr );
451 		crossing_logic_t::add( cr, crossing_logic_t::CROSSING_INVALID );
452 	}
453 }
454 
455 
grund_t(koord3d pos)456 grund_t::grund_t(koord3d pos)
457 {
458 	this->pos = pos;
459 	flags = 0;
460 	set_image(IMG_EMPTY);    // set   flags = dirty;
461 	back_imageid = 0;
462 }
463 
464 
~grund_t()465 grund_t::~grund_t()
466 {
467 	destroy_win((ptrdiff_t)this);
468 
469 	// remove text from table
470 	set_text(NULL);
471 
472 	if(flags&is_halt_flag) {
473 		get_halt()->rem_grund(this);
474 	}
475 }
476 
477 
sort_trees()478 void grund_t::sort_trees()
479 {
480 	if (get_typ() != boden) {
481 		return;
482 	}
483 	uint8 trees = 0, offset = 0;
484 	for(  int i=0;  i<objlist.get_top();  i++  ) {
485 		if (obj_bei(i)->get_typ() == obj_t::baum) {
486 			trees++;
487 			offset = i;
488 		}
489 	}
490 	if(trees > 1) {
491 		objlist.sort_trees(offset-trees+1u, trees);
492 	}
493 }
494 
495 
rotate90()496 void grund_t::rotate90()
497 {
498 	pos.rotate90( welt->get_size().y-1 );
499 	slope = slope_t::rotate90( slope );
500 	// then rotate the things on this tile
501 	uint8 trees = 0, offset = 0;
502 	if(  get_top()==254  ) {
503 		dbg->warning( "grund_t::rotate90()", "Too many stuff on (%s)", pos.get_str() );
504 	}
505 	for(  uint8 i=0;  i<objlist.get_top();  i++  ) {
506 		obj_bei(i)->rotate90();
507 		if (obj_bei(i)->get_typ() == obj_t::baum) {
508 			trees++;
509 			offset = i;
510 		}
511 	}
512 	// if more than one tree on a tile .. resort since offsets changed
513 	if(trees > 1) {
514 		objlist.sort_trees(offset-trees+1u, trees);
515 	}
516 }
517 
518 
519 // after processing the last tile, we recalculate the hashes of the ground texts
finish_rotate90()520 void grund_t::finish_rotate90()
521 {
522 	typedef inthashtable_tpl<uint64, char*> text_map;
523 	text_map ground_texts_rotating;
524 	// first get the old hashes
525 	FOR(text_map, iter, ground_texts) {
526 		koord3d k = get_ground_koord3d_key( iter.key );
527 		k.rotate90( welt->get_size().y-1 );
528 		ground_texts_rotating.put( get_ground_text_key(k), iter.value );
529 	}
530 	ground_texts.clear();
531 	// then transfer all rotated texts
532 	FOR(text_map, const& iter, ground_texts_rotating) {
533 		ground_texts.put(iter.key, iter.value);
534 	}
535 	ground_texts_rotating.clear();
536 }
537 
538 
enlarge_map(sint16,sint16)539 void grund_t::enlarge_map( sint16, sint16 /*new_size_y*/ )
540 {
541 	typedef inthashtable_tpl<uint64, char*> text_map;
542 	text_map ground_texts_enlarged;
543 	// we have recalculate the keys
544 	FOR(text_map, iter, ground_texts) {
545 		koord3d k = get_ground_koord3d_key( iter.key );
546 		ground_texts_enlarged.put( get_ground_text_key(k), iter.value );
547 	}
548 	ground_texts.clear();
549 	// then transfer all texts back
550 	FOR(text_map, const& iter, ground_texts_enlarged) {
551 		ground_texts.put(iter.key, iter.value);
552 	}
553 	ground_texts_enlarged.clear();
554 }
555 
556 
557 // moves all objects from the old to the new grund_t
take_obj_from(grund_t * other_gr)558 void grund_t::take_obj_from(grund_t* other_gr)
559 {
560 	// transfer all things
561 	while( other_gr->get_top() ) {
562 		objlist.add( other_gr->obj_remove_top() );
563 	}
564 	// transfer the way flags
565 	if(other_gr->get_flag(has_way1)) {
566 		flags |= has_way1;
567 		other_gr->clear_flag(has_way1);
568 	}
569 	if(other_gr->get_flag(has_way2)) {
570 		flags |= has_way2;
571 		other_gr->clear_flag(has_way2);
572 	}
573 }
574 
575 
open_info_window()576 void grund_t::open_info_window()
577 {
578 	if(env_t::ground_info  ||  hat_wege()) {
579 		create_win(new grund_info_t(this), w_info, (ptrdiff_t)this);
580 	}
581 }
582 
583 
info(cbuffer_t & buf) const584 void grund_t::info(cbuffer_t& buf) const
585 {
586 	if(!is_water()) {
587 		if(flags&has_way1) {
588 			// bridges / tunnels only carry dummy ways
589 			if(!ist_tunnel()  &&  !ist_bruecke()) {
590 				buf.append(translator::translate(get_weg_nr(0)->get_name()));
591 				buf.append("\n");
592 			}
593 			obj_bei(0)->info(buf);
594 			// creator of bridge or tunnel graphic
595 			const char* maker = NULL;
596 			if (ist_tunnel()) {
597 				if (tunnel_t* tunnel = find<tunnel_t>(1)) {
598 					maker = tunnel->get_desc()->get_copyright();
599 				}
600 			}
601 			if (ist_bruecke()) {
602 				if (bruecke_t* bridge = find<bruecke_t>(1)) {
603 					maker = bridge->get_desc()->get_copyright();
604 				}
605 			}
606 			if (maker) {
607 				buf.printf(translator::translate("Constructed by %s"), maker);
608 				buf.append("\n");
609 			}
610 			buf.append("\n");
611 			// second way
612 			if(flags&has_way2) {
613 				buf.append(translator::translate(get_weg_nr(1)->get_name()));
614 				buf.append("\n");
615 				obj_bei(1)->info(buf);
616 				buf.append("\n");
617 				if(ist_uebergang()) {
618 					crossing_t* crossing = find<crossing_t>(2);
619 					crossing->info(buf);
620 				}
621 			}
622 		}
623 	}
624 
625 	buf.printf("%s\n%s", get_name(), translator::translate( ground_desc_t::get_climate_name_from_bit( welt->get_climate( get_pos().get_2d() ) ) ) );
626 #if MSG_LEVEL >= 4
627 	buf.printf("\nflags $%0X", flags );
628 	buf.printf("\n\npos: (%s)",pos.get_str());
629 	buf.printf("\nslope: %i",get_grund_hang());
630 	buf.printf("\nback0: %i",abs(back_imageid)%11);
631 	buf.printf("\nback1: %i",(abs(back_imageid)/11)+11);
632 	if(  get_weg_nr(0)  ) {
633 		buf.printf("\nway slope %i", (int)get_weg_hang() );
634 	}
635 	if(get_weg_ribi_unmasked(water_wt)) {
636 		buf.printf("\nwater ribi: %i",get_weg_ribi_unmasked(water_wt));
637 	}
638 	if(is_water()) {
639 		buf.printf("\ncanal ribi: %i", ((const wasser_t*)this)->get_canal_ribi());
640 	}
641 	buf.printf("\ndraw_as_obj= %i",(flags&draw_as_obj)!=0);
642 #endif
643 }
644 
645 
set_halt(halthandle_t halt)646 void grund_t::set_halt(halthandle_t halt)
647 {
648 	bool add = halt.is_bound();
649 	if(  add  ) {
650 		// ok, we want to add a stop: first check if it can apply to water
651 		if(  get_weg_ribi(water_wt)  ||  is_water()  ||  (ist_karten_boden()  &&  welt->get_climate(pos.get_2d())==water_climate)  ) {
652 			add = (halt->get_station_type() & haltestelle_t::dock) > 0;
653 		}
654 	}
655 	// then add or remove halt flag
656 	// and record the halt
657 	if(  add  ) {
658 		this_halt = halt;
659 		flags |= is_halt_flag|dirty;
660 	}
661 	else {
662 		this_halt = halthandle_t();
663 		flags &= ~is_halt_flag;
664 		flags |= dirty;
665 	}
666 }
667 
668 
669 
get_halt() const670 halthandle_t grund_t::get_halt() const
671 {
672 	return (flags&is_halt_flag) ? this_halt : halthandle_t();
673 }
674 
675 
676 // ----------------------- image calculation stuff from here ------------------
677 
678 
calc_image()679 void grund_t::calc_image()
680 {
681 	// will automatically recalculate ways ...
682 	objlist.calc_image();
683 	// since bridges may alter images of ways, this order is needed!
684 	calc_image_internal( false );
685 }
686 
687 
set_underground_mode(const uint8 ugm,const sint8 level)688 void grund_t::set_underground_mode(const uint8 ugm, const sint8 level)
689 {
690 	underground_mode = ugm;
691 	switch(ugm) {
692 		case ugm_all:
693 			underground_level = -128;
694 			break;
695 		case ugm_level:
696 			underground_level = level;
697 			break;
698 		case ugm_none:
699 		default:
700 			underground_mode = ugm_none;
701 			underground_level = 127;
702 	}
703 }
704 
705 
get_back_image(int leftback) const706 image_id grund_t::get_back_image(int leftback) const
707 {
708 	if(back_imageid==0) {
709 		return IMG_EMPTY;
710 	}
711 	uint16 back_image = abs(back_imageid);
712 	back_image = leftback ? (back_image / grund_t::WALL_IMAGE_COUNT) + grund_t::WALL_IMAGE_COUNT : back_image % grund_t::WALL_IMAGE_COUNT;
713 	if(back_imageid<0) {
714 		return ground_desc_t::fundament->get_image(back_image);
715 	}
716 	else {
717 		return ground_desc_t::slopes->get_image(back_image);
718 	}
719 }
720 
721 
722 // with double height ground tiles!
723 // can also happen with single height tiles
get_back_image_from_diff(sint8 h1,sint8 h2)724 static inline uint8 get_back_image_from_diff(sint8 h1, sint8 h2)
725 {
726 	sint8 min_diff = min( h1, h2 );
727 	while(  min_diff > 2  ||  (min_diff > 0  &&  h1 != h2)  ) {
728 		h1 -= min_diff > 1 ? 2 : 1;
729 		h2 -= min_diff > 1 ? 2 : 1;
730 		min_diff -= 2;
731 	}
732 
733 	if(h1*h2<0) {
734 		// middle slop of double height
735 		return h1<0 ? 9 : 10;
736 	}
737 	else {
738 		return (h1>0?h1:0)+(h2>0?h2:0)*3;
739 	}
740 }
741 
742 /**
743 * if ground is deleted mark the old spot as dirty
744 */
mark_image_dirty()745 void grund_t::mark_image_dirty()
746 {
747 	// see obj_t::mark_image_dirty
748 	if(imageid!=IMG_EMPTY) {
749 		const scr_coord scr_pos = welt->get_viewport()->get_screen_coord(koord3d(pos.get_2d(),get_disp_height()));
750 		display_mark_img_dirty( imageid, scr_pos.x, scr_pos.y );
751 	}
752 }
753 
754 // artificial walls from here on ...
calc_back_image(const sint8 hgt,const slope_t::type slope_this)755 void grund_t::calc_back_image(const sint8 hgt, const slope_t::type slope_this)
756 {
757 	// full underground mode or not ground -> no back image, no need for draw_as_obj
758 	if(  underground_mode == ugm_all  ||  !ist_karten_boden()  ) {
759 		clear_flag(grund_t::draw_as_obj);
760 		this->back_imageid = 0;
761 		return;
762 	}
763 
764 	// store corner heights sw, nw, ne scaled to screen dimensions
765 	const sint16 scale_z_step = tile_raster_scale_y(TILE_HEIGHT_STEP,64);
766 	const sint16 scale_y_step = 64/2;
767 
768 	sint16 corners[grund_t::BACK_CORNER_COUNT] = {(sint16)(scale_z_step*(hgt + corner_sw(slope_this))),
769 	                     (sint16)(scale_z_step*(hgt + corner_nw(slope_this))),
770 	                     (sint16)(scale_z_step*(hgt + corner_ne(slope_this)))};
771 	sint16 corners_add[grund_t::BACK_CORNER_COUNT] = {0,0,0}; // extra height of possible back-image
772 
773 	// now calculate back image
774 	sint8 back_imageid=0;
775 	bool is_building = get_typ()==grund_t::fundament;
776 	const bool isvisible = is_visible();
777 	bool fence[grund_t::BACK_WALL_COUNT] = {false, false};
778 	const koord k = get_pos().get_2d();
779 
780 	clear_flag(grund_t::draw_as_obj);
781 	weg_t const* w;
782 	if(  (  (w = get_weg_nr(0))  &&  w->get_desc()->is_draw_as_obj()  )  ||
783 			(  (w = get_weg_nr(1))  &&  w->get_desc()->is_draw_as_obj()  )
784 	  ) {
785 		set_flag(grund_t::draw_as_obj);
786 	}
787 
788 	for(  size_t i=0;  i<grund_t::BACK_WALL_COUNT;  i++  ) {
789 		// now enter the left/back two height differences
790 		if(  const grund_t *gr=welt->lookup_kartenboden(k + koord::nsew[(i-1)&3])  ) {
791 			const uint8 back_height = min(corner_nw(slope_this),(i==0?corner_sw(slope_this):corner_ne(slope_this)));
792 
793 			const sint16 left_hgt=gr->get_disp_height()-back_height;
794 			const sint8 slope=gr->get_disp_slope();
795 
796 			const uint8 corner_a = (i==0?corner_sw(slope_this):corner_nw(slope_this))-back_height;
797 			const uint8 corner_b = (i==0?corner_nw(slope_this):corner_ne(slope_this))-back_height;
798 
799 			sint8 diff_from_ground_1 = left_hgt+(i==0?corner_se(slope):corner_sw(slope))-hgt;
800 			sint8 diff_from_ground_2 = left_hgt+(i==0?corner_ne(slope):corner_se(slope))-hgt;
801 
802 			if (underground_mode==ugm_level) {
803 				const bool gr_is_visible = gr->is_visible();
804 
805 				// if exactly one of (this) and (gr) is visible, show full walls
806 				if ( isvisible && !gr_is_visible){
807 					diff_from_ground_1 += 1;
808 					diff_from_ground_2 += 1;
809 					set_flag(grund_t::draw_as_obj);
810 					fence[i] = corner_a==corner_b;
811 				}
812 				else if ( !isvisible && gr_is_visible){
813 					diff_from_ground_1 = max(diff_from_ground_1, 1);
814 					diff_from_ground_2 = max(diff_from_ground_2, 1);
815 				}
816 				// avoid walls that cover the tunnel mounds
817 				if ( gr_is_visible && (gr->get_typ()==grund_t::tunnelboden) && ist_karten_boden() && gr->get_pos().z==underground_level
818 				     && corner_se( gr->get_grund_hang() ) > 0 /* se corner */) {
819 					diff_from_ground_1 = 0;
820 					diff_from_ground_2 = 0;
821 				}
822 				if ( isvisible && (get_typ()==grund_t::tunnelboden) && ist_karten_boden() && pos.z==underground_level
823 					&& corner_nw( get_grund_hang() ) > 0 /* nw corner */) {
824 
825 					if ( (i==0)  ^  (corner_sw( get_grund_hang() )==0) ) {
826 						diff_from_ground_1 = 0;
827 						diff_from_ground_2 = 0;
828 					}
829 				}
830 			}
831 
832 			// up slope hiding something ...
833 			if(diff_from_ground_1-corner_a<0  ||  diff_from_ground_2-corner_b<0)  {
834 				if(  corner_a==corner_b  ) {
835 					// ok, we need a fence here, if there is not a vertical bridgehead
836 					weg_t const* w;
837 					fence[i] = !(w = get_weg_nr(0)) || (
838 						!(w->get_ribi_unmasked() & ribi_t::nsew[(i-1)&3]) &&
839 						(!(w = get_weg_nr(1)) || !(w->get_ribi_unmasked() & ribi_t::nsew[(i-1)&3]))
840 					);
841 
842 					// no fences between water tiles or between invisible tiles
843 					if(  fence[i]  &&  ( (is_water() && gr->is_water()) || (!isvisible && !gr->is_visible()) )  ) {
844 						fence[i] = false;
845 					}
846 				}
847 			}
848 			// any height difference AND something to see?
849 			if(  (diff_from_ground_1-corner_a>0  ||  diff_from_ground_2-corner_b>0)
850 				&&  (diff_from_ground_1>0  ||  diff_from_ground_2>0)  ) {
851 				back_imageid += get_back_image_from_diff( diff_from_ground_1, diff_from_ground_2 )*(i==0?1:grund_t::WALL_IMAGE_COUNT);
852 				is_building |= gr->get_typ()==grund_t::fundament;
853 			}
854 			// update corner heights
855 			if (diff_from_ground_1 > corner_a) {
856 				corners_add[i] = max(corners_add[i], scale_z_step * (diff_from_ground_1-corner_a));
857 			}
858 			if (diff_from_ground_2 > corner_b) {
859 				corners_add[i+1] = max(corners_add[i+1], scale_z_step * (diff_from_ground_2 - corner_b));
860 			}
861 		}
862 	}
863 
864 	for(uint i=0; i<grund_t::BACK_CORNER_COUNT; i++) {
865 		corners[i] += corners_add[i];
866 	}
867 
868 	// now test more tiles behind whether they are hidden by this tile
869 	static const koord  testdir[grund_t::BACK_CORNER_COUNT] = { koord(-1,0), koord(-1,-1), koord(0,-1) };
870 	for(sint16 step = 0; step<grund_t::MAXIMUM_HIDE_TEST_DISTANCE  &&  !get_flag(draw_as_obj); step ++) {
871 		sint16 test[grund_t::BACK_CORNER_COUNT];
872 
873 		for(uint i=0; i<grund_t::BACK_CORNER_COUNT; i++) {
874 			test[i] = corners[i] + 1;
875 		}
876 
877 		for(uint i=0; i<grund_t::BACK_CORNER_COUNT; i++) {
878 			if(  const grund_t *gr=welt->lookup_kartenboden(k + testdir[i] - koord(step,step))  ) {
879 				sint16 h = gr->get_disp_height()*scale_z_step;
880 				sint8 s = gr->get_disp_slope();
881 				// tile should hide anything in tunnel portal behind (tile not in direction of tunnel)
882 				if (step == 0  &&  ((i&1)==0)  &&  s  &&  gr->ist_tunnel()) {
883 					if ( ribi_t::reverse_single(ribi_type(s)) != ribi_type(testdir[i])) {
884 						s = slope_t::flat;
885 					}
886 				}
887 				// take backimage into account, take base-height of back image as corner heights
888 				sint8 bb = abs(gr->back_imageid);
889 				sint8 lh = 2, rh = 2; // height of start of back image
890 				if (bb  &&  bb<grund_t::BIID_ENCODE_FENCE_OFFSET) {
891 					if (bb % grund_t::WALL_IMAGE_COUNT) {
892 						lh = min(corner_sw(s), corner_nw(s));
893 					}
894 					if (bb / grund_t::WALL_IMAGE_COUNT) {
895 						rh = min(corner_ne(s), corner_nw(s));
896 					}
897 				}
898 				if (i>0) {
899 					test[i-1] = min(test[i-1], h + min(corner_sw(s),lh)*scale_z_step + (2-i)*scale_y_step + step*scale_y_step );
900 				}
901 				test[i] = min(test[i], h + min( corner_se(s)*scale_z_step, min(min(corner_nw(s),lh),rh)*scale_z_step + scale_y_step) + step*scale_y_step );
902 				if (i<2) {
903 					test[i+1] = min(test[i+1], h + min(corner_ne(s),rh)*scale_z_step + i*scale_y_step + step*scale_y_step);
904 				}
905 			}
906 		}
907 		if (test[0] < corners[0]  ||  test[1] < corners[1]  ||  test[2] < corners[2]) {
908 			// we hide at least 1 thing behind
909 			set_flag(draw_as_obj);
910 			break;
911 		}
912 		else if (test[0] > corners[0]  &&  test[1] > corners[1]  &&  test[2] > corners[2]) {
913 			// we cannot hide anything anymore
914 			break;
915 		}
916 	}
917 
918 	// needs a fence?
919 	if(back_imageid==0) {
920 		sint8 fence_offset = fence[0] + 2 * fence[1];
921 		if(fence_offset) {
922 			back_imageid = grund_t::BIID_ENCODE_FENCE_OFFSET + fence_offset;
923 		}
924 	}
925 	this->back_imageid = (is_building!=0)? -back_imageid : back_imageid;
926 }
927 
928 
929 #ifdef MULTI_THREAD
display_boden(const sint16 xpos,const sint16 ypos,const sint16 raster_tile_width,const sint8 clip_num,const bool force_show_grid) const930 void grund_t::display_boden(const sint16 xpos, const sint16 ypos, const sint16 raster_tile_width, const sint8 clip_num, const bool force_show_grid ) const
931 #else
932 void grund_t::display_boden(const sint16 xpos, const sint16 ypos, const sint16 raster_tile_width) const
933 #endif
934 {
935 	static const uint16 wall_image_offset[grund_t::BACK_WALL_COUNT] = {0, 11};
936 
937 	const bool dirty = get_flag(grund_t::dirty);
938 	const koord k = get_pos().get_2d();
939 
940 	// here: we are either ground(kartenboden) or visible
941 	const bool visible = !ist_karten_boden()  ||  is_karten_boden_visible();
942 
943 	// walls, fences, foundations etc
944 	if(back_imageid!=0) {
945 		const uint8 abs_back_imageid = abs(back_imageid);
946 		const bool artificial = back_imageid < 0;
947 		if(abs_back_imageid > grund_t::BIID_ENCODE_FENCE_OFFSET) {
948 			// fence before a drop
949 			const sint16 offset = -tile_raster_scale_y( TILE_HEIGHT_STEP*corner_nw(get_grund_hang()), raster_tile_width);
950 			const uint16 typ = abs_back_imageid - grund_t::BIID_ENCODE_FENCE_OFFSET - 1 + (artificial ? grund_t::FENCE_IMAGE_COUNT : 0);
951 			display_normal( ground_desc_t::fences->get_image(typ), xpos, ypos + offset, 0, true, dirty CLIP_NUM_PAR );
952 		}
953 		else {
954 			// artificial slope
955 			const uint16 back_image[grund_t::BACK_WALL_COUNT] = {(uint16)(abs_back_imageid % grund_t::WALL_IMAGE_COUNT), (uint16)(abs_back_imageid / grund_t::WALL_IMAGE_COUNT)};
956 
957 			// choose foundation or natural slopes
958 			const ground_desc_t *sl_draw = artificial ? ground_desc_t::fundament : ground_desc_t::slopes;
959 
960 			const sint8 disp_slope = get_disp_slope();
961 			// first draw left, then back slopes
962 			for(  size_t i=0;  i<grund_t::BACK_WALL_COUNT;  i++  ) {
963 				const uint8 back_height = min(i==0?corner_sw(disp_slope):corner_ne(disp_slope),corner_nw(disp_slope));
964 
965 				if (back_height + get_disp_height() > underground_level) {
966 					continue;
967 				}
968 
969 				sint16 yoff = tile_raster_scale_y( -TILE_HEIGHT_STEP*back_height, raster_tile_width );
970 				if(  back_image[i]  ) {
971 					// Draw extra wall images for walls that cannot be represented by a image.
972 					grund_t *gr = welt->lookup_kartenboden( k + koord::nsew[(i-1)&3] );
973 					if(  gr  ) {
974 						// for left we test corners 2 and 3 (east), for back we use 1 and 2 (south)
975 						const sint8 gr_slope = gr->get_disp_slope();
976 						uint8 corner_a = corner_se(gr_slope);
977 						uint8 corner_b = i==0?corner_ne(gr_slope):corner_sw(gr_slope);
978 
979 						// at least one level of solid wall between invisible and visible tiles
980 						if (!visible  &&  gr->is_visible()) {
981 							corner_a = max(1, corner_a);
982 							corner_b = max(1, corner_b);
983 						}
984 
985 						sint16 hgt_diff = gr->get_disp_height() - get_disp_height() + min( corner_a, corner_b ) - back_height
986 							+ ((underground_mode == ugm_level  &&  gr->pos.z > underground_level) ? 1 : 0);
987 
988 						while(  hgt_diff > 2  ||  (hgt_diff > 0  &&  corner_a != corner_b)  ) {
989 							uint16 img_index = grund_t::WALL_IMAGE_COUNT * 2 + (hgt_diff>1) + 2 * (uint16)i;
990 							if( sl_draw->get_image( img_index ) == IMG_EMPTY ) {
991 								img_index = 4 + 4 * (hgt_diff>1) + grund_t::WALL_IMAGE_COUNT * (uint16)i;
992 							}
993 							display_normal( sl_draw->get_image( img_index ), xpos, ypos + yoff, 0, true, dirty CLIP_NUM_PAR );
994 							yoff     -= tile_raster_scale_y( TILE_HEIGHT_STEP * (hgt_diff > 1 ? 2 : 1), raster_tile_width );
995 							hgt_diff -= 2;
996 						}
997 					}
998 					display_normal( sl_draw->get_image( back_image[i] + wall_image_offset[i] ), xpos, ypos + yoff, 0, true, dirty CLIP_NUM_PAR );
999 				}
1000 			}
1001 		}
1002 	}
1003 
1004 	// ground
1005 	image_id image = get_image();
1006 	if(image==IMG_EMPTY) {
1007 		// only check for forced redraw (of marked ... )
1008 		if(dirty) {
1009 			mark_rect_dirty_clip( xpos, ypos + raster_tile_width / 2, xpos + raster_tile_width - 1, ypos + raster_tile_width - 1 CLIP_NUM_PAR );
1010 		}
1011 	}
1012 	else {
1013 		if(get_typ()!=wasser) {
1014 			// show image if tile is visible
1015 			if (visible)  {
1016 				display_normal( get_image(), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1017 #ifdef SHOW_FORE_GRUND
1018 				if (get_flag(grund_t::draw_as_obj)) {
1019 					display_blend( get_image(), xpos, ypos, 0, color_idx_to_rgb(COL_RED) | OUTLINE_FLAG |TRANSPARENT50_FLAG, true, dirty CLIP_NUM_PAR );
1020 				}
1021 #endif
1022 				//display climate transitions - only needed if below snowline (snow_transition>0)
1023 				//need to process whole tile for all heights anyway as water transitions are needed for all heights
1024 				const planquadrat_t * plan = welt->access( k );
1025 				uint8 climate_corners = plan->get_climate_corners();
1026 				const sint8 snow_transition = welt->get_snowline() - pos.z;
1027 				weg_t *weg = get_weg(road_wt);
1028 				if(  climate_corners != 0  &&  (!weg  ||  !weg->hat_gehweg())  ) {
1029 					uint8 water_corners = 0;
1030 
1031 					// get neighbour corner heights
1032 					sint8 neighbour_height[8][4];
1033 					welt->get_neighbour_heights( k, neighbour_height );
1034 
1035 					//look up neighbouring climates
1036 					climate neighbour_climate[8];
1037 					for(  int i = 0;  i < 8;  i++  ) {
1038 						koord k_neighbour = k + koord::neighbours[i];
1039 						if(  !welt->is_within_limits(k_neighbour)  ) {
1040 							k_neighbour = welt->get_closest_coordinate(k_neighbour);
1041 						}
1042 						neighbour_climate[i] = welt->get_climate( k_neighbour );
1043 					}
1044 
1045 					climate climate0 = plan->get_climate();
1046 					slope_t::type slope_corner = get_grund_hang();
1047 
1048 					// get transition climate - look for each corner in turn
1049 					for(  int i = 0;  i < 4;  i++  ) {
1050 						sint8 corner_height = get_hoehe() + (slope_corner % 3);
1051 
1052 						climate transition_climate = climate0;
1053 						climate min_climate = arctic_climate;
1054 
1055 						for(  int j = 1;  j < 4;  j++ ) {
1056 							if(  corner_height == neighbour_height[(i * 2 + j) & 7][(i + j) & 3]) {
1057 								climate climatej = neighbour_climate[(i * 2 + j) & 7];
1058 								climatej > transition_climate ? transition_climate = climatej : 0;
1059 								climatej < min_climate ? min_climate = climatej : 0;
1060 							}
1061 						}
1062 
1063 						if(  min_climate == water_climate  ) {
1064 							water_corners += 1 << i;
1065 						}
1066 						if(  (climate_corners >> i) & 1  &&  !is_water()  &&  snow_transition > 0  ) {
1067 							// looks up sw, se, ne, nw for i=0...3
1068 							// we compare with tile either side (e.g. for sw, w and s) and pick highest one
1069 							if(  transition_climate > climate0  ) {
1070 								uint8 overlay_corners = 1 << i;
1071 								slope_t::type slope_corner_se = slope_corner;
1072 								for(  int j = i + 1;  j < 4;  j++  ) {
1073 									slope_corner_se /= 3;
1074 
1075 									// now we check to see if any of remaining corners have same climate transition (also using highest of course)
1076 									// if so we combine into this overlay layer
1077 									if(  (climate_corners >> j) & 1  ) {
1078 										climate compare = climate0;
1079 										for(  int k = 1;  k < 4;  k++  ) {
1080 											corner_height = get_hoehe() + (slope_corner_se % 3);
1081 											if(  corner_height == neighbour_height[(j * 2 + k) & 7][(j + k) & 3]) {
1082 												climate climatej = neighbour_climate[(j * 2 + k) & 7];
1083 												climatej > compare ? compare = climatej : 0;
1084 											}
1085 										}
1086 
1087 										if(  transition_climate == compare  ) {
1088 											overlay_corners += 1 << j;
1089 											climate_corners -= 1 << j;
1090 										}
1091 									}
1092 								}
1093 								// overlay transition climates
1094 								display_alpha( ground_desc_t::get_climate_tile( transition_climate, slope ), ground_desc_t::get_alpha_tile( slope, overlay_corners ), ALPHA_GREEN | ALPHA_BLUE, xpos, ypos, 0, 0, true, dirty CLIP_NUM_PAR );
1095 							}
1096 						}
1097 						slope_corner /= 3;
1098 					}
1099 					// finally overlay any water transition
1100 					if(  water_corners  ) {
1101 						if(  slope  ) {
1102 							display_alpha( ground_desc_t::get_water_tile(slope, wasser_t::stage), ground_desc_t::get_beach_tile( slope, water_corners ), ALPHA_RED, xpos, ypos, 0, 0, true, dirty|wasser_t::change_stage CLIP_NUM_PAR );
1103 							if(  ground_desc_t::shore  ) {
1104 								// additional shore image
1105 								image_id shore = ground_desc_t::shore->get_image(slope,snow_transition<=0);
1106 								if(  shore==IMG_EMPTY  &&  snow_transition<=0  ) {
1107 									shore = ground_desc_t::shore->get_image(slope,0);
1108 								}
1109 								display_normal( shore, xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1110 							}
1111 						}
1112 						else {
1113 							// animate
1114 							display_alpha( ground_desc_t::sea->get_image(0,wasser_t::stage), ground_desc_t::get_beach_tile( slope, water_corners ), ALPHA_RED, xpos, ypos, 0, 0, true, dirty|wasser_t::change_stage CLIP_NUM_PAR );
1115 						}
1116 					}
1117 				}
1118 
1119 				//display snow transitions if required
1120 				if(  slope != 0  &&  (!weg  ||  !weg->hat_gehweg())  ) {
1121 					switch(  snow_transition  ) {
1122 						case 1: {
1123 							display_alpha( ground_desc_t::get_snow_tile(slope), ground_desc_t::get_alpha_tile(slope), ALPHA_GREEN | ALPHA_BLUE, xpos, ypos, 0, 0, true, dirty CLIP_NUM_PAR );
1124 							break;
1125 						}
1126 						case 2: {
1127 							if(  slope_t::max_diff(slope) > 1  ) {
1128 								display_alpha( ground_desc_t::get_snow_tile(slope), ground_desc_t::get_alpha_tile(slope), ALPHA_BLUE, xpos, ypos, 0, 0, true, dirty CLIP_NUM_PAR );
1129 							}
1130 							break;
1131 						}
1132 					}
1133 				}
1134 
1135 				// we show additionally a grid
1136 				// for undergroundmode = ugm_all the grid is plotted in display_obj
1137 #ifdef MULTI_THREAD
1138 				if(  show_grid  || force_show_grid  ) {
1139 #else
1140 				if(  show_grid  ){
1141 #endif
1142 					const uint8 hang = get_grund_hang();
1143 					display_normal( ground_desc_t::get_border_image(hang), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1144 				}
1145 			}
1146 		}
1147 		else {
1148 			// take animation into account
1149 			if(  underground_mode != ugm_all  ) {
1150 				display_normal( ground_desc_t::sea->get_image(get_image(),wasser_t::stage), xpos, ypos, 0, true, dirty|wasser_t::change_stage CLIP_NUM_PAR );
1151 			}
1152 			else {
1153 				display_blend( ground_desc_t::sea->get_image(get_image(),wasser_t::stage), xpos, ypos, 0, TRANSPARENT50_FLAG, true, dirty|wasser_t::change_stage CLIP_NUM_PAR );
1154 			}
1155 			return;
1156 		}
1157 	}
1158 	// display ways
1159 	if(  visible  &&  (flags&has_way1)  ){
1160 		const bool clip = (  (flags&draw_as_obj)  ||  !ist_karten_boden()  )  &&  !env_t::simple_drawing;
1161 		const int hgt_step = tile_raster_scale_y( TILE_HEIGHT_STEP, raster_tile_width );
1162 		for(  uint8 i = 0;  i < offsets[flags / has_way1];  i++  ) {
1163 			obj_t* d = obj_bei(i);
1164 			// clip
1165 			// .. nonconvex n/w if not both n/w are active
1166 			if(  clip  ) {
1167 				const ribi_t::ribi way_ribi = (static_cast<const weg_t*>(d))->get_ribi_unmasked();
1168 				clear_all_poly_clip( CLIP_NUM_VAR );
1169 				const uint8 non_convex = (way_ribi & ribi_t::northwest) == ribi_t::northwest ? 0 : 16;
1170 				if(  way_ribi & ribi_t::west  ) {
1171 					const int dh = corner_nw(get_disp_way_slope()) * hgt_step;
1172 					add_poly_clip( xpos + raster_tile_width / 2 - 1, ypos + raster_tile_width / 2 - dh, xpos - 1, ypos + 3 * raster_tile_width / 4 - dh, ribi_t::west | non_convex CLIP_NUM_PAR );
1173 				}
1174 				if(  way_ribi & ribi_t::north  ) {
1175 					const int dh = corner_nw(get_disp_way_slope()) * hgt_step;
1176 					add_poly_clip( xpos + raster_tile_width, ypos + 3 * raster_tile_width / 4 - dh, xpos + raster_tile_width / 2, ypos + raster_tile_width / 2 - dh, ribi_t::north | non_convex CLIP_NUM_PAR );
1177 				}
1178 				activate_ribi_clip( (way_ribi & ribi_t::northwest) | non_convex  CLIP_NUM_PAR );
1179 			}
1180 			d->display( xpos, ypos CLIP_NUM_PAR );
1181 		}
1182 		// end of clipping
1183 		if(  clip  ) {
1184 			clear_all_poly_clip( CLIP_NUM_VAR );
1185 		}
1186 	}
1187 }
1188 
1189 
1190 void grund_t::display_border( sint16 xpos, sint16 ypos, const sint16 raster_tile_width CLIP_NUM_DEF)
1191 {
1192 	if(  pos.z < welt->get_groundwater()  ) {
1193 		// we do not display below water (yet)
1194 		return;
1195 	}
1196 
1197 	const sint16 hgt_step = tile_raster_scale_y( TILE_HEIGHT_STEP, raster_tile_width);
1198 	static sint8 lookup_hgt[5] = { 6, 3, 0, 1, 2 };
1199 
1200 	if(  pos.y-welt->get_size().y+1 == 0  ) {
1201 		// move slopes to front of tile
1202 		sint16 x = xpos - raster_tile_width/2;
1203 		sint16 y = ypos + raster_tile_width/4 + (pos.z-welt->get_groundwater())*hgt_step;
1204 		// left side border
1205 		sint16 diff = corner_sw(slope)-corner_se(slope);
1206 		image_id slope_img = ground_desc_t::slopes->get_image( lookup_hgt[ 2+diff ]+11 );
1207 		diff = -min(corner_sw(slope),corner_se(slope));
1208 		sint16 zz = pos.z-welt->get_groundwater();
1209 		if(  diff < zz && ((zz-diff)&1)==1  ) {
1210 			display_normal( ground_desc_t::slopes->get_image(15), x, y, 0, true, false CLIP_NUM_PAR );
1211 			y -= hgt_step;
1212 			diff++;
1213 		}
1214 		// ok, now we have the height; since the slopes may end with a fence they are drawn in reverse order
1215 		while(  diff < zz  ) {
1216 			display_normal( ground_desc_t::slopes->get_image(19), x, y, 0, true, false CLIP_NUM_PAR );
1217 			y -= hgt_step*2;
1218 			diff+=2;
1219 		}
1220 		display_normal( slope_img, x, y, 0, true, false CLIP_NUM_PAR );
1221 	}
1222 
1223 	if(  pos.x-welt->get_size().x+1 == 0  ) {
1224 		// move slopes to front of tile
1225 		sint16 x = xpos + raster_tile_width/2;
1226 		sint16 y = ypos + raster_tile_width/4 + (pos.z-welt->get_groundwater())*hgt_step;
1227 		// right side border
1228 		sint16 diff = corner_se(slope)-corner_ne(slope);
1229 		image_id slope_img = ground_desc_t::slopes->get_image( lookup_hgt[ 2+diff ] );
1230 		diff = -min(corner_se(slope),corner_ne(slope));
1231 		sint16 zz = pos.z-welt->get_groundwater();
1232 		if(  diff < zz && ((zz-diff)&1)==1  ) {
1233 			display_normal( ground_desc_t::slopes->get_image(4), x, y, 0, true, false CLIP_NUM_PAR );
1234 			y -= hgt_step;
1235 			diff++;
1236 		}
1237 		// ok, now we have the height; since the slopes may end with a fence they are drawn in reverse order
1238 		while(  diff < zz  ) {
1239 			display_normal( ground_desc_t::slopes->get_image(8), x, y, 0, true, false CLIP_NUM_PAR );
1240 			y -= hgt_step*2;
1241 			diff+=2;
1242 		}
1243 		display_normal( slope_img, x, y, 0, true, false CLIP_NUM_PAR );
1244 	}
1245 }
1246 
1247 
1248 #ifdef MULTI_THREAD
1249 void grund_t::display_if_visible(sint16 xpos, sint16 ypos, const sint16 raster_tile_width, const sint8 clip_num, const bool force_show_grid )
1250 #else
1251 void grund_t::display_if_visible(sint16 xpos, sint16 ypos, const sint16 raster_tile_width)
1252 #endif
1253 {
1254 	if(  !is_karten_boden_visible()  ) {
1255 		// only check for forced redraw (of marked ... )
1256 		if(  get_flag(grund_t::dirty)  ) {
1257 			mark_rect_dirty_clip( xpos, ypos + raster_tile_width / 2, xpos + raster_tile_width - 1, ypos + raster_tile_width - 1 CLIP_NUM_PAR );
1258 		}
1259 		return;
1260 	}
1261 
1262 	if(  env_t::draw_earth_border  &&  (pos.x-welt->get_size().x+1 == 0  ||  pos.y-welt->get_size().y+1 == 0)  ) {
1263 		// the last tile. might need a border
1264 		display_border( xpos, ypos, raster_tile_width CLIP_NUM_PAR );
1265 	}
1266 
1267 	if(!get_flag(grund_t::draw_as_obj)) {
1268 #ifdef MULTI_THREAD
1269 		display_boden( xpos, ypos, raster_tile_width, clip_num, force_show_grid );
1270 #else
1271 		display_boden( xpos, ypos, raster_tile_width );
1272 #endif
1273 	}
1274 }
1275 
1276 
1277 slope_t::type grund_t::get_disp_way_slope() const
1278 {
1279 	if (is_visible()) {
1280 		if (ist_bruecke()) {
1281 			const slope_t::type slope = get_grund_hang();
1282 			if(  slope != 0  ) {
1283 				// for half height slopes we want all corners at 1, for full height all corners at 2
1284 				return (slope & 7) ? slope_t::raised / 2 : slope_t::raised;
1285 			}
1286 			else {
1287 				return get_weg_hang();
1288 			}
1289 		}
1290 		else if (ist_tunnel() && ist_karten_boden()) {
1291 			if (pos.z >= underground_level) {
1292 				return slope_t::flat;
1293 			}
1294 			else {
1295 				return get_grund_hang();
1296 			}
1297 		}
1298 		else {
1299 			return get_grund_hang();
1300 		}
1301 	}
1302 	else {
1303 		return slope_t::flat;
1304 	}
1305 }
1306 
1307 
1308 /**
1309  * The old main display routine. Used for very small tile sizes, where clipping error
1310  * will be only one or two pixels.
1311  *
1312  * Also used in multi-threaded display.
1313  */
1314 void grund_t::display_obj_all_quick_and_dirty(const sint16 xpos, sint16 ypos, const sint16 raster_tile_width, const bool is_global CLIP_NUM_DEF ) const
1315 {
1316 	const bool dirty = get_flag(grund_t::dirty);
1317 	const uint8 start_offset=offsets[flags/has_way1];
1318 
1319 	// here: we are either ground(kartenboden) or visible
1320 	const bool visible = !ist_karten_boden()  ||  is_karten_boden_visible();
1321 
1322 	if(  visible  ) {
1323 		if(  is_global  &&  get_flag( grund_t::marked )  ) {
1324 			const uint8 hang = get_grund_hang();
1325 			display_img_aux( ground_desc_t::get_marker_image( hang, true ), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1326 #ifdef MULTI_THREAD
1327 			objlist.display_obj_quick_and_dirty( xpos, ypos, start_offset CLIP_NUM_PAR );
1328 #else
1329 			objlist.display_obj_quick_and_dirty( xpos, ypos, start_offset, is_global );
1330 #endif
1331 			display_img_aux( ground_desc_t::get_marker_image( hang, false ), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1332 			if(  !ist_karten_boden()  ) {
1333 				const grund_t *gr = welt->lookup_kartenboden(pos.get_2d());
1334 				if(  pos.z > gr->get_hoehe()  ) {
1335 					//display front part of marker for grunds in between
1336 					for(  sint8 z = pos.z - 1;  z > gr->get_hoehe();  z--  ) {
1337 						display_img_aux( ground_desc_t::get_marker_image(0, false), xpos, ypos - tile_raster_scale_y( (z - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, true, true CLIP_NUM_PAR );
1338 					}
1339 					//display front part of marker for ground
1340 					display_img_aux( ground_desc_t::get_marker_image( gr->get_grund_hang(), false ), xpos, ypos - tile_raster_scale_y( (gr->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, true, true CLIP_NUM_PAR );
1341 				}
1342 				else if(  pos.z < gr->get_disp_height()  ) {
1343 					//display back part of marker for grunds in between
1344 					for(  sint8 z = pos.z + 1;  z < gr->get_disp_height();  z++  ) {
1345 						display_img_aux( ground_desc_t::get_border_image(0), xpos, ypos - tile_raster_scale_y( (z - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, true, true CLIP_NUM_PAR );
1346 					}
1347 					//display back part of marker for ground
1348 					const uint8 kbhang = gr->get_grund_hang() | gr->get_weg_hang();
1349 					display_img_aux( ground_desc_t::get_border_image(kbhang), xpos, ypos - tile_raster_scale_y( (gr->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, true, true CLIP_NUM_PAR );
1350 				}
1351 			}
1352 		}
1353 		else {
1354 #ifdef MULTI_THREAD
1355 			objlist.display_obj_quick_and_dirty( xpos, ypos, start_offset CLIP_NUM_PAR );
1356 #else
1357 			objlist.display_obj_quick_and_dirty( xpos, ypos, start_offset, is_global );
1358 #endif
1359 		}
1360 	}
1361 	else { // must be karten_boden
1362 		// in undergroundmode: draw ground grid
1363 		const uint8 hang = underground_mode==ugm_all ? get_grund_hang() : (uint8)slope_t::flat;
1364 		display_img_aux( ground_desc_t::get_border_image(hang), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1365 		// show marker for marked but invisible tiles
1366 		if(  is_global  &&  get_flag(grund_t::marked)  ) {
1367 			display_img_aux( ground_desc_t::get_marker_image( hang, true ), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1368 			display_img_aux( ground_desc_t::get_marker_image( hang, false ), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1369 		}
1370 	}
1371 }
1372 
1373 
1374 /* The main display routine
1375 
1376 Premise:
1377 -- all objects on one tile are sorted such that are no graphical glitches on the tile
1378 -- all glitches happens with objects that are not on the tile but their graphics is (vehicles on tiles before/after stations)
1379 -- ground graphics is already painted when this routine is called
1380 
1381 Idea:
1382 -- clip everything along tile borders that are crossed by ways
1383 -- vehicles of neighboring tiles may overlap our graphic, hence we have to call display-routines of neighboring tiles too
1384 
1385 Algorithm:
1386 0) detect way ribis on the tile and prepare the clipping (see simgraph: add_poly_clip, display_img_pc)
1387 1) display our background (for example station graphics background)
1388 2) display vehicles of w/nw/n neighbors (these are behind this tile) (if we have ways in this direction; clipped along the n and/or w tile border)
1389 3) display background of s/e neighbors (clipped - only pixels that are on our tile are painted)
1390 4) display vehicles on this tile (clipped)
1391 5) display vehicles of ne/e/se/s/sw neighbors
1392 6) display our foreground (foreground image of station/overheadwire piles etc) no clipping
1393 */
1394 void grund_t::display_obj_all(const sint16 xpos, const sint16 ypos, const sint16 raster_tile_width, const bool is_global CLIP_NUM_DEF ) const
1395 {
1396 	if(  env_t::simple_drawing  ) {
1397 		display_obj_all_quick_and_dirty( xpos, ypos, raster_tile_width, is_global CLIP_NUM_PAR );
1398 		return;
1399 	}
1400 
1401 	// end of clipping
1402 	clear_all_poly_clip( CLIP_NUM_VAR );
1403 
1404 	// here: we are either ground(kartenboden) or visible
1405 	const bool visible = !ist_karten_boden()  ||  is_karten_boden_visible();
1406 
1407 	ribi_t::ribi ribi = ribi_t::none;
1408 	if (weg_t const* const w1 = get_weg_nr(0)) {
1409 		ribi |= w1->get_ribi_unmasked();
1410 		if (weg_t const* const w2 = get_weg_nr(1)) {
1411 			ribi |= w2->get_ribi_unmasked();
1412 		}
1413 	}
1414 	else if (is_water()) {
1415 		ribi = (static_cast<const wasser_t*>(this))->get_display_ribi();
1416 	}
1417 
1418 	// no ways? - no clipping needed, avoid all the ribi-checks
1419 	if (ribi==ribi_t::none) {
1420 		// display background
1421 		const uint8 offset_vh = display_obj_bg( xpos, ypos, is_global, true, visible CLIP_NUM_PAR );
1422 		if (visible) {
1423 			// display our vehicles
1424 			const uint8 offset_fg = display_obj_vh( xpos, ypos, offset_vh, ribi, true CLIP_NUM_PAR );
1425 			// foreground
1426 			display_obj_fg( xpos, ypos, is_global, offset_fg CLIP_NUM_PAR );
1427 		}
1428 		return;
1429 	}
1430 
1431 	// ships might be large and could be clipped by vertical walls on our tile
1432 	const bool ontile_se = back_imageid  &&  is_water();
1433 	// get slope of way as displayed
1434 	const uint8 slope = get_disp_way_slope();
1435 	// tunnel portals need special clipping
1436 	bool tunnel_portal = ist_tunnel()  &&  ist_karten_boden();
1437 	if (tunnel_portal) {
1438 		// pretend tunnel portal is connected to the inside
1439 		ribi |= ribi_type(get_grund_hang());
1440 	}
1441 	// clip
1442 	const int hgt_step = tile_raster_scale_y( TILE_HEIGHT_STEP, raster_tile_width);
1443 	// .. nonconvex n/w if not both n/w are active and if we have back image
1444 	//              otherwise our backwall clips into the part of our back image that is drawn by n/w neighbor
1445 	const uint8 non_convex = ((ribi & ribi_t::northwest) == ribi_t::northwest)  &&  back_imageid ? 0 : 16;
1446 	if(  ribi & ribi_t::west  ) {
1447 		const int dh = corner_nw(slope) * hgt_step;
1448 		add_poly_clip( xpos + raster_tile_width / 2 - 1, ypos + raster_tile_width / 2 - dh, xpos - 1, ypos + 3 * raster_tile_width / 4 - dh, ribi_t::west | non_convex CLIP_NUM_PAR );
1449 	}
1450 	if(  ribi & ribi_t::north  ) {
1451 		const int dh = corner_nw(slope) * hgt_step;
1452 		add_poly_clip( xpos + raster_tile_width, ypos + 3 * raster_tile_width / 4 - dh, xpos + raster_tile_width / 2, ypos + raster_tile_width / 2 - dh, ribi_t::north | non_convex CLIP_NUM_PAR );
1453 	}
1454 	if(  ribi & ribi_t::east  ) {
1455 		const int dh = corner_se(slope) * hgt_step;
1456 		add_poly_clip( xpos + raster_tile_width / 2, ypos + raster_tile_width - dh, xpos + raster_tile_width, ypos + 3 * raster_tile_width / 4 - dh, ribi_t::east|16 CLIP_NUM_PAR );
1457 	}
1458 	if(  ribi & ribi_t::south  ) {
1459 		const int dh = corner_se(slope) * hgt_step;
1460 		add_poly_clip( xpos- 1, ypos + 3 * raster_tile_width / 4  - dh, xpos + raster_tile_width / 2 - 1, ypos + raster_tile_width  - dh, ribi_t::south|16  CLIP_NUM_PAR );
1461 	}
1462 
1463 	// display background
1464 	if (!tunnel_portal  ||  slope == 0) {
1465 		activate_ribi_clip( (ribi_t::northwest & ribi) | 16 CLIP_NUM_PAR );	}
1466 	else {
1467 		// also clip along the upper edge of the tunnel tile
1468 		activate_ribi_clip( ribi | 16 CLIP_NUM_PAR );
1469 	}
1470 	// get offset of first vehicle
1471 	const uint8 offset_vh = display_obj_bg( xpos, ypos, is_global, false, visible CLIP_NUM_PAR );
1472 	if(  !visible  ) {
1473 		// end of clipping
1474 		clear_all_poly_clip( CLIP_NUM_VAR );
1475 		return;
1476 	}
1477 	// display vehicles of w/nw/n neighbors
1478 	grund_t *gr_nw = NULL, *gr_ne = NULL, *gr_se = NULL, *gr_sw = NULL;
1479 	if(  ribi & ribi_t::west  ) {
1480 		grund_t *gr;
1481 		if(  get_neighbour( gr, invalid_wt, ribi_t::west )  ) {
1482 			if (!tunnel_portal  ||  gr->is_visible()) {
1483 				gr->display_obj_vh( xpos - raster_tile_width / 2, ypos - raster_tile_width / 4 - tile_raster_scale_y( (gr->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, ribi_t::west, false CLIP_NUM_PAR );
1484 			}
1485 			if(  ribi & ribi_t::south  ) {
1486 				gr->get_neighbour( gr_nw, invalid_wt, ribi_t::north );
1487 			}
1488 			if(  ribi & ribi_t::north  ) {
1489 				gr->get_neighbour( gr_sw, invalid_wt, ribi_t::south );
1490 			}
1491 		}
1492 	}
1493 	if(  ribi & ribi_t::north  ) {
1494 		grund_t *gr;
1495 		if(  get_neighbour( gr, invalid_wt, ribi_t::north )  ) {
1496 			if (!tunnel_portal  ||  gr->is_visible()) {
1497 				gr->display_obj_vh( xpos + raster_tile_width / 2, ypos - raster_tile_width / 4 - tile_raster_scale_y( (gr->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, ribi_t::north, false CLIP_NUM_PAR );
1498 			}
1499 			if(  (ribi & ribi_t::east)  &&  (gr_nw == NULL)  ) {
1500 				gr->get_neighbour( gr_nw, invalid_wt, ribi_t::west );
1501 			}
1502 			if(  (ribi & ribi_t::west)  ) {
1503 				gr->get_neighbour( gr_ne, invalid_wt, ribi_t::east );
1504 			}
1505 		}
1506 	}
1507 	if(  (ribi & ribi_t::northwest)  &&  gr_nw  ) {
1508 		gr_nw->display_obj_vh( xpos, ypos - raster_tile_width / 2 - tile_raster_scale_y( (gr_nw->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, ribi_t::northwest, false CLIP_NUM_PAR );
1509 	}
1510 	// display background s/e
1511 	if(  ribi & ribi_t::east  ) {
1512 		grund_t *gr;
1513 		if(  get_neighbour( gr, invalid_wt, ribi_t::east )  ) {
1514 			const bool draw_other_ways = (flags&draw_as_obj)  ||  (gr->flags&draw_as_obj)  ||  !gr->ist_karten_boden();
1515 			activate_ribi_clip( ribi_t::east|16 CLIP_NUM_PAR );
1516 			gr->display_obj_bg( xpos + raster_tile_width / 2, ypos + raster_tile_width / 4 - tile_raster_scale_y( (gr->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), is_global, draw_other_ways, true CLIP_NUM_PAR );
1517 		}
1518 	}
1519 	if(  ribi & ribi_t::south  ) {
1520 		grund_t *gr;
1521 		if(  get_neighbour( gr, invalid_wt, ribi_t::south )  ) {
1522 			const bool draw_other_ways = (flags&draw_as_obj)  ||  (gr->flags&draw_as_obj)  ||  !gr->ist_karten_boden();
1523 			activate_ribi_clip( ribi_t::south|16 CLIP_NUM_PAR );
1524 			gr->display_obj_bg( xpos - raster_tile_width / 2, ypos + raster_tile_width / 4 - tile_raster_scale_y( (gr->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), is_global, draw_other_ways, true CLIP_NUM_PAR );
1525 		}
1526 	}
1527 	// display our vehicles
1528 	const uint8 offset_fg = display_obj_vh( xpos, ypos, offset_vh, ribi, true CLIP_NUM_PAR );
1529 
1530 	// display vehicles of ne/e/se/s/sw neighbors
1531 	if(  ribi & ribi_t::east  ) {
1532 		grund_t *gr;
1533 		if(  get_neighbour( gr, invalid_wt, ribi_t::east )  ) {
1534 			gr->display_obj_vh( xpos + raster_tile_width / 2, ypos + raster_tile_width / 4 - tile_raster_scale_y( (gr->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, ribi_t::east, ontile_se CLIP_NUM_PAR );
1535 			if(  (ribi & ribi_t::south)  &&  (gr_ne == NULL)  ) {
1536 				gr->get_neighbour( gr_ne, invalid_wt, ribi_t::north );
1537 			}
1538 			if(  (ribi & ribi_t::north)  &&  (gr_se == NULL)  ) {
1539 				gr->get_neighbour( gr_se, invalid_wt, ribi_t::south );
1540 			}
1541 		}
1542 	}
1543 	if(  ribi & ribi_t::south  ) {
1544 		grund_t *gr;
1545 		if(  get_neighbour( gr, invalid_wt, ribi_t::south )  ) {
1546 			gr->display_obj_vh( xpos - raster_tile_width / 2, ypos + raster_tile_width / 4 - tile_raster_scale_y( (gr->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, ribi_t::south, ontile_se CLIP_NUM_PAR );
1547 			if(  (ribi & ribi_t::east)  &&  (gr_sw == NULL)) {
1548 				gr->get_neighbour( gr_sw, invalid_wt, ribi_t::west );
1549 			}
1550 			if(  (ribi & ribi_t::west)  &&  (gr_se == NULL)) {
1551 				gr->get_neighbour( gr_se, invalid_wt, ribi_t::east );
1552 			}
1553 		}
1554 	}
1555 	if(  (ribi & ribi_t::northeast)  &&  gr_ne  ) {
1556 		gr_ne->display_obj_vh( xpos + raster_tile_width, ypos - tile_raster_scale_y( (gr_ne->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, ribi_t::northeast, ontile_se CLIP_NUM_PAR );
1557 	}
1558 	if(  (ribi & ribi_t::southwest)  &&  gr_sw  ) {
1559 		gr_sw->display_obj_vh( xpos - raster_tile_width, ypos - tile_raster_scale_y( (gr_sw->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, ribi_t::southwest, ontile_se CLIP_NUM_PAR );
1560 	}
1561 	if(  (ribi & ribi_t::southeast)  &&  gr_se  ) {
1562 		gr_se->display_obj_vh( xpos, ypos + raster_tile_width / 2 - tile_raster_scale_y( (gr_se->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, ribi_t::southeast, ontile_se CLIP_NUM_PAR );
1563 	}
1564 
1565 	// foreground
1566 	if (tunnel_portal  &&  slope != 0) {
1567 		// clip at the upper edge
1568 		activate_ribi_clip( (ribi & (corner_se(slope)>0 ?  ribi_t::southeast : ribi_t::northwest) )| 16 CLIP_NUM_PAR );
1569 	}
1570 	else {
1571 		clear_all_poly_clip( CLIP_NUM_VAR );
1572 	}
1573 	display_obj_fg( xpos, ypos, is_global, offset_fg CLIP_NUM_PAR );
1574 
1575 	// end of clipping
1576 	if (tunnel_portal) {
1577 		clear_all_poly_clip( CLIP_NUM_VAR );
1578 	}
1579 }
1580 
1581 
1582 uint8 grund_t::display_obj_bg(const sint16 xpos, const sint16 ypos, const bool is_global, const bool draw_ways, const bool visible CLIP_NUM_DEF ) const
1583 {
1584 	const bool dirty = get_flag(grund_t::dirty);
1585 
1586 	if(  visible  ) {
1587 		// display back part of markers
1588 		if(  is_global  &&  get_flag( grund_t::marked )  ) {
1589 			display_normal( ground_desc_t::get_marker_image( get_grund_hang(), true ), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1590 			if(  !ist_karten_boden()  ) {
1591 				const grund_t *gr = welt->lookup_kartenboden(pos.get_2d());
1592 				const sint16 raster_tile_width = get_current_tile_raster_width();
1593 				if(  pos.z < gr->get_disp_height()  ) {
1594 					//display back part of marker for grunds in between
1595 					for(  sint8 z = pos.z + 1;  z < gr->get_disp_height();  z++  ) {
1596 						display_normal( ground_desc_t::get_marker_image(0, true), xpos, ypos - tile_raster_scale_y( (z - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, true, true CLIP_NUM_PAR );
1597 					}
1598 					//display back part of marker for ground
1599 					display_normal( ground_desc_t::get_marker_image( gr->get_grund_hang() | gr->get_weg_hang(), true ), xpos, ypos - tile_raster_scale_y( (gr->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, true, true CLIP_NUM_PAR );
1600 				}
1601 			}
1602 		}
1603 		// display background images of everything but vehicles
1604 		const uint8 start_offset = draw_ways ? 0 : offsets[flags/has_way1];
1605 		return objlist.display_obj_bg( xpos, ypos, start_offset CLIP_NUM_PAR );
1606 	}
1607 	else { // must be karten_boden
1608 		// in undergroundmode: draw ground grid
1609 		const uint8 hang = underground_mode == ugm_all ? get_grund_hang() : (slope_t::type)slope_t::flat;
1610 		display_normal( ground_desc_t::get_border_image(hang), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1611 		// show marker for marked but invisible tiles
1612 		if(  is_global  &&  get_flag( grund_t::marked )  ) {
1613 			display_img_aux( ground_desc_t::get_marker_image( hang, true ), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1614 			display_img_aux( ground_desc_t::get_marker_image( hang, false ), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1615 		}
1616 		return 255;
1617 	}
1618 }
1619 
1620 
1621 uint8 grund_t::display_obj_vh(const sint16 xpos, const sint16 ypos, const uint8 start_offset, const ribi_t::ribi ribi, const bool ontile CLIP_NUM_DEF ) const
1622 {
1623 	return objlist.display_obj_vh( xpos, ypos, start_offset, ribi, ontile CLIP_NUM_PAR );
1624 }
1625 
1626 
1627 void grund_t::display_obj_fg(const sint16 xpos, const sint16 ypos, const bool is_global, const uint8 start_offset CLIP_NUM_DEF ) const
1628 {
1629 	const bool dirty = get_flag(grund_t::dirty);
1630 #ifdef MULTI_THREAD
1631 	objlist.display_obj_fg( xpos, ypos, start_offset CLIP_NUM_PAR );
1632 #else
1633 	objlist.display_obj_fg( xpos, ypos, start_offset, is_global );
1634 #endif
1635 	// display front part of markers
1636 	if(  is_global  &&  get_flag( grund_t::marked )  ) {
1637 		display_normal( ground_desc_t::get_marker_image( get_grund_hang(), false ), xpos, ypos, 0, true, dirty CLIP_NUM_PAR );
1638 		if(  !ist_karten_boden()  ) {
1639 			const grund_t *gr = welt->lookup_kartenboden(pos.get_2d());
1640 			const sint16 raster_tile_width = get_tile_raster_width();
1641 			if(  pos.z > gr->get_hoehe()  ) {
1642 				//display front part of marker for grunds in between
1643 				for(  sint8 z = pos.z - 1;  z > gr->get_hoehe();  z--  ) {
1644 					display_normal( ground_desc_t::get_marker_image( 0, false ), xpos, ypos - tile_raster_scale_y( (z - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, true, true CLIP_NUM_PAR );
1645 				}
1646 				//display front part of marker for ground
1647 				display_normal( ground_desc_t::get_marker_image( gr->get_grund_hang(), false ), xpos, ypos - tile_raster_scale_y( (gr->get_hoehe() - pos.z) * TILE_HEIGHT_STEP, raster_tile_width ), 0, true, true CLIP_NUM_PAR );
1648 			}
1649 		}
1650 	}
1651 }
1652 
1653 
1654 void grund_t::display_overlay(const sint16 xpos, const sint16 ypos)
1655 {
1656 	const bool dirty = get_flag(grund_t::dirty);
1657 #ifdef MULTI_THREAD
1658 	objlist.display_obj_overlay( xpos, ypos );
1659 #endif
1660 	// marker/station text
1661 	if(  get_flag(has_text)  &&  env_t::show_names  ) {
1662 		if(  env_t::show_names&1  ) {
1663 			const char *text = get_text();
1664 			const sint16 raster_tile_width = get_tile_raster_width();
1665 			const int width = proportional_string_width(text)+7;
1666 			int new_xpos = xpos - (width-raster_tile_width)/2;
1667 			FLAGGED_PIXVAL pc = text_farbe();
1668 
1669 			switch( env_t::show_names >> 2 ) {
1670 				case 0:
1671 					display_ddd_proportional_clip( new_xpos, ypos, width, 0, pc, color_idx_to_rgb(COL_BLACK), text, dirty );
1672 					break;
1673 				case 1:
1674 					display_outline_proportional_rgb( new_xpos, ypos-(LINESPACE/2), pc+3, color_idx_to_rgb(COL_BLACK), text, dirty );
1675 					break;
1676 				case 2:
1677 					display_outline_proportional_rgb( new_xpos + LINESPACE + D_H_SPACE, ypos-(LINESPACE/2), color_idx_to_rgb(COL_YELLOW), color_idx_to_rgb(COL_BLACK), text, dirty );
1678 					display_ddd_box_clip_rgb( new_xpos, ypos-(LINESPACE/2), LINESPACE, LINESPACE, pc-2, pc+2 );
1679 					display_fillbox_wh_rgb( new_xpos+1, ypos-(LINESPACE/2)+1, LINESPACE-2, LINESPACE-2, pc, dirty );
1680 					break;
1681 			}
1682 		}
1683 
1684 		// display station waiting information/status
1685 		if(env_t::show_names & 2) {
1686 			const halthandle_t halt = get_halt();
1687 			if(halt.is_bound()  &&  halt->get_basis_pos3d()==pos) {
1688 				halt->display_status(xpos, ypos);
1689 			}
1690 		}
1691 	}
1692 	clear_flag(grund_t::dirty);
1693 }
1694 
1695 
1696 // ---------------- way and wayobj handling -------------------------------
1697 
1698 
1699 ribi_t::ribi grund_t::get_weg_ribi(waytype_t typ) const
1700 {
1701 	weg_t *weg = get_weg(typ);
1702 	return (weg) ? weg->get_ribi() : (ribi_t::ribi)ribi_t::none;
1703 }
1704 
1705 
1706 ribi_t::ribi grund_t::get_weg_ribi_unmasked(waytype_t typ) const
1707 {
1708 	weg_t *weg = get_weg(typ);
1709 	return (weg) ? weg->get_ribi_unmasked() : (ribi_t::ribi)ribi_t::none;
1710 }
1711 
1712 
1713 /**
1714 * If there's a depot here, return this
1715 * @author Volker Meyer
1716 */
1717 depot_t* grund_t::get_depot() const
1718 {
1719 	return dynamic_cast<depot_t *>(first_obj());
1720 }
1721 
1722 
1723 bool grund_t::weg_erweitern(waytype_t wegtyp, ribi_t::ribi ribi)
1724 {
1725 	weg_t   *weg = get_weg(wegtyp);
1726 	if(weg) {
1727 		weg->ribi_add(ribi);
1728 		weg->count_sign();
1729 		if(weg->is_electrified()) {
1730 			wayobj_t *wo = get_wayobj( wegtyp );
1731 			if( (ribi & wo->get_dir()) == 0 ) {
1732 				// ribi isn't set at wayobj;
1733 				for( uint8 i = 0; i < 4; i++ ) {
1734 					// Add ribis to adjacent wayobj.
1735 					if( ribi_t::nsew[i] & ribi ) {
1736 						grund_t *next_gr;
1737 						if( get_neighbour( next_gr, wegtyp, ribi_t::nsew[i] ) ) {
1738 							wayobj_t *wo2 = next_gr->get_wayobj( wegtyp );
1739 							if( wo2 ) {
1740 								wo->set_dir( wo->get_dir() | ribi_t::nsew[i] );
1741 								wo2->set_dir( wo2->get_dir() | ribi_t::backward(ribi_t::nsew[i]) );
1742 							}
1743 						}
1744 					}
1745 				}
1746 			}
1747 		}
1748 		calc_image();
1749 		set_flag(dirty);
1750 		return true;
1751 	}
1752 	return false;
1753 }
1754 
1755 /**
1756  * remove trees and groundobjs on this tile
1757  * called before building way or powerline
1758  * @return costs
1759  */
1760 sint64 grund_t::remove_trees()
1761 {
1762 	sint64 cost=0;
1763 	// remove all trees ...
1764 	while (baum_t* const d = find<baum_t>(0)) {
1765 		// we must mark it by hand, since we want to join costs
1766 		d->mark_image_dirty( get_image(), 0 );
1767 		delete d;
1768 		cost -= welt->get_settings().cst_remove_tree;
1769 	}
1770 	// remove all groundobjs ...
1771 	while (groundobj_t* const d = find<groundobj_t>(0)) {
1772 		cost += d->get_desc()->get_price();
1773 		delete d;
1774 	}
1775 	return cost;
1776 }
1777 
1778 
1779 sint64 grund_t::neuen_weg_bauen(weg_t *weg, ribi_t::ribi ribi, player_t *player)
1780 {
1781 	sint64 cost=0;
1782 
1783 	// not already there?
1784 	const weg_t * alter_weg = get_weg(weg->get_waytype());
1785 	if(alter_weg==NULL) {
1786 		// ok, we are unique
1787 
1788 		if((flags&has_way1)==0) {
1789 			// new first way here, clear trees
1790 			cost += remove_trees();
1791 
1792 			// add
1793 			weg->set_ribi(ribi);
1794 			weg->set_pos(pos);
1795 			objlist.add( weg );
1796 			flags |= has_way1;
1797 		}
1798 		else {
1799 			weg_t *other = (weg_t *)obj_bei(0);
1800 			// another way will be added
1801 			if(flags&has_way2) {
1802 				dbg->fatal("grund_t::neuen_weg_bauen()","cannot built more than two ways on %i,%i,%i!",pos.x,pos.y,pos.z);
1803 				return 0;
1804 			}
1805 			// add the way
1806 			objlist.add( weg );
1807 			weg->set_ribi(ribi);
1808 			weg->set_pos(pos);
1809 			flags |= has_way2;
1810 			if(ist_uebergang()) {
1811 				// no tram => crossing needed!
1812 				waytype_t w2 =  other->get_waytype();
1813 				const crossing_desc_t *cr_desc = crossing_logic_t::get_crossing( weg->get_waytype(), w2, weg->get_max_speed(), other->get_desc()->get_topspeed(), welt->get_timeline_year_month() );
1814 				if(cr_desc==0) {
1815 					dbg->fatal("crossing_t::crossing_t()","requested for waytypes %i and %i but nothing defined!", weg->get_waytype(), w2 );
1816 				}
1817 				crossing_t *cr = new crossing_t(obj_bei(0)->get_owner(), pos, cr_desc, ribi_t::is_straight_ns(get_weg(cr_desc->get_waytype(1))->get_ribi_unmasked()) );
1818 				objlist.add( cr );
1819 				cr->finish_rd();
1820 			}
1821 		}
1822 
1823 		// just add the maintenance
1824 		if(player && !is_water()) {
1825 			player_t::add_maintenance( player, weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype() );
1826 			weg->set_owner( player );
1827 		}
1828 
1829 		// may result in a crossing, but the wegebauer will recalc all images anyway
1830 		weg->calc_image();
1831 	}
1832 	return cost;
1833 }
1834 
1835 
1836 sint32 grund_t::weg_entfernen(waytype_t wegtyp, bool ribi_rem)
1837 {
1838 	weg_t *weg = get_weg(wegtyp);
1839 	if(weg!=NULL) {
1840 
1841 		weg->mark_image_dirty(get_image(), 0);
1842 
1843 		if(ribi_rem) {
1844 			ribi_t::ribi ribi = weg->get_ribi();
1845 			grund_t *to;
1846 
1847 			for(int r = 0; r < 4; r++) {
1848 				if((ribi & ribi_t::nsew[r]) && get_neighbour(to, wegtyp, ribi_t::nsew[r])) {
1849 					weg_t *weg2 = to->get_weg(wegtyp);
1850 					if(weg2) {
1851 						weg2->ribi_rem(ribi_t::backward(ribi_t::nsew[r]));
1852 						to->calc_image();
1853 					}
1854 				}
1855 			}
1856 		}
1857 
1858 		sint32 costs=weg->get_desc()->get_price();	// costs for removal are construction costs
1859 		weg->cleanup( NULL );
1860 		delete weg;
1861 
1862 		// delete the second way ...
1863 		if(flags&has_way2) {
1864 			flags &= ~has_way2;
1865 
1866 			// reset speed limit/crossing info (maybe altered by crossing)
1867 			// Not all ways (i.e. with styp==7) will imply crossings, so we have to check
1868 			crossing_t* cr = find<crossing_t>(1);
1869 			if(cr) {
1870 				cr->cleanup(0);
1871 				delete cr;
1872 				// restore speed limit
1873 				weg_t* w = (weg_t*)obj_bei(0);
1874 				w->set_desc(w->get_desc());
1875 				w->count_sign();
1876 			}
1877 		}
1878 		else {
1879 			flags &= ~has_way1;
1880 		}
1881 
1882 		calc_image();
1883 		minimap_t::get_instance()->calc_map_pixel(get_pos().get_2d());
1884 
1885 		return costs;
1886 	}
1887 	return 0;
1888 }
1889 
1890 
1891 // this function is called many many times => make it as fast as possible
1892 // i.e. no reverse lookup of ribis from koord
1893 bool grund_t::get_neighbour(grund_t *&to, waytype_t type, ribi_t::ribi ribi) const
1894 {
1895 	// must be a single direction
1896 	assert( ribi_t::is_single(ribi) );
1897 
1898 	if(  type != invalid_wt  &&   (get_weg_ribi_unmasked(type) & ribi) == 0  ) {
1899 		// no way on this tile in the given direction
1900 		return false;
1901 	}
1902 
1903 	const planquadrat_t *plan = welt->access(pos.get_2d() + koord(ribi) );
1904 	if(!plan) {
1905 		return false;
1906 	}
1907 	const ribi_t::ribi back = ribi_t::reverse_single(ribi);
1908 
1909 	// most common on empty ground => check this first
1910 	if(  get_grund_hang() == slope_t::flat  &&  get_weg_hang() == slope_t::flat  ) {
1911 		if(  grund_t *gr = plan->get_boden_in_hoehe( pos.z )  ) {
1912 			if(  gr->get_grund_hang() == slope_t::flat  &&  gr->get_weg_hang() == slope_t::flat  ) {
1913 				if(  type == invalid_wt  ||  (gr->get_weg_ribi_unmasked(type) & back)  ) {
1914 					to = gr;
1915 					return true;
1916 				}
1917 			}
1918 		}
1919 	}
1920 
1921 	// most common on empty round => much faster this way
1922 	const sint16 this_height = get_vmove(ribi);
1923 	for( unsigned i=0;  i<plan->get_boden_count();  i++  ) {
1924 		grund_t* gr = plan->get_boden_bei(i);
1925 		if(gr->get_vmove(back)==this_height) {
1926 			// test, if connected
1927 			if(  type == invalid_wt  ||  (gr->get_weg_ribi_unmasked(type) & back)  ) {
1928 				to = gr;
1929 				return true;
1930 			}
1931 		}
1932 	}
1933 	return false;
1934 }
1935 
1936 
1937 int grund_t::get_max_speed() const
1938 {
1939 	int max = 0;
1940 	if (weg_t const* const w = get_weg_nr(0)) {
1941 		max = w->get_max_speed();
1942 	}
1943 	if (weg_t const* const w = get_weg_nr(1)) {
1944 		max = min(max, w->get_max_speed());
1945 	}
1946 	return max;
1947 }
1948 
1949 
1950 bool grund_t::remove_everything_from_way(player_t* player, waytype_t wt, ribi_t::ribi rem)
1951 {
1952 	// check, if the way must be totally removed?
1953 	weg_t *weg = get_weg(wt);
1954 	if(weg) {
1955 		waytype_t wt = weg->get_waytype();
1956 		waytype_t finance_wt = weg->get_desc()->get_finance_waytype();
1957 		const koord here = pos.get_2d();
1958 
1959 		// stops
1960 		if(flags&is_halt_flag  &&  (get_halt()->get_owner()==player  || player==welt->get_public_player())) {
1961 			bool remove_halt = get_typ()!=boden;
1962 			// remove only if there is no other way
1963 			if(get_weg_nr(1)==NULL) {
1964 				remove_halt = true;
1965 			}
1966 			else {
1967 #ifdef delete_matching_stops
1968 				// delete halts with the same waytype ... may lead to confusing / unexpected but completely logical results ;)
1969 				gebaeude_t *gb = find<gebaeude_t>();
1970 				if (gb) {
1971 					waytype_t halt_wt = (waytype_t)gb->get_tile()->get_desc()->get_extra();
1972 					if (halt_wt == wt || (wt==track_wt && halt_wt==tram_wt) || (wt==tram_wt && halt_wt==track_wt)) {
1973 						remove_halt = true;
1974 					}
1975 				}
1976 #else
1977 				remove_halt = false;
1978 #endif
1979 			}
1980 			if (remove_halt) {
1981 				if (!haltestelle_t::remove(player, pos)) {
1982 					return false;
1983 				}
1984 			}
1985 		}
1986 		// remove ribi from canals to sea level
1987 		if(  wt == water_wt  &&  pos.z == welt->get_water_hgt( here )  &&  slope != slope_t::flat  ) {
1988 			rem &= ~ribi_t::doubles(ribi_type(slope));
1989 		}
1990 
1991 		ribi_t::ribi add=(weg->get_ribi_unmasked()&rem);
1992 		sint32 costs = 0;
1993 
1994 		for(  sint16 i=get_top();  i>=0;  i--  ) {
1995 			// we need to delete backwards, since we might miss things otherwise
1996 			if(  i>=get_top()  ) {
1997 				continue;
1998 			}
1999 
2000 			obj_t *obj=obj_bei((uint8)i);
2001 			// do not delete ways
2002 			if(  obj->get_typ()==obj_t::way  ) {
2003 				continue;
2004 			}
2005 			if (roadsign_t* const sign = obj_cast<roadsign_t>(obj)) {
2006 				// roadsigns: check dir: dirs changed => delete
2007 				if (sign->get_desc()->get_wtyp() == wt && (sign->get_dir() & ~add) != 0) {
2008 					costs -= sign->get_desc()->get_price();
2009 					delete sign;
2010 				}
2011 			}
2012 			else if (signal_t* const signal = obj_cast<signal_t>(obj)) {
2013 				// signal: not on crossings => remove all
2014 				if (signal->get_desc()->get_wtyp() == wt) {
2015 					costs -= signal->get_desc()->get_price();
2016 					delete signal;
2017 				}
2018 			}
2019 			else if (wayobj_t* const wayobj = obj_cast<wayobj_t>(obj)) {
2020 				// wayobj: check dir
2021 				if (add == ribi_t::none && wayobj->get_desc()->get_wtyp() == wt) {
2022 					uint8 new_dir=wayobj->get_dir()&add;
2023 					if(new_dir) {
2024 						// just change dir
2025 						wayobj->set_dir(new_dir);
2026 					}
2027 					else {
2028 						costs -= wayobj->get_desc()->get_price();
2029 						delete wayobj;
2030 					}
2031 				}
2032 			}
2033 			else if (private_car_t* const citycar = obj_cast<private_car_t>(obj)) {
2034 				// citycar: just delete
2035 				if (wt == road_wt) delete citycar;
2036 			}
2037 			else if (pedestrian_t* const pedestrian = obj_cast<pedestrian_t>(obj)) {
2038 				// pedestrians: just delete
2039 				if (wt == road_wt) delete pedestrian;
2040 			}
2041 			else if (tunnel_t* const tunnel = obj_cast<tunnel_t>(obj)) {
2042 				// remove tunnel portal, if not the last tile ...
2043 				// must be done before weg_entfernen() to get maintenance right
2044 				uint8 wt = tunnel->get_desc()->get_waytype();
2045 				if (weg->get_waytype()==wt) {
2046 					if((flags&has_way2)==0) {
2047 						if (add==ribi_t::none) {
2048 							// last way was belonging to this tunnel
2049 							tunnel->cleanup(player);
2050 							delete tunnel;
2051 						}
2052 					}
2053 					else {
2054 						// we must leave the way to prevent destroying the other one
2055 						add |= get_weg_nr(1)->get_ribi_unmasked();
2056 						weg->calc_image();
2057 					}
2058 				}
2059 			}
2060 		}
2061 
2062 		// need to remove railblocks to recalculate connections
2063 		// remove all ways or just some?
2064 		if(add==ribi_t::none) {
2065 			costs -= weg_entfernen(wt, true);
2066 			if(flags&is_kartenboden) {
2067 				// remove ribis from sea tiles
2068 				if(  wt == water_wt  &&  pos.z == welt->get_water_hgt( here )  &&  slope != slope_t::flat  ) {
2069 					grund_t *gr = welt->lookup_kartenboden(here - ribi_type(slope));
2070 					if (gr  &&  gr->is_water()) {
2071 						gr->calc_image(); // to recalculate ribis
2072 					}
2073 				}
2074 				// make tunnel portals to normal ground
2075 				if (get_typ()==tunnelboden  &&  (flags&has_way1)==0) {
2076 					// remove remaining objs
2077 					obj_loesche_alle( player );
2078 					// set to normal ground
2079 					welt->access(here)->kartenboden_setzen( new boden_t( pos, slope ) );
2080 					// now this is already deleted !
2081 				}
2082 			}
2083 		}
2084 		else {
2085 DBG_MESSAGE("tool_wayremover()","change remaining way to ribi %d",add);
2086 			// something will remain, we just change ribis
2087 			weg->set_ribi(add);
2088 			calc_image();
2089 		}
2090 		// we have to pay?
2091 		if(costs) {
2092 			player_t::book_construction_costs(player, costs, here, finance_wt);
2093 		}
2094 	}
2095 	return true;
2096 }
2097 
2098 
2099 wayobj_t *grund_t::get_wayobj( waytype_t wt ) const
2100 {
2101 	waytype_t wt1 = ( wt == tram_wt ) ? track_wt : wt;
2102 
2103 	// since there might be more than one, we have to iterate through all of them
2104 	for(  uint8 i = 0;  i < get_top();  i++  ) {
2105 		obj_t *obj = obj_bei(i);
2106 		if (wayobj_t* const wayobj = obj_cast<wayobj_t>(obj)) {
2107 			waytype_t wt2 = wayobj->get_desc()->get_wtyp();
2108 			if(  wt2 == tram_wt  ) {
2109 				wt2 = track_wt;
2110 			}
2111 			if(  wt1 == wt2  ) {
2112 				return wayobj;
2113 			}
2114 		}
2115 	}
2116 	return NULL;
2117 }
2118