src/town_cmd.cpp
branchNewGRF_ports
changeset 6878 7d1ff2f621c7
parent 6877 889301acc299
child 10184 fcf5fb2548eb
--- a/src/town_cmd.cpp	Sun Feb 03 20:34:26 2008 +0000
+++ b/src/town_cmd.cpp	Mon Mar 10 15:26:39 2008 +0000
@@ -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;
@@ -593,7 +592,7 @@
 	td->owner = OWNER_TOWN;
 }
 
-static uint32 GetTileTrackStatus_Town(TileIndex tile, TransportType mode, uint sub_mode)
+static TrackStatus GetTileTrackStatus_Town(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
 {
 	/* not used */
 	return 0;
@@ -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,155 +1607,347 @@
 	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)
 {
-	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;
+	#if !defined(NDEBUG) || defined(WITH_ASSERT)
+		CommandCost cc =
+	#endif /* !defined(NDEBUG) || defined(WITH_ASSERT) */
+
+	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)
+{
+	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 town town that is checking
+ * @param noslope are slopes (foundations) allowed?
+ * @return true iff house can be built here
+ */
+static inline bool CanBuildHouseHere(TileIndex tile, TownID town, 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;
+
+	/* do not try to build over house owned by another town */
+	if (IsTileType(tile, MP_HOUSE) && GetTownIndex(tile) != town) 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 town town that is checking
+ * @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, TownID town, uint z, bool noslope)
+{
+	if (!CanBuildHouseHere(tile, town, 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 town town that is checking
+ * @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, TownID town, uint z, bool noslope)
+{
+	/* we need to check this tile too because we can be at different tile now */
+	if (!CheckBuildHouseSameZ(tile, town, z, noslope)) return false;
+
+	for (DiagDirection d = DIAGDIR_SE; d < DIAGDIR_END; d++) {
+		tile += TileOffsByDiagDir(d);
+		if (!CheckBuildHouseSameZ(tile, town, z, noslope)) return false;
 	}
 
 	return true;
 }
 
-static void DoBuildTownHouse(Town *t, TileIndex tile)
+
+/**
+ * Checks if current town layout allows building here
+ * @param t town
+ * @param tile tile to check
+ * @return true iff town layout allows building here
+ * @note see layouts
+ */
+static inline bool TownLayoutAllowsHouseHere(Town *t, TileIndex tile)
 {
-	int i;
-	uint bitmask;
-	HouseID house;
-	Slope slope;
+	TileIndexDiffC grid_pos = TileIndexToTileIndexDiffC(t->xy, tile);
+
+	switch (_patches.town_layout) {
+		case TL_2X2_GRID:
+			if ((grid_pos.x % 3) == 0 || (grid_pos.y % 3) == 0) return false;
+			break;
+
+		case TL_3X3_GRID:
+			if ((grid_pos.x % 4) == 0 || (grid_pos.y % 4) == 0) return false;
+			break;
+
+		default:
+			break;
+	}
+
+	return true;
+}
+
+
+/**
+ * Checks if current town layout allows 2x2 building here
+ * @param t town
+ * @param tile tile to check
+ * @return true iff town layout allows 2x2 building here
+ * @note see layouts
+ */
+static inline bool TownLayoutAllows2x2HouseHere(Town *t, TileIndex tile)
+{
+	/* MapSize() is sure dividable by both MapSizeX() and MapSizeY(),
+	 * so to do only one memory access, use MapSize() */
+	uint dx = MapSize() + TileX(t->xy) - TileX(tile);
+	uint dy = MapSize() + TileY(t->xy) - TileY(tile);
+
+	switch (_patches.town_layout) {
+		case TL_2X2_GRID:
+			if ((dx % 3) != 0 || (dy % 3) != 0) return false;
+			break;
+
+		case TL_3X3_GRID:
+			if ((dx % 4) < 2 || (dy % 4) < 2) return false;
+			break;
+
+		default:
+			break;
+	}
+
+	return true;
+}
+
+
+/**
+ * Checks if 1x2 or 2x1 building is allowed here, also takes into account current town layout
+ * Also, tests both building positions that occupy this tile
+ * @param tile tile where the building should be built
+ * @param t town
+ * @param maxz all tiles should have the same height
+ * @param noslope are slopes forbidden?
+ * @param second diagdir from first tile to second tile
+ **/
+static bool CheckTownBuild2House(TileIndex *tile, Town *t, uint maxz, bool noslope, DiagDirection second)
+{
+	/* 'tile' is already checked in BuildTownHouse() - CanBuildHouseHere() and slope test */
+
+	TileIndex tile2 = *tile + TileOffsByDiagDir(second);
+	if (TownLayoutAllowsHouseHere(t, tile2) && CheckBuildHouseSameZ(tile2, t->index, maxz, noslope)) return true;
+
+	tile2 = *tile + TileOffsByDiagDir(ReverseDiagDir(second));
+	if (TownLayoutAllowsHouseHere(t, tile2) && CheckBuildHouseSameZ(tile2, t->index, maxz, noslope)) {
+		*tile = tile2;
+		return true;
+	}
+
+	return false;
+}
+
+
+/**
+ * Checks if 2x2 building is allowed here, also takes into account current town layout
+ * Also, tests all four building positions that occupy this tile
+ * @param tile tile where the building should be built
+ * @param t town
+ * @param maxz all tiles should have the same height
+ * @param noslope are slopes forbidden?
+ **/
+static bool CheckTownBuild2x2House(TileIndex *tile, Town *t, uint maxz, bool noslope)
+{
+	TileIndex tile2 = *tile;
+
+	for (DiagDirection d = DIAGDIR_SE;;d++) { // 'd' goes through DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_END
+		if (TownLayoutAllows2x2HouseHere(t, tile2) && CheckFree2x2Area(tile2, t->index, maxz, noslope)) {
+			*tile = tile2;
+			return true;
+		}
+		if (d == DIAGDIR_END) break;
+		tile2 += TileOffsByDiagDir(ReverseDiagDir(d)); // go clockwise
+	}
+
+	return false;
+}
+
+
+/**
+ * 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)
+{
+	/* forbidden building here by town layout */
+	if (!TownLayoutAllowsHouseHere(t, tile)) return false;
+
+	/* no house allowed at all, bail out */
+	if (!CanBuildHouseHere(tile, t->index, false)) return false;
+
 	uint z;
-	uint oneof = 0;
-	HouseSpec *hs;
-
-	/* Above snow? */
-	slope = GetTileSlope(tile, &z);
+	Slope slope = GetTileSlope(tile, &z);
 
 	/* Get the town zone type of the current tile, as well as the climate.
 	 * This will allow to easily compare with the specs of the new house to build */
-	{
-		HouseZonesBits rad = GetTownRadiusGroup(t, tile);
-
-		int land = _opt.landscape;
-		if (land == LT_ARCTIC && z >= _opt.snow_line) land = -1;
-
-		bitmask = (1 << rad) + (1 << (land + 12));
-	}
+	HouseZonesBits rad = GetTownRadiusGroup(t, tile);
+
+	/* Above snow? */
+	int land = _opt.landscape;
+	if (land == LT_ARCTIC && z >= _opt.snow_line) land = -1;
+
+	uint bitmask = (1 << rad) + (1 << (land + 12));
 
 	/* bits 0-4 are used
 	 * bits 11-15 are used
 	 * bits 5-10 are not used. */
-	{
-		HouseID houses[HOUSE_MAX];
-		int num = 0;
-		uint cumulative_probs[HOUSE_MAX];
-		uint probability_max = 0;
-
-		/* Generate a list of all possible houses that can be built. */
-		for (i = 0; i < HOUSE_MAX; i++) {
-			hs = GetHouseSpecs(i);
-			/* Verify that the candidate house spec matches the current tile status */
-			if ((~hs->building_availability & bitmask) == 0 && hs->enabled) {
-				if (_loaded_newgrf_features.has_newhouses) {
-					probability_max += hs->probability;
-					cumulative_probs[num] = probability_max;
-				}
-				houses[num++] = (HouseID)i;
+	HouseID houses[HOUSE_MAX];
+	uint num = 0;
+	uint probs[HOUSE_MAX];
+	uint probability_max = 0;
+
+	/* Generate a list of all possible houses that can be built. */
+	for (uint i = 0; i < HOUSE_MAX; i++) {
+		HouseSpec *hs = GetHouseSpecs(i);
+		/* Verify that the candidate house spec matches the current tile status */
+		if ((~hs->building_availability & bitmask) == 0 && hs->enabled) {
+			/* Without NewHouses, all houses have probability '1' */
+			uint cur_prob = (_loaded_newgrf_features.has_newhouses ? hs->probability : 1);
+			probability_max += cur_prob;
+			probs[num] = cur_prob;
+			houses[num++] = (HouseID)i;
+		}
+	}
+
+	uint maxz = GetTileMaxZ(tile);
+
+	while (probability_max > 0) {
+		uint r = RandomRange(probability_max);
+		uint i;
+		for (i = 0; i < num; i++) {
+			if (probs[i] > r) break;
+			r -= probs[i];
+		}
+
+		HouseID house = houses[i];
+		probability_max -= probs[i];
+
+		/* remove tested house from the set */
+		num--;
+		houses[i] = houses[num];
+		probs[i] = probs[num];
+
+		HouseSpec *hs = GetHouseSpecs(house);
+
+		if (_loaded_newgrf_features.has_newhouses) {
+			if (hs->override != 0) {
+				house = hs->override;
+				hs = GetHouseSpecs(house);
+			}
+
+			if ((hs->extra_flags & BUILDING_IS_HISTORICAL) && !_generating_world) continue;
+
+			if (HasBit(hs->callback_mask, CBM_HOUSE_ALLOW_CONSTRUCTION)) {
+				uint16 callback_res = GetHouseCallback(CBID_HOUSE_ALLOW_CONSTRUCTION, 0, 0, house, t, tile);
+				if (callback_res != CALLBACK_FAILED && callback_res == 0) continue;
 			}
 		}
 
-		for (;;) {
-			if (_loaded_newgrf_features.has_newhouses) {
-				uint r = RandomRange(probability_max);
-				for (i = 0; i < num; i++) if (cumulative_probs[i] >= r) break;
-
-				house = houses[i];
-			} else {
-				house = houses[RandomRange(num)];
-			}
-
-			hs = GetHouseSpecs(house);
-
-			if (_loaded_newgrf_features.has_newhouses) {
-				if (hs->override != 0) {
-					house = hs->override;
-					hs = GetHouseSpecs(house);
-				}
-
-				if ((hs->extra_flags & BUILDING_IS_HISTORICAL) && !_generating_world) continue;
-
-				if (HasBit(hs->callback_mask, CBM_HOUSE_ALLOW_CONSTRUCTION)) {
-					uint16 callback_res = GetHouseCallback(CBID_HOUSE_ALLOW_CONSTRUCTION, 0, 0, house, t, tile);
-					if (callback_res != CALLBACK_FAILED && callback_res == 0) continue;
-				}
-			}
-
-			if (_cur_year < hs->min_date || _cur_year > hs->max_date) continue;
-
-			/* Special houses that there can be only one of. */
-			if (hs->building_flags & BUILDING_IS_CHURCH) {
-				SetBit(oneof, TOWN_HAS_CHURCH);
-			} else if (hs->building_flags & BUILDING_IS_STADIUM) {
-				SetBit(oneof, TOWN_HAS_STADIUM);
-			} else {
-				oneof = 0;
-			}
-
-			if (HASBITS(t->flags12 , oneof)) continue;
-
-			/* Make sure there is no slope? */
-			if (hs->building_flags & TILE_NOT_SLOPED && 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))) {
-					break;
-				}
-				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 += 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)) {
-					tile += TileDiffXY(0, -1);
-					break;
-				}
-			} else {
-				break;
-			}
+		if (_cur_year < hs->min_date || _cur_year > hs->max_date) continue;
+
+		/* Special houses that there can be only one of. */
+		uint oneof = 0;
+
+		if (hs->building_flags & BUILDING_IS_CHURCH) {
+			SetBit(oneof, TOWN_HAS_CHURCH);
+		} else if (hs->building_flags & BUILDING_IS_STADIUM) {
+			SetBit(oneof, TOWN_HAS_STADIUM);
 		}
-	}
-
-	t->num_houses++;
-	IncreaseBuildingCount(t, house);
-
-	/* Special houses that there can be only one of. */
-	t->flags12 |= oneof;
-
-	{
-		byte construction_counter = 0, construction_stage = 0;
+
+		if (HASBITS(t->flags12 , oneof)) continue;
+
+		/* Make sure there is no slope? */
+		bool noslope = (hs->building_flags & TILE_NOT_SLOPED) != 0;
+		if (noslope && slope != SLOPE_FLAT) continue;
+
+		if (hs->building_flags & TILE_SIZE_2x2) {
+			if (!CheckTownBuild2x2House(&tile, t, maxz, noslope)) continue;
+		} else if (hs->building_flags & TILE_SIZE_2x1) {
+			if (!CheckTownBuild2House(&tile, t, maxz, noslope, DIAGDIR_SW)) continue;
+		} else if (hs->building_flags & TILE_SIZE_1x2) {
+			if (!CheckTownBuild2House(&tile, t, maxz, noslope, DIAGDIR_SE)) continue;
+		} else {
+			/* 1x1 house checks are already done */
+		}
+
+		/* build the house */
+		t->num_houses++;
+		IncreaseBuildingCount(t, house);
+
+		/* Special houses that there can be only one of. */
+		t->flags12 |= oneof;
+
+		byte construction_counter = 0;
+		byte construction_stage = 0;
 
 		if (_generating_world) {
 			uint32 r = Random();
@@ -1793,22 +1961,13 @@
 				construction_counter = GB(r, 2, 2);
 			}
 		}
+
 		MakeTownHouse(tile, t->index, construction_counter, construction_stage, house, Random());
+
+		return true;
 	}
-}
-
-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;
+
+	return false;
 }