﻿Index: src/simutrans/builder/wegbauer.cc
===================================================================
--- src/simutrans/builder/wegbauer.cc	(revisi�n: 12125)
+++ src/simutrans/builder/wegbauer.cc	(copia de trabajo)
@@ -627,8 +627,8 @@
 			}
 			if(gb) {
 				// no halt => citybuilding => do not touch
-				// also check for too high buildings ...
-				if(!check_owner(gb->get_owner(),player_builder)  ||  gb->get_tile()->has_upper_storey()) {
+				// the whole building must fit below the deck, not just the crossed tile
+				if(!check_owner(gb->get_owner(),player_builder)  ||  gb->get_tile()->get_desc()->get_height_clearance() > get_way_height_offset(to)) {
 					return false;
 				}
 				// building above houses is expensive ... avoid it!
Index: src/simutrans/builder/brueckenbauer.cc
===================================================================
--- src/simutrans/builder/brueckenbauer.cc	(revisi�n: 12125)
+++ src/simutrans/builder/brueckenbauer.cc	(copia de trabajo)
@@ -11,6 +11,7 @@
 #include "../dataobj/scenario.h"
 
 #include "../descriptor/bridge_desc.h"
+#include "../descriptor/building_desc.h"
 
 #include "../ground/boden.h"
 #include "../ground/brueckenboden.h"
@@ -20,6 +21,7 @@
 
 #include "../obj/bruecke.h"
 #include "../obj/depot.h"
+#include "../obj/gebaeude.h"
 #include "../obj/leitung2.h"
 #include "../obj/pillar.h"
 #include "../obj/signal.h"
@@ -513,6 +515,21 @@
 			return "bridge is too high for its type!";
 		}
 
+		// every building below must fit under the deck ("" lets a higher deck be tried)
+		for (unsigned b = 0; b < pl->get_boden_count(); b++) {
+			grund_t* below = pl->get_boden_bei(b);
+			if (below->get_hoehe() >= height) {
+				continue;
+			}
+			const gebaeude_t* gb = below->find<gebaeude_t>();
+			if (gb == NULL) {
+				gb = below->get_depot();
+			}
+			if (gb  &&  height - below->get_hoehe() < gb->get_tile()->get_desc()->get_height_clearance()) {
+				return "";
+			}
+		}
+
 		if (gr->hat_weg(air_wt)  &&  gr->get_styp(air_wt) == type_runway) {
 			return "No bridges over runways!";
 		}
Index: tests/all_tests.nut
===================================================================
--- tests/all_tests.nut	(revisi�n: 12125)
+++ tests/all_tests.nut	(copia de trabajo)
@@ -27,12 +27,13 @@
 include("tests/test_sign")
 include("tests/test_slope")
 include("tests/test_terraform")
-include("tests/test_transport")
-include("tests/test_trees")
-include("tests/test_way_bridge")
-include("tests/test_way_road")
-include("tests/test_way_runway")
-include("tests/test_way_tram")
+include("tests/test_transport")
+include("tests/test_trees")
+include("tests/test_way_bridge")
+include("tests/test_way_elevated")
+include("tests/test_way_road")
+include("tests/test_way_runway")
+include("tests/test_way_tram")
 include("tests/test_way_tunnel")
 include("tests/test_wayobj")
 
@@ -205,12 +206,16 @@
 	test_way_bridge_planner,
 	test_way_bridge_planner_max_length,
 	test_way_bridge_planner_desc_limits,
-	test_way_bridge_planner_flat_ends,
-	test_way_bridge_planner_ownership,
-	test_way_bridge_planner_forbidden_by_scenario,
-	test_way_road_build_single_tile,
-	test_way_road_build_straight,
-	test_way_road_build_bend,
+	test_way_bridge_planner_flat_ends,
+	test_way_bridge_planner_ownership,
+	test_way_bridge_planner_forbidden_by_scenario,
+	test_way_bridge_over_building,
+	test_way_bridge_over_fitting_stacked_building,
+	test_way_elevated_over_building,
+	test_way_elevated_over_building_footprint,
+	test_way_road_build_single_tile,
+	test_way_road_build_straight,
+	test_way_road_build_bend,
 	test_way_road_build_parallel,
 	test_way_road_build_below_powerline,
 	test_way_road_build_crossing,
Index: tests/tests/test_way_bridge.nut
===================================================================
--- tests/tests/test_way_bridge.nut	(revisi�n: 12125)
+++ tests/tests/test_way_bridge.nut	(copia de trabajo)
@@ -506,12 +506,91 @@
 	ASSERT_EQUAL(setslope(pl, coord3d(3, 5, 0), slope.all_down_slope), null)
 
 	RESET_ALL_PLAYER_FUNDS()
-}
-
-
-function test_way_bridge_build_above_runway()
-{
-	local pl = player_x(0)
+}
+
+
+function test_way_bridge_over_building()
+{
+	local remover = command_x(tool_remove_way)
+	local setslope = command_x.set_slope
+	local pl = player_x(0)
+	local public_pl = player_x(1)
+	local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0]
+	ASSERT_TRUE(bridge_desc != null)
+
+	// same geometry as test_way_bridge_build_above_way, but with a tall building on
+	// the valley floor: the deck leaves less clearance than the building needs
+	ASSERT_EQUAL(command_x(tool_add_city).work(public_pl, coord3d(12, 12, 0), "0"), null)
+
+	ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.south), null)
+	ASSERT_EQUAL(setslope(pl, coord3d(3, 5, 0), slope.north), null)
+
+	// control: over an empty valley the same bridge builds fine
+	ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 2, 0), coord3d(3, 5, 0), bridge_desc), null)
+	ASSERT_EQUAL(remover.work(pl, coord3d(3, 2, 0), coord3d(3, 5, 0), "" + wt_road), null)
+
+	ASSERT_EQUAL(command_x(tool_build_house).work(public_pl, coord3d(3, 3, 0), "1#Theatre"), null)
+	ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(3, 2, 0), coord3d(3, 5, 0), bridge_desc), "")
+	// no deck may have been left over the building at any height
+	ASSERT_TRUE(square_x(3, 3).get_tile_at_height(1) == null)
+	ASSERT_TRUE(square_x(3, 4).get_tile_at_height(1) == null)
+	ASSERT_TRUE(square_x(3, 3).get_tile_at_height(2) == null)
+	ASSERT_TRUE(square_x(3, 4).get_tile_at_height(2) == null)
+
+	ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 3, 0)), null)
+
+	ASSERT_EQUAL(setslope(pl, coord3d(3, 2, 0), slope.all_down_slope), null)
+	ASSERT_EQUAL(setslope(pl, coord3d(3, 5, 0), slope.all_down_slope), null)
+	ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(12, 12, 0)), null)
+	ASSERT_EQUAL(remover.work(public_pl, coord3d(11, 13, 0), coord3d(13, 13, 0), "" + wt_road), null)
+
+	RESET_ALL_PLAYER_FUNDS()
+}
+
+
+function test_way_bridge_over_fitting_stacked_building()
+{
+	local remover = command_x(tool_remove_way)
+	local setslope = command_x.set_slope
+	local pl = player_x(0)
+	local bridge_desc = bridge_desc_x.get_available_bridges(wt_road)[0]
+	local station_desc = building_desc_x.get_available_stations(building_desc_x.station, wt_road, good_desc_x.passenger)[0]
+	ASSERT_TRUE(bridge_desc != null)
+	ASSERT_TRUE(station_desc != null)
+
+	// lower bridge, east-west, with its deck at z=1
+	ASSERT_EQUAL(setslope(pl, coord3d(4, 9, 0), slope.east), null)
+	ASSERT_EQUAL(setslope(pl, coord3d(7, 9, 0), slope.west), null)
+	ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(4, 9, 0), coord3d(7, 9, 0), bridge_desc), null)
+
+	// raised endpoints let a north-south bridge try z=1, then z=2
+	ASSERT_EQUAL(setslope(pl, coord3d(5, 7, 0), slope.all_up_slope), null)
+	ASSERT_EQUAL(setslope(pl, coord3d(5, 11, 0), slope.all_up_slope), null)
+
+	// control: the upper bridge builds over the empty lower deck
+	ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(5, 7, 1), coord3d(5, 11, 1), bridge_desc), null)
+	ASSERT_EQUAL(remover.work(pl, coord3d(5, 7, 1), coord3d(5, 11, 1), "" + wt_road), null)
+
+	// a station that fits exactly in the one-level gap must not be overblocked
+	ASSERT_EQUAL(command_x.build_station(pl, coord3d(5, 9, 1), station_desc), null)
+	ASSERT_EQUAL(command_x.build_bridge(pl, coord3d(5, 7, 1), coord3d(5, 11, 1), bridge_desc), null)
+	ASSERT_TRUE(square_x(5, 9).get_tile_at_height(2) != null)
+
+	ASSERT_EQUAL(remover.work(pl, coord3d(5, 7, 1), coord3d(5, 11, 1), "" + wt_road), null)
+	ASSERT_EQUAL(command_x(tool_remover).work(pl, coord3d(5, 9, 1)), null)
+	ASSERT_EQUAL(remover.work(pl, coord3d(4, 9, 0), coord3d(7, 9, 0), "" + wt_road), null)
+	ASSERT_EQUAL(setslope(pl, coord3d(4, 9, 0), slope.all_down_slope), null)
+	ASSERT_EQUAL(setslope(pl, coord3d(7, 9, 0), slope.all_down_slope), null)
+	ASSERT_EQUAL(setslope(pl, coord3d(5, 7, 1), slope.all_down_slope), null)
+	ASSERT_EQUAL(setslope(pl, coord3d(5, 11, 1), slope.all_down_slope), null)
+
+	RESET_ALL_PLAYER_FUNDS()
+}
+
+
+function test_way_bridge_build_above_runway()
+{
+	local pl = player_x(0)
 	local wayremover = command_x(tool_remove_way)
 	local taxiway = way_desc_x.get_available_ways(wt_air, st_flat)[0]
 	local runway = way_desc_x.get_available_ways(wt_air, st_elevated)[0]
Index: tests/tests/test_way_elevated.nut
===================================================================
--- tests/tests/test_way_elevated.nut	(nonexistent)
+++ tests/tests/test_way_elevated.nut	(copia de trabajo)
@@ -0,0 +1,69 @@
+//
+// This file is part of the Simutrans project under the Artistic License.
+// (see LICENSE.txt)
+//
+
+// An elevated way must not be built over a building that needs more clearance
+// than the deck leaves. pak64 assets: Theatre = 2x2 drawn two levels tall,
+// Tennis_Court = 2x2 drawn flat.
+
+function test_way_elevated_over_building()
+{
+	local public_pl = player_x(1)
+
+	local elevated = way_desc_x.get_available_ways(wt_monorail, st_elevated)[0]
+	ASSERT_TRUE(elevated != null)
+
+	ASSERT_EQUAL(command_x(tool_add_city).work(public_pl, coord3d(8, 8, 0), "0"), null)
+
+	// (A) control: over empty ground the same two-tile span is allowed
+	ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 4, 0), coord3d(4, 4, 0), elevated, true), null)
+	ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(3, 4, 1), coord3d(4, 4, 1), "" + wt_monorail), null)
+
+	// (B) Theatre draws two levels although no tile carries a second image row,
+	//     so the old has_upper_storey() test let a way through it
+	ASSERT_EQUAL(command_x(tool_build_house).work(public_pl, coord3d(3, 4, 0), "1#Theatre"), null)
+	ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 4, 0), coord3d(4, 4, 0), elevated, true), "")
+	ASSERT_TRUE(square_x(3, 4).get_tile_at_height(1) == null)
+	ASSERT_TRUE(square_x(4, 4).get_tile_at_height(1) == null)
+	ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null) // removes the whole 2x2
+
+	// (C) a building that does fit below the deck stays allowed
+	ASSERT_EQUAL(command_x(tool_build_house).work(public_pl, coord3d(3, 4, 0), "1#Tennis_Court"), null)
+	ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 4, 0), coord3d(4, 4, 0), elevated, true), null)
+	ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(3, 4, 1), coord3d(4, 4, 1), "" + wt_monorail), null)
+	ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null)
+
+	ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(8, 8, 0)), null) // remove city
+	ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(7, 9, 0), coord3d(9, 9, 0), "" + wt_road), null)
+
+	RESET_ALL_PLAYER_FUNDS()
+}
+
+
+function test_way_elevated_over_building_footprint()
+{
+	local public_pl = player_x(1)
+
+	local elevated = way_desc_x.get_available_ways(wt_monorail, st_elevated)[0]
+	ASSERT_TRUE(elevated != null)
+
+	ASSERT_EQUAL(command_x(tool_add_city).work(public_pl, coord3d(8, 8, 0), "0"), null)
+
+	// the far row of the 2x2 Theatre carries no anchor, and the height belongs to
+	// the building and not to the tile, so crossing it must be refused as well
+	ASSERT_EQUAL(command_x(tool_build_house).work(public_pl, coord3d(3, 4, 0), "1#Theatre"), null)
+	ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 5, 0), coord3d(4, 5, 0), elevated, true), "")
+	ASSERT_TRUE(square_x(3, 5).get_tile_at_height(1) == null)
+	ASSERT_TRUE(square_x(4, 5).get_tile_at_height(1) == null)
+
+	// control: the same span one tile further, off the building, is allowed
+	ASSERT_EQUAL(command_x.build_way(public_pl, coord3d(3, 6, 0), coord3d(4, 6, 0), elevated, true), null)
+	ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(3, 6, 1), coord3d(4, 6, 1), "" + wt_monorail), null)
+
+	ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(3, 4, 0)), null)
+	ASSERT_EQUAL(command_x(tool_remover).work(public_pl, coord3d(8, 8, 0)), null)
+	ASSERT_EQUAL(command_x(tool_remove_way).work(public_pl, coord3d(7, 9, 0), coord3d(9, 9, 0), "" + wt_road), null)
+
+	RESET_ALL_PLAYER_FUNDS()
+}
