1 /*
2 * Copyright (c) 1997 - 2001 Hansj�rg Malthaner
3 *
4 * This file is part of the Simutrans project under the artistic licence.
5 * (see licence.txt)
6 */
7
8 #include <stdio.h>
9
10 #include "../simdebug.h"
11
12 #include "tunnelbauer.h"
13
14 #include "../gui/minimap.h"
15
16 #include "../simworld.h"
17 #include "../player/simplay.h"
18 #include "../simtool.h"
19
20 #include "../descriptor/tunnel_desc.h"
21
22 #include "../boden/tunnelboden.h"
23
24 #include "../dataobj/scenario.h"
25 #include "../dataobj/environment.h"
26 #include "../dataobj/marker.h"
27
28 #include "../obj/tunnel.h"
29 #include "../obj/leitung2.h"
30 #include "../obj/signal.h"
31
32 #include "../gui/messagebox.h"
33 #include "../gui/tool_selector.h"
34
35 #include "wegbauer.h"
36 #include "../tpl/stringhashtable_tpl.h"
37 #include "../tpl/vector_tpl.h"
38
39
40 karte_ptr_t tunnel_builder_t::welt;
41
42 static stringhashtable_tpl<tunnel_desc_t *> tunnel_by_name;
43
44
register_desc(tunnel_desc_t * desc)45 void tunnel_builder_t::register_desc(tunnel_desc_t *desc)
46 {
47 // avoid duplicates with same name
48 if( const tunnel_desc_t *old_desc = tunnel_by_name.get(desc->get_name()) ) {
49 dbg->doubled( "tunnel", desc->get_name() );
50 tool_t::general_tool.remove( old_desc->get_builder() );
51 delete old_desc->get_builder();
52 // delete old_desc; because deleting PowerTunnel seems to corrupt memory, and the small memory loss in not really worth the troubles
53 }
54 // add the tool
55 tool_build_tunnel_t *tool = new tool_build_tunnel_t();
56 tool->set_icon( desc->get_cursor()->get_image_id(1) );
57 tool->cursor = desc->get_cursor()->get_image_id(0);
58 tool->set_default_param( desc->get_name() );
59 tool_t::general_tool.append( tool );
60 desc->set_builder( tool );
61 tunnel_by_name.put(desc->get_name(), desc);
62 }
63
64
get_desc(const char * name)65 const tunnel_desc_t *tunnel_builder_t::get_desc(const char *name)
66 {
67 return (name ? tunnel_by_name.get(name) : NULL);
68 }
69
70
71 /**
72 * Find a matching tunnel
73 * @author Hj. Malthaner
74 */
get_tunnel_desc(const waytype_t wtyp,const sint32 min_speed,const uint16 time)75 const tunnel_desc_t *tunnel_builder_t::get_tunnel_desc(const waytype_t wtyp, const sint32 min_speed, const uint16 time)
76 {
77 const tunnel_desc_t *find_desc = NULL;
78
79 FOR(stringhashtable_tpl<tunnel_desc_t*>, const& i, tunnel_by_name) {
80 tunnel_desc_t* const desc = i.value;
81 if( desc->get_waytype()==wtyp ) {
82 if( desc->is_available(time) ) {
83 if( find_desc==NULL ||
84 (find_desc->get_topspeed()<min_speed && find_desc->get_topspeed()<desc->get_topspeed()) ||
85 (desc->get_topspeed()>=min_speed && desc->get_maintenance()<find_desc->get_maintenance())
86 ) {
87 find_desc = desc;
88 }
89 }
90 }
91 }
92 return find_desc;
93 }
94
95
compare_tunnels(const tunnel_desc_t * a,const tunnel_desc_t * b)96 static bool compare_tunnels(const tunnel_desc_t* a, const tunnel_desc_t* b)
97 {
98 int cmp = a->get_topspeed() - b->get_topspeed();
99 if(cmp==0) {
100 cmp = (int)a->get_intro_year_month() - (int)b->get_intro_year_month();
101 }
102 if(cmp==0) {
103 cmp = strcmp(a->get_name(), b->get_name());
104 }
105 return cmp<0;
106 }
107
108
109 /**
110 * Fill menu with icons of given waytype
111 * @author Hj. Malthaner
112 */
fill_menu(tool_selector_t * tool_selector,const waytype_t wtyp,sint16)113 void tunnel_builder_t::fill_menu(tool_selector_t* tool_selector, const waytype_t wtyp, sint16 /*sound_ok*/)
114 {
115 // check if scenario forbids this
116 if (!welt->get_scenario()->is_tool_allowed(welt->get_active_player(), TOOL_BUILD_TUNNEL | GENERAL_TOOL, wtyp)) {
117 return;
118 }
119
120 const uint16 time=welt->get_timeline_year_month();
121 vector_tpl<const tunnel_desc_t*> matching(tunnel_by_name.get_count());
122
123 FOR(stringhashtable_tpl<tunnel_desc_t*>, const& i, tunnel_by_name) {
124 tunnel_desc_t* const desc = i.value;
125 if( desc->get_waytype()==wtyp && desc->is_available(time) ) {
126 matching.insert_ordered(desc, compare_tunnels);
127 }
128 }
129 // now sorted ...
130 FOR(vector_tpl<tunnel_desc_t const*>, const i, matching) {
131 tool_selector->add_tool_selector(i->get_builder());
132 }
133 }
134
135
get_available_tunnels(const waytype_t wtyp)136 const vector_tpl<const tunnel_desc_t *>& tunnel_builder_t::get_available_tunnels(const waytype_t wtyp)
137 {
138 static vector_tpl<const tunnel_desc_t *> dummy;
139 dummy.clear();
140 const uint16 time = welt->get_timeline_year_month();
141 FOR(stringhashtable_tpl<tunnel_desc_t*>, const& i, tunnel_by_name) {
142 tunnel_desc_t const* const b = i.value;
143 if ( b->get_waytype()==wtyp && b->is_available(time) ) {
144 dummy.append(b);
145 }
146 }
147 return dummy;
148 }
149
150
151 /* now construction stuff */
152
153
find_end_pos(player_t * player,koord3d pos,koord zv,const tunnel_desc_t * desc,bool full_tunnel,const char ** msg)154 koord3d tunnel_builder_t::find_end_pos(player_t *player, koord3d pos, koord zv, const tunnel_desc_t *desc, bool full_tunnel, const char** msg)
155 {
156 const grund_t *gr;
157 leitung_t *lt;
158 waytype_t wegtyp = desc->get_waytype();
159 // use the is_allowed_step routine of way_builder_t, needs an instance
160 way_builder_t bauigel(player);
161 bauigel.init_builder( way_builder_t::tunnel_flag | (way_builder_t::bautyp_t)wegtyp, way_builder_t::weg_search( wegtyp, 1, 0, type_flat ), desc);
162 sint32 dummy;
163
164 while(true) {
165 pos = pos + zv;
166 if(!welt->is_within_limits(pos.get_2d())) {
167 return koord3d::invalid;
168 }
169
170 // check if ground is below tunnel level
171 gr = welt->lookup_kartenboden(pos.get_2d());
172
173 // steep slopes and we are appearing at the top of one
174 if( gr->get_hoehe() == pos.z-1 && env_t::pak_height_conversion_factor==1 ) {
175 const slope_t::type new_slope = slope_type(-zv);
176 sint8 hsw = pos.z + corner_sw(new_slope);
177 sint8 hse = pos.z + corner_se(new_slope);
178 sint8 hne = pos.z + corner_ne(new_slope);
179 sint8 hnw = pos.z + corner_nw(new_slope);
180 karte_t::terraformer_t raise(welt);
181 raise.add_raise_node(pos.x, pos.y, hsw, hse, hne, hnw);
182 raise.iterate(true);
183 if (raise.can_raise_all(player)) {
184 // returned true therefore error reported
185 return koord3d::invalid;
186 }
187 // if we can adjust height here we can build an entrance so don't need checks below
188 return pos;
189 }
190 if( gr->get_hoehe() < pos.z ){
191 return koord3d::invalid;
192 }
193
194 // check water level
195 if (gr->is_water() && welt->lookup_hgt(pos.get_2d()) <= pos.z) {
196 return koord3d::invalid;
197 }
198
199 if (const char* err = welt->get_scenario()->is_work_allowed_here(player, TOOL_BUILD_TUNNEL|GENERAL_TOOL, wegtyp, pos)) {
200 if (msg) {
201 *msg = err;
202 }
203 return koord3d::invalid;
204 }
205
206 // next tile
207 gr = welt->lookup(pos);
208 if( gr == NULL ) {
209 // check for slope down ...
210 gr = welt->lookup(pos + koord3d(0,0,-1));
211 if( !gr ) {
212 gr = welt->lookup(pos + koord3d(0,0,-2));
213 }
214 if( gr && gr->get_weg_hang() == slope_t::flat ) {
215 // Don't care about _flat_ tunnels below.
216 gr = NULL;
217 }
218
219 if( !gr && env_t::pak_height_conversion_factor==2 ) {
220 // check for one above
221 gr = welt->lookup(pos + koord3d(0,0,1));
222 }
223 }
224
225 if(gr) {
226 // if there is a tunnel try to connect
227 if( gr->ist_tunnel() ) {
228 if( gr->get_vmove(ribi_type(-zv))!=pos.z) {
229 // wrong slope
230 return koord3d::invalid;
231 }
232 // fake tunnel tile
233 tunnelboden_t from(pos - zv, slope_t::flat);
234 if (bauigel.is_allowed_step(&from, gr, &dummy)) {
235 return gr->get_pos();
236 }
237 else {
238 return koord3d::invalid;
239 }
240 }
241 const uint8 slope = gr->get_grund_hang();
242 const slope_t::type new_slope = slope_type(-zv) * welt->get_settings().get_way_height_clearance();
243
244 if( gr->ist_karten_boden() && ( slope!=new_slope || pos.z!=gr->get_pos().z ) ) {
245 // lower terrain to match - most of time shouldn't need to raise
246 // however player might have manually altered terrain so check this anyway
247 sint8 hsw = pos.z + corner_sw(new_slope);
248 sint8 hse = pos.z + corner_se(new_slope);
249 sint8 hne = pos.z + corner_ne(new_slope);
250 sint8 hnw = pos.z + corner_nw(new_slope);
251 karte_t::terraformer_t raise(welt), lower(welt);
252 raise.add_raise_node(pos.x, pos.y, hsw, hse, hne, hnw);
253 raise.iterate(false);
254 lower.add_lower_node(pos.x, pos.y, hsw, hse, hne, hnw);
255 lower.iterate(false);
256 if (raise.can_lower_all(player) || lower.can_lower_all(player)) {
257 // returned true therefore error reported
258 return koord3d::invalid;
259 }
260 // if we can adjust height here we can build an entrance so don't need checks below
261 return pos;
262 }
263
264
265 if( gr->get_typ() != grund_t::boden || slope != new_slope || gr->is_halt() || ((wegtyp != powerline_wt) ? gr->get_leitung() != NULL : gr->hat_wege()) ) {
266 // must end on boden_t and correct slope and not on halts
267 // ways cannot end on powerlines, powerlines cannot end on ways
268 return koord3d::invalid;
269 }
270 if( gr->has_two_ways() && wegtyp != road_wt ) {
271 // Only road tunnels allowed here.
272 return koord3d::invalid;
273 }
274
275 ribi_t::ribi ribi = 0;
276 if(wegtyp != powerline_wt) {
277 ribi = gr->get_weg_ribi_unmasked(wegtyp);
278 }
279 else {
280 if(gr->get_leitung()) {
281 ribi = gr->get_leitung()->get_ribi();
282 }
283 }
284
285 if( ribi && koord(ribi) == zv ) {
286 // There is already a way (with correct ribi)
287 return pos;
288 }
289 if( !ribi ) {
290 // End of the slope - Missing end rail or has no ribis
291 // we still consider if we interfere with a way (original: pr�fen noch, ob uns dort ein anderer Weg st�rt)
292 if(wegtyp != powerline_wt) {
293 if( !gr->hat_wege() || gr->hat_weg(wegtyp) ) {
294 return pos;
295 }
296 }
297 else {
298 lt = gr->find<leitung_t>();
299 if(!gr->hat_wege() || lt) {
300 return pos;
301 }
302 }
303 }
304 return koord3d::invalid; // Was im Weg (slope hillside or so)
305 }
306
307 // stop if we only want to check tile behind tunnel mouth
308 if (!full_tunnel) {
309 return pos;
310 }
311 // All free - keep looking
312 }
313 }
314
315
build(player_t * player,koord pos,const tunnel_desc_t * desc,bool full_tunnel)316 const char *tunnel_builder_t::build( player_t *player, koord pos, const tunnel_desc_t *desc, bool full_tunnel )
317 {
318 assert( desc );
319
320 const grund_t *gr = welt->lookup_kartenboden(pos);
321 if(gr==NULL) {
322 return "Tunnel must start on single way!";
323 }
324
325 koord zv;
326 const waytype_t wegtyp = desc->get_waytype();
327 const slope_t::type slope = gr->get_grund_hang();
328
329 if( wegtyp != powerline_wt ) {
330 const weg_t *weg = gr->get_weg(wegtyp);
331
332 if( gr->get_typ() != grund_t::boden || gr->is_halt() || gr->get_leitung()) {
333 return "Tunnel must start on single way!";
334 }
335 // If there is a way on this tile, it must have the right ribis.
336 if( weg && (weg->get_ribi_unmasked() & ~ribi_t::backward( ribi_type(slope) )) ) {
337 return "Tunnel must start on single way!";
338 }
339 }
340 else {
341 leitung_t *lt = gr->find<leitung_t>();
342 if( gr->get_typ() != grund_t::boden || gr->hat_wege() ) {
343 return "Tunnel must start on single way!";
344 }
345 if( lt && (lt->get_ribi() & ~ribi_t::backward( ribi_type(slope) )) ) {
346 return "Tunnel must start on single way!";
347 }
348 }
349 if( !slope_t::is_single(slope) ) {
350 return "Tunnel muss an\neinfachem\nHang beginnen!\n";
351 }
352
353 /************************************** FIX ME ***************************************************
354 ********************** THIS MUST BE RATHER A PROPERTY OF THE TUNNEL IN QUESTION ! ****************/
355 // for conversion factor 1, must be single height, for conversion factor 2, must be double
356 if( (env_t::pak_height_conversion_factor == 1 && !(slope & 7)) || (env_t::pak_height_conversion_factor == 2 && (slope & 7)) ) {
357 return "Tunnel muss an\neinfachem\nHang beginnen!\n";
358 }
359
360 if( gr->has_two_ways() && wegtyp != road_wt ) {
361 return "Tunnel must start on single way!";
362 }
363 zv = koord(slope);
364
365 // Search tunnel end and check intermediate tiles
366 const char *err = NULL;
367 koord3d end = find_end_pos(player, gr->get_pos(), zv, desc, full_tunnel, &err);
368 if (err) {
369 return err;
370 }
371
372 if (!full_tunnel) {
373 // if there is no tunnel behind set end to start position
374 const grund_t *gr_end = welt->lookup(end);
375 if (gr_end == NULL || !gr_end->ist_tunnel()) {
376 end = gr->get_pos();
377 }
378 }
379
380
381 if(!welt->is_within_limits(end.get_2d())) {
382 return "Tunnel must start on single way!";
383 }
384
385 // check ownership
386 if (const grund_t *gr_end = welt->lookup(end)) {
387 if (weg_t *weg_end = gr_end->get_weg(wegtyp)) {
388 if (weg_end->is_deletable(player)!=NULL) {
389 return "Das Feld gehoert\neinem anderen Spieler\n";
390 }
391 }
392 }
393
394 // Begin and end found, we can build
395
396 const grund_t *end_gr = welt->lookup(end);
397 slope_t::type end_slope = slope_type(-zv) * env_t::pak_height_conversion_factor;
398 if( full_tunnel && (!end_gr || end_gr->get_grund_hang()!=end_slope) ) {
399 // end slope not at correct height - we have already checked in find_end_pos that we can change this
400 sint8 hsw = end.z + corner_sw(end_slope);
401 sint8 hse = end.z + corner_se(end_slope);
402 sint8 hne = end.z + corner_ne(end_slope);
403 sint8 hnw = end.z + corner_nw(end_slope);
404
405 int n = 0;
406
407 karte_t::terraformer_t raise(welt),lower(welt);
408 raise.add_raise_node(end.x, end.y, hsw, hse, hne, hnw);
409 lower.add_lower_node(end.x, end.y, hsw, hse, hne, hnw);
410 raise.iterate(true);
411 lower.iterate(false);
412 err = raise.can_raise_all(player);
413 if (!err) err = lower.can_lower_all(player);
414 if (err) return 0;
415
416 // TODO: this is rather hackish as 4 seems to come from nowhere but works most of the time
417 // feel free to change if you have a better idea!
418 n = (raise.raise_all()+lower.lower_all())/4;
419 player_t::book_construction_costs(player, welt->get_settings().cst_alter_land * n, end.get_2d(), desc->get_waytype());
420 }
421
422 if(!build_tunnel(player, gr->get_pos(), end, zv, desc)) {
423 return "Ways not connected";
424 }
425 return NULL;
426 }
427
428
build_tunnel(player_t * player,koord3d start,koord3d end,koord zv,const tunnel_desc_t * desc)429 bool tunnel_builder_t::build_tunnel(player_t *player, koord3d start, koord3d end, koord zv, const tunnel_desc_t *desc)
430 {
431 ribi_t::ribi ribi = 0;
432 weg_t *weg = NULL;
433 leitung_t *lt = NULL;
434 koord3d pos = start;
435 int cost = 0;
436 const way_desc_t *way_desc;
437 waytype_t wegtyp = desc->get_waytype();
438
439 DBG_MESSAGE("tunnel_builder_t::build()","build from (%d,%d,%d) to (%d,%d,%d) ", pos.x, pos.y, pos.z, end.x, end.y, end.z );
440
441 // now we search a matching way for the tunnels top speed
442 way_desc = desc->get_way_desc();
443 if(way_desc==NULL) {
444 // ignore timeline to get consistent results
445 way_desc = way_builder_t::weg_search( wegtyp, desc->get_topspeed(), 0, type_flat );
446 }
447
448 build_tunnel_portal(player, pos, zv, desc, way_desc, cost, start != end);
449
450 ribi = ribi_type(-zv);
451
452 // move on
453 pos = pos + zv;
454
455 // calc back image to remove wall blocking tunnel portal for active underground view
456 if(grund_t::underground_mode) {
457 grund_t *gr = welt->lookup_kartenboden(pos.get_2d());
458 gr->calc_image();
459 gr->set_flag(grund_t::dirty);
460 }
461
462 if( end == start ) {
463 // already finished
464 return true;
465 }
466
467 // Now we build the invisible part
468 while(pos.get_2d()!=end.get_2d()) {
469 tunnelboden_t *tunnel = new tunnelboden_t( pos, 0);
470 welt->access(pos.get_2d())->boden_hinzufuegen(tunnel);
471 if(wegtyp != powerline_wt) {
472 weg = weg_t::alloc(desc->get_waytype());
473 weg->set_desc(way_desc);
474 weg->set_max_speed(desc->get_topspeed());
475 tunnel->neuen_weg_bauen(weg, ribi_t::doubles(ribi), player);
476 player_t::add_maintenance( player, -weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype() );
477 }
478 else {
479 lt = new leitung_t(tunnel->get_pos(), player);
480 lt->set_desc(way_desc);
481 tunnel->obj_add( lt );
482 lt->finish_rd();
483 player_t::add_maintenance( player, -way_desc->get_maintenance(), powerline_wt );
484 }
485 tunnel->obj_add(new tunnel_t(pos, player, desc));
486 tunnel->calc_image();
487 tunnel->set_flag(grund_t::dirty);
488 assert(!tunnel->ist_karten_boden());
489 player_t::add_maintenance( player, desc->get_maintenance(), desc->get_finance_waytype() );
490 cost += desc->get_price();
491 pos = pos + zv;
492 }
493
494 // if end is tunnel then connect
495 grund_t *gr_end = welt->lookup(end);
496 if (gr_end) {
497 if (gr_end->ist_tunnel()) {
498 gr_end->weg_erweitern(desc->get_waytype(), ribi);
499 }
500 else if (gr_end->ist_karten_boden()) {
501 // if end is above ground construct an exit
502 build_tunnel_portal(player, pos, -zv, desc, way_desc, cost, true);
503 gr_end = NULL; // invalid - replaced by tunnel ground
504 // calc new back image for the ground
505 if (end!=start && grund_t::underground_mode) {
506 grund_t *gr = welt->lookup_kartenboden(pos.get_2d()-zv);
507 gr->calc_image();
508 gr->set_flag(grund_t::dirty);
509 }
510 }
511 else {
512 // good luck
513 assert(0);
514 }
515 }
516 else {
517 // construct end tunnel tile
518 tunnelboden_t *tunnel = new tunnelboden_t( pos, 0);
519 welt->access(pos.get_2d())->boden_hinzufuegen(tunnel);
520 if(wegtyp != powerline_wt) {
521 weg = weg_t::alloc(desc->get_waytype());
522 weg->set_desc(way_desc);
523 weg->set_max_speed(desc->get_topspeed());
524 tunnel->neuen_weg_bauen(weg, ribi, player);
525 player_t::add_maintenance( player, -weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype() );
526 }
527 else {
528 lt = new leitung_t(tunnel->get_pos(), player);
529 lt->set_desc(way_desc);
530 tunnel->obj_add( lt );
531 lt->finish_rd();
532 player_t::add_maintenance( player, -way_desc->get_maintenance(), powerline_wt );
533 }
534 tunnel->obj_add(new tunnel_t(pos, player, desc));
535 tunnel->calc_image();
536 tunnel->set_flag(grund_t::dirty);
537 assert(!tunnel->ist_karten_boden());
538 player_t::add_maintenance( player, desc->get_maintenance(), desc->get_finance_waytype() );
539 cost += desc->get_price();
540 }
541
542 player_t::book_construction_costs(player, -cost, start.get_2d(), desc->get_waytype());
543 return true;
544 }
545
546
build_tunnel_portal(player_t * player,koord3d end,koord zv,const tunnel_desc_t * desc,const way_desc_t * way_desc,int & cost,bool connect_inside)547 void tunnel_builder_t::build_tunnel_portal(player_t *player, koord3d end, koord zv, const tunnel_desc_t *desc, const way_desc_t *way_desc, int &cost, bool connect_inside)
548 {
549 grund_t *alter_boden = welt->lookup(end);
550 ribi_t::ribi ribi = 0;
551 if(desc->get_waytype()!=powerline_wt) {
552 ribi = alter_boden->get_weg_ribi_unmasked(desc->get_waytype());
553 }
554 if (connect_inside) {
555 ribi |= ribi_type(zv);
556 }
557
558 tunnelboden_t *tunnel = new tunnelboden_t( end, alter_boden->get_grund_hang());
559 tunnel->obj_add(new tunnel_t(end, player, desc));
560
561 weg_t *weg = NULL;
562 if(desc->get_waytype()!=powerline_wt) {
563 weg = alter_boden->get_weg( desc->get_waytype() );
564 }
565 // take care of everything on that tile ...
566 tunnel->take_obj_from( alter_boden );
567 welt->access(end.get_2d())->kartenboden_setzen( tunnel );
568 if(desc->get_waytype() != powerline_wt) {
569 if(weg) {
570 // has already a way
571 tunnel->weg_erweitern(desc->get_waytype(), ribi);
572 }
573 else {
574 // needs still one
575 weg = weg_t::alloc( desc->get_waytype() );
576 if( way_desc ) {
577 weg->set_desc( way_desc );
578 }
579 tunnel->neuen_weg_bauen( weg, ribi, player );
580 }
581 player_t::add_maintenance( player, -weg->get_desc()->get_maintenance(), weg->get_desc()->get_finance_waytype() );
582 weg->set_max_speed( desc->get_topspeed() );
583 }
584 else {
585 leitung_t *lt = tunnel->get_leitung();
586 if(!lt) {
587 lt = new leitung_t(tunnel->get_pos(), player);
588 lt->set_desc(way_desc);
589 tunnel->obj_add( lt );
590 player_t::add_maintenance( player, -way_desc->get_maintenance(), powerline_wt );
591 }
592 else {
593 // subtract twice maintenance: once for the already existing powerline
594 // once since leitung_t::finish_rd will add it again
595 player_t::add_maintenance( player, -2*lt->get_desc()->get_maintenance(), powerline_wt );
596 }
597 lt->finish_rd();
598 }
599
600 // remove sidewalk
601 weg_t *str = tunnel->get_weg( road_wt );
602 if( str && str->hat_gehweg()) {
603 str->set_gehweg(false);
604 }
605
606 tunnel->calc_image();
607 tunnel->set_flag(grund_t::dirty);
608
609 // Auto-connect to a way outside the new tunnel mouth
610 grund_t *ground_outside = welt->lookup(end-zv);
611 if( !ground_outside ) {
612 ground_outside = welt->lookup(end-zv+koord3d(0,0,-1));
613 if( ground_outside && ground_outside->get_grund_hang() != tunnel->get_grund_hang() ) {
614 // Not the correct slope.
615 ground_outside = NULL;
616 }
617 }
618 if( ground_outside) {
619 weg_t *way_outside = ground_outside->get_weg( desc->get_waytype() );
620 if( way_outside ) {
621 // use the check_owner routine of way_builder_t (not player_t!), needs an instance
622 way_builder_t bauigel(player);
623 bauigel.init_builder( (way_builder_t::bautyp_t)desc->get_waytype(), way_outside->get_desc());
624 sint32 dummy;
625 if(bauigel.is_allowed_step(tunnel, ground_outside, &dummy)) {
626 tunnel->weg_erweitern(desc->get_waytype(), ribi_type(-zv));
627 ground_outside->weg_erweitern(desc->get_waytype(), ribi_type(zv));
628 }
629 }
630 if (desc->get_waytype()==water_wt && ground_outside->is_water()) {
631 // connect to the sea
632 tunnel->weg_erweitern(desc->get_waytype(), ribi_type(-zv));
633 ground_outside->calc_image(); // to recalculate ribis
634 }
635 }
636
637
638 if(player!=NULL) {
639 player_t::add_maintenance( player, desc->get_maintenance(), desc->get_finance_waytype() );
640 }
641 cost += desc->get_price();
642 }
643
644
remove(player_t * player,koord3d start,waytype_t wegtyp,bool remove_all)645 const char *tunnel_builder_t::remove(player_t *player, koord3d start, waytype_t wegtyp, bool remove_all )
646 {
647 marker_t& marker = marker_t::instance(welt->get_size().x, welt->get_size().y);
648 slist_tpl<koord3d> end_list;
649 slist_tpl<koord3d> part_list;
650 slist_tpl<koord3d> tmp_list;
651 koord3d pos = start;
652
653 // Erstmal das ganze Au�ma� des Tunnels bestimmen und sehen,
654 // ob uns was im Weg ist.
655 tmp_list.insert(pos);
656 grund_t *from = welt->lookup(pos);
657 marker.mark(from);
658 waytype_t delete_wegtyp = wegtyp==powerline_wt ? invalid_wt : wegtyp;
659
660 do {
661 pos = tmp_list.remove_first();
662
663 // V.Meyer: weg_position_t changed to grund_t::get_neighbour()
664 grund_t *from = welt->lookup(pos);
665 grund_t *to;
666 koord zv = koord::invalid;
667
668 if(from->ist_karten_boden()) {
669 // Der Grund ist Tunnelanfang/-ende - hier darf nur in
670 // eine Richtung getestet werden.
671 zv = koord(from->get_grund_hang());
672 end_list.insert(pos);
673 }
674 else {
675 part_list.insert(pos);
676 }
677 // Alle Tunnelteile auf Entfernbarkeit pr�fen!
678 if( from->kann_alle_obj_entfernen(player) ) {
679 return "Der Tunnel ist nicht frei!\n";
680 }
681
682 ribi_t::ribi waytype_ribi = ribi_t::none;
683 if( wegtyp == powerline_wt ) {
684 if( from->get_leitung() ) {
685 waytype_ribi = from->get_leitung()->get_ribi();
686 }
687 }
688 else {
689 waytype_ribi = from->get_weg_ribi_unmasked(delete_wegtyp);
690 }
691 if( !remove_all && ribi_t::is_threeway(waytype_ribi) ) {
692 return "This tunnel branches. You can try Control+Click to remove.";
693 }
694
695 // Nachbarn raussuchen
696 for(int r = 0; r < 4; r++) {
697 if((zv == koord::invalid || zv == koord::nsew[r]) &&
698 from->get_neighbour(to, delete_wegtyp, ribi_t::nsew[r]) &&
699 !marker.is_marked(to) &&
700 (wegtyp != powerline_wt || to->get_leitung()))
701 {
702 tmp_list.insert(to->get_pos());
703 marker.mark(to);
704 }
705 }
706 } while (!tmp_list.empty());
707
708 // Jetzt geht es ans l�schen der Tunnel
709 while (!part_list.empty()) {
710 pos = part_list.remove_first();
711 grund_t *gr = welt->lookup(pos);
712 // remove the second way first in the tunnel
713 if(gr->get_weg_nr(1)) {
714 gr->remove_everything_from_way(player,gr->get_weg_nr(1)->get_waytype(),ribi_t::none);
715 }
716 gr->remove_everything_from_way(player,wegtyp,ribi_t::none); // removes stop and signals correctly
717 // remove everything else
718 gr->obj_loesche_alle(player);
719 gr->mark_image_dirty();
720 welt->access(pos.get_2d())->boden_entfernen(gr);
721 delete gr;
722
723 minimap_t::get_instance()->calc_map_pixel( pos.get_2d() );
724 }
725
726 // Und die Tunnelenden am Schlu�
727 while (!end_list.empty()) {
728 pos = end_list.remove_first();
729
730 grund_t *gr = welt->lookup(pos);
731 if(wegtyp == powerline_wt) {
732 // remove tunnel portals
733 tunnel_t *t = gr->find<tunnel_t>();
734 if(t) {
735 t->cleanup(player);
736 delete t;
737 }
738 if (leitung_t *lt = gr->get_leitung()) {
739 // remove single powerlines
740 if ( (lt->get_ribi() & ~ribi_type(gr->get_grund_hang())) == ribi_t::none ) {
741 lt->cleanup(player);
742 delete lt;
743 }
744 }
745 }
746 else {
747 ribi_t::ribi mask = gr->get_grund_hang()!=slope_t::flat ? ~ribi_type(gr->get_grund_hang()) : ~ribi_type(slope_t::opposite(gr->get_weg_hang()));
748
749 // remove the second way first in the tunnel
750 if(gr->get_weg_nr(1)) {
751 gr->remove_everything_from_way(player,gr->get_weg_nr(1)->get_waytype(),gr->get_weg_nr(1)->get_ribi_unmasked() & mask);
752 }
753 // removes single signals, bridge head, pedestrians, stops, changes catenary etc
754 ribi_t::ribi ribi = gr->get_weg_ribi_unmasked(wegtyp) & mask;
755
756 tunnel_t *t = gr->find<tunnel_t>();
757 uint8 broad_type = t->get_broad_type();
758 gr->remove_everything_from_way(player,wegtyp,ribi); // removes stop and signals correctly
759
760 // remove tunnel portals
761 t = gr->find<tunnel_t>();
762 if(t) {
763 t->cleanup(player);
764 delete t;
765 }
766
767 if( broad_type ) {
768 slope_t::type hang = gr->get_grund_hang();
769 ribi_t::ribi dir = ribi_t::rotate90( ribi_type( hang ) );
770 if( broad_type & 1 ) {
771 const grund_t *gr_l = welt->lookup(pos + dir);
772 tunnel_t* tunnel_l = gr_l ? gr_l->find<tunnel_t>() : NULL;
773 if( tunnel_l ) {
774 tunnel_l->calc_image();
775 }
776 }
777 if( broad_type & 2 ) {
778 const grund_t *gr_r = welt->lookup(pos - dir);
779 tunnel_t* tunnel_r = gr_r ? gr_r->find<tunnel_t>() : NULL;
780 if( tunnel_r ) {
781 tunnel_r->calc_image();
782 }
783 }
784 }
785
786 // corrects the ways
787 weg_t *weg=gr->get_weg_nr(0);
788 if(weg) {
789 // fails if it was previously the last ribi
790 weg->set_desc(weg->get_desc());
791 weg->set_ribi( ribi );
792 if(gr->get_weg_nr(1)) {
793 gr->get_weg_nr(1)->set_ribi( ribi );
794 }
795 }
796 }
797
798 // then add the new ground, copy everything and replace the old one
799 grund_t *gr_new = new boden_t(pos, gr->get_grund_hang());
800 gr_new->take_obj_from( gr );
801 welt->access(pos.get_2d())->kartenboden_setzen(gr_new );
802
803 if(gr_new->get_leitung()) {
804 gr_new->get_leitung()->finish_rd();
805 }
806
807 // recalc image of ground
808 grund_t *kb = welt->access(pos.get_2d()+koord(gr_new->get_grund_hang()))->get_kartenboden();
809 kb->calc_image();
810 kb->set_flag(grund_t::dirty);
811 }
812 return NULL;
813 }
814