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