changeset 8485:a4493021a8f3 draft

(svn r12060) -Fix: do not clear tiles when the town won't be able to build any buildings anyway -Fix: allow building 2x2 building on slopes if not explicitly forbidden -Fix: it was possible to build 2x1 and 1x2 buildings on slopes even if it was not allowed
author smatz <smatz@openttd.org>
date Mon, 04 Feb 2008 22:15:48 +0000
parents c61265fecd4c
children 99f058177c49
files src/town_cmd.cpp src/town_map.h
diffstat 2 files changed, 124 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -117,7 +117,6 @@
 };
 
 static bool BuildTownHouse(Town *t, TileIndex tile);
-static void DoBuildTownHouse(Town *t, TileIndex tile);
 
 static void TownDrawHouseLift(const TileInfo *ti)
 {
@@ -493,7 +492,7 @@
 		ClearTownHouse(t, tile);
 
 		/* Rebuild with another house? */
-		if (GB(r, 24, 8) >= 12) DoBuildTownHouse(t, tile);
+		if (GB(r, 24, 8) >= 12) BuildTownHouse(t, tile);
 	}
 
 	_current_player = OWNER_NONE;
@@ -1586,29 +1585,6 @@
 	return true;
 }
 
-static bool CheckBuildHouseMode(TileIndex tile, Slope tileh, int mode)
-{
-	int b;
-	Slope slope;
-
-	static const Slope _masks[8] = {
-		SLOPE_NE,  SLOPE_SW,  SLOPE_NW,  SLOPE_SE,
-		SLOPE_SW,  SLOPE_NE,  SLOPE_SE,  SLOPE_NW,
-	};
-
-	slope = GetTileSlope(tile, NULL);
-	if (IsSteepSlope(slope)) return false;
-
-	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return false;
-
-	b = 0;
-	if ((slope != SLOPE_FLAT && ~slope & _masks[mode])) b = ~b;
-	if ((tileh != SLOPE_FLAT && ~tileh & _masks[mode + 4])) b = ~b;
-	if (b)
-		return false;
-
-	return CmdSucceeded(DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_LANDSCAPE_CLEAR));
-}
 
 /** Returns the bit corresponding to the town zone of the specified tile
  * @param t Town on which radius is to be found
@@ -1631,32 +1607,115 @@
 	return smallest;
 }
 
-static bool CheckFree2x2Area(TileIndex tile)
+/**
+ * Clears tile and builds a house or house part.
+ * @param t tile index
+ * @param tid Town index
+ * @param counter of construction step
+ * @param stage of construction (used for drawing)
+ * @param type of house. Index into house specs array
+ * @param random_bits required for newgrf houses
+ * @pre house can be built here
+ */
+
+static inline void ClearMakeHouseTile(TileIndex tile, TownID tid, byte counter, byte stage, HouseID type, byte random_bits)
+{
+	CommandCost cc = DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_LANDSCAPE_CLEAR);
+	assert(CmdSucceeded(cc));
+
+	MakeHouseTile(tile, tid, counter, stage, type, random_bits);
+}
+
+/**
+ * Write house information into the map. For houses > 1 tile, all tiles are marked.
+ * @param t tile index
+ * @param tid Town index
+ * @param counter of construction step
+ * @param stage of construction (used for drawing)
+ * @param type of house. Index into house specs array
+ * @param random_bits required for newgrf houses
+ * @pre house can be built here
+ */
+static void MakeTownHouse(TileIndex t, TownID tid, byte counter, byte stage, HouseID type, byte random_bits)
 {
-	int i;
-
-	static const TileIndexDiffC _tile_add[] = {
-		{0    , 0    },
-		{0 - 0, 1 - 0},
-		{1 - 0, 0 - 1},
-		{1 - 1, 1 - 0}
-	};
-
-	for (i = 0; i != 4; i++) {
-		tile += ToTileIndexDiff(_tile_add[i]);
-
-		if (GetTileSlope(tile, NULL) != SLOPE_FLAT) return false;
-
-		if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return false;
-
-		if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER | DC_FORCETEST, CMD_LANDSCAPE_CLEAR)))
-			return false;
+	BuildingFlags size = GetHouseSpecs(type)->building_flags;
+
+	ClearMakeHouseTile(t, tid, counter, stage, type, random_bits);
+	if (size & BUILDING_2_TILES_Y)   ClearMakeHouseTile(t + TileDiffXY(0, 1), tid, counter, stage, ++type, random_bits);
+	if (size & BUILDING_2_TILES_X)   ClearMakeHouseTile(t + TileDiffXY(1, 0), tid, counter, stage, ++type, random_bits);
+	if (size & BUILDING_HAS_4_TILES) ClearMakeHouseTile(t + TileDiffXY(1, 1), tid, counter, stage, ++type, random_bits);
+}
+
+
+/**
+ * Checks if a house can be built here. Important is slope, bridge above
+ * and ability to clear the land.
+ * @param tile tile to check
+ * @param noslope are slopes (foundations) allowed?
+ * @return true iff house can be built here
+ */
+static inline bool CanBuildHouseHere(TileIndex tile, bool noslope)
+{
+	/* cannot build on these slopes... */
+	Slope slope = GetTileSlope(tile, NULL);
+	if ((noslope && slope != SLOPE_FLAT) || IsSteepSlope(slope)) return false;
+
+	/* building under a bridge? */
+	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return false;
+
+	/* can we clear the land? */
+	return CmdSucceeded(DoCommand(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_LANDSCAPE_CLEAR));
+}
+
+
+/**
+ * Checks if a house can be built at this tile, must have the same max z as parameter.
+ * @param tile tile to check
+ * @param z max z of this tile so more parts of a house are at the same height (with foundation)
+ * @param noslope are slopes (foundations) allowed?
+ * @return true iff house can be built here
+ * @see CanBuildHouseHere()
+ */
+static inline bool CheckBuildHouseSameZ(TileIndex tile, uint z, bool noslope)
+{
+	if (!CanBuildHouseHere(tile, noslope)) return false;
+
+	/* if building on slopes is allowed, there will be flattening foundation (to tile max z) */
+	if (GetTileMaxZ(tile) != z) return false;
+
+	return true;
+}
+
+
+/**
+ * Checks if a house of size 2x2 can be built at this tile
+ * @param tile tile, N corner
+ * @param z maximum tile z so all tile have the same max z
+ * @param noslope are slopes (foundations) allowed?
+ * @return true iff house can be built
+ * @see CheckBuildHouseSameZ()
+ */
+static bool CheckFree2x2Area(TileIndex tile, uint z, bool noslope)
+{
+	/* we need to check this tile too because we can be at different tile now */
+	if (!CheckBuildHouseSameZ(tile, z, noslope)) return false;
+
+	for (DiagDirection d = DIAGDIR_SE; d < DIAGDIR_END; d++) {
+		tile += TileOffsByDiagDir(d);
+		if (!CheckBuildHouseSameZ(tile, z, noslope)) return false;
 	}
 
 	return true;
 }
 
-static void DoBuildTownHouse(Town *t, TileIndex tile)
+
+/**
+ * Tries to build a house at this tile
+ * @param t town the house will belong to
+ * @param tile where the house will be built
+ * @return false iff no house can be built at this tile
+ */
+static bool BuildTownHouse(Town *t, TileIndex tile)
 {
 	int i;
 	uint bitmask;
@@ -1666,6 +1725,9 @@
 	uint oneof = 0;
 	HouseSpec *hs;
 
+	/* no house allowed at all, bail out */
+	if (!CanBuildHouseHere(tile, false)) return false;
+
 	/* Above snow? */
 	slope = GetTileSlope(tile, &z);
 
@@ -1702,6 +1764,8 @@
 			}
 		}
 
+		uint maxz = GetTileMaxZ(tile);
+
 		for (;;) {
 			if (_loaded_newgrf_features.has_newhouses) {
 				uint r = RandomRange(probability_max);
@@ -1742,27 +1806,30 @@
 			if (HASBITS(t->flags12 , oneof)) continue;
 
 			/* Make sure there is no slope? */
-			if (hs->building_flags & TILE_NOT_SLOPED && slope != SLOPE_FLAT) continue;
+			bool noslope = (hs->building_flags & TILE_NOT_SLOPED) != 0;
+			if (noslope && slope != SLOPE_FLAT) continue;
 
 			if (hs->building_flags & TILE_SIZE_2x2) {
-				if (CheckFree2x2Area(tile) ||
-						CheckFree2x2Area(tile += TileDiffXY(-1,  0)) ||
-						CheckFree2x2Area(tile += TileDiffXY( 0, -1)) ||
-						CheckFree2x2Area(tile += TileDiffXY( 1,  0))) {
+				if (CheckFree2x2Area(tile, maxz, noslope) ||
+						CheckFree2x2Area(tile += TileDiffXY(-1,  0), maxz, noslope) ||
+						CheckFree2x2Area(tile += TileDiffXY( 0, -1), maxz, noslope) ||
+						CheckFree2x2Area(tile += TileDiffXY( 1,  0), maxz, noslope)) {
 					break;
 				}
+				/* return to original tile */
 				tile += TileDiffXY(0, 1);
 			} else if (hs->building_flags & TILE_SIZE_2x1) {
-				if (CheckBuildHouseMode(tile + TileDiffXY(1, 0), slope, 0)) break;
-
-				if (CheckBuildHouseMode(tile + TileDiffXY(-1, 0), slope, 1)) {
+				/* 'tile' is already checked above - CanBuildHouseHere() and slope test */
+				if (CheckBuildHouseSameZ(tile + TileDiffXY(1, 0), maxz, noslope)) break;
+
+				if (CheckBuildHouseSameZ(tile + TileDiffXY(-1, 0), maxz, noslope)) {
 					tile += TileDiffXY(-1, 0);
 					break;
 				}
 			} else if (hs->building_flags & TILE_SIZE_1x2) {
-				if (CheckBuildHouseMode(tile + TileDiffXY(0, 1), slope, 2)) break;
-
-				if (CheckBuildHouseMode(tile + TileDiffXY(0, -1), slope, 3)) {
+				if (CheckBuildHouseSameZ(tile + TileDiffXY(0, 1), maxz, noslope)) break;
+
+				if (CheckBuildHouseSameZ(tile + TileDiffXY(0, -1), maxz, noslope)) {
 					tile += TileDiffXY(0, -1);
 					break;
 				}
@@ -1795,19 +1862,7 @@
 		}
 		MakeTownHouse(tile, t->index, construction_counter, construction_stage, house, Random());
 	}
-}
-
-static bool BuildTownHouse(Town *t, TileIndex tile)
-{
-	CommandCost r;
-
-	if (IsSteepSlope(GetTileSlope(tile, NULL))) return false;
-	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return false;
-
-	r = DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_LANDSCAPE_CLEAR);
-	if (CmdFailed(r)) return false;
-
-	DoBuildTownHouse(t, tile);
+
 	return true;
 }
 
--- a/src/town_map.h
+++ b/src/town_map.h
@@ -211,26 +211,6 @@
 }
 
 /**
- * Helper function for MakeHouseTile.
- * It is called  for each tile of a multi-tile house.
- * Parametes are the same.
- * @param t tile index
- * @param tid Town index
- * @param counter of construction step
- * @param stage of construction (used for drawing)
- * @param type of house.  Index into house specs array
- * @param random_bits required for newgrf houses
- */
-static inline void MakeTownHouse(TileIndex t, TownID tid, byte counter, byte stage, HouseID type, byte random_bits)
-{
-	BuildingFlags size = GetHouseSpecs(type)->building_flags;
-	MakeHouseTile(t, tid, counter, stage, type, random_bits);
-	if (size & BUILDING_2_TILES_Y)   MakeHouseTile(t + TileDiffXY(0, 1), tid, counter, stage, ++type, random_bits);
-	if (size & BUILDING_2_TILES_X)   MakeHouseTile(t + TileDiffXY(1, 0), tid, counter, stage, ++type, random_bits);
-	if (size & BUILDING_HAS_4_TILES) MakeHouseTile(t + TileDiffXY(1, 1), tid, counter, stage, ++type, random_bits);
-}
-
-/**
  * House Construction Scheme.
  *  Construction counter, for buildings under construction. Incremented on every
  *  periodic tile processing.