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