src/clear_cmd.cpp
changeset 7983 06d272a9618b
parent 7976 5d93386b748b
child 7990 70039e33e893
equal deleted inserted replaced
7982:539e32cc37ce 7983:06d272a9618b
    44 	TileIndex tile_table[TERRAFORMER_TILE_TABLE_SIZE];           ///< Dirty tiles, i.e. at least one corner changed.
    44 	TileIndex tile_table[TERRAFORMER_TILE_TABLE_SIZE];           ///< Dirty tiles, i.e. at least one corner changed.
    45 	TerraformerHeightMod modheight[TERRAFORMER_MODHEIGHT_SIZE];  ///< Height modifications.
    45 	TerraformerHeightMod modheight[TERRAFORMER_MODHEIGHT_SIZE];  ///< Height modifications.
    46 };
    46 };
    47 
    47 
    48 /**
    48 /**
    49  * Tests if a tile has already been terraformed.
       
    50  *
       
    51  * @param ts TerraformerState.
       
    52  * @param tile Tile.
       
    53  * @return Tile not yet terraformed (1), tile already terraformed (0), void land (-1).
       
    54  */
       
    55 static int TerraformAllowTileProcess(TerraformerState *ts, TileIndex tile)
       
    56 {
       
    57 	TileIndex *t;
       
    58 	int count;
       
    59 
       
    60 	if (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY()) return -1;
       
    61 
       
    62 	t = ts->tile_table;
       
    63 	for (count = ts->tile_table_count; count != 0; count--, t++) {
       
    64 		if (*t == tile) return 0;
       
    65 	}
       
    66 
       
    67 	return 1;
       
    68 }
       
    69 
       
    70 /**
       
    71  * Gets the TileHeight (height of north corner) of a tile as of current terraforming progress.
    49  * Gets the TileHeight (height of north corner) of a tile as of current terraforming progress.
    72  *
    50  *
    73  * @param ts TerraformerState.
    51  * @param ts TerraformerState.
    74  * @param tile Tile.
    52  * @param tile Tile.
    75  * @return TileHeight.
    53  * @return TileHeight.
   122 	TerraformAddDirtyTile(ts, tile + TileDiffXY(-1,  0));
   100 	TerraformAddDirtyTile(ts, tile + TileDiffXY(-1,  0));
   123 	TerraformAddDirtyTile(ts, tile);
   101 	TerraformAddDirtyTile(ts, tile);
   124 }
   102 }
   125 
   103 
   126 /**
   104 /**
   127  * Checks if a tile can be terraformed.
   105  * Checks if a tile can be terraformed, perform tiletype specific things (like clearing the tile) and compute their extra-cost.
   128  * Extra costs for terraforming (like clearing the tile) are added to ts->cost.
       
   129  *
   106  *
   130  * @param ts TerraformerState.
   107  * @param ts TerraformerState.
   131  * @param tile Tile.
   108  * @param tile Tile.
   132  * @param mode Affected corner: 0 = north, 1 = east, 2 = south, 3 = west.
   109  * @param mode Affected corner: 0 = north, 1 = east, 2 = south, 3 = west.
   133  * @return 0 on success, else -1
   110  * @return Error message or extra-cost.
   134  */
   111  */
   135 static int TerraformProc(TerraformerState *ts, TileIndex tile, int mode)
   112 static CommandCost TerraformProc(TerraformerState *ts, TileIndex tile, int mode)
   136 {
   113 {
   137 	int r;
       
   138 	CommandCost ret;
       
   139 
       
   140 	assert(tile < MapSize());
       
   141 
       
   142 	/* Check for void land, and if the tile was already involved in a terraforming step, (i.e. is already cleared). */
       
   143 	r = TerraformAllowTileProcess(ts, tile);
       
   144 	if (r <= 0) return r;
       
   145 
       
   146 	/* Check if a tile can be terraformed. */
   114 	/* Check if a tile can be terraformed. */
   147 	if (IsTileType(tile, MP_RAILWAY)) {
   115 	if (IsTileType(tile, MP_RAILWAY)) {
   148 		static const TrackBits safe_track[] = { TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER, TRACK_BIT_RIGHT };
   116 		static const TrackBits safe_track[] = { TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER, TRACK_BIT_RIGHT };
   149 		static const Slope unsafe_slope[] = { SLOPE_S, SLOPE_W, SLOPE_N, SLOPE_E };
   117 		static const Slope unsafe_slope[] = { SLOPE_S, SLOPE_W, SLOPE_N, SLOPE_E };
   150 
   118 
   157 		tileh = GetTileSlope(tile, &z);
   125 		tileh = GetTileSlope(tile, &z);
   158 		if (tileh == unsafe_slope[mode] ||
   126 		if (tileh == unsafe_slope[mode] ||
   159 				tileh == (SLOPE_STEEP | ComplementSlope(unsafe_slope[mode]))) {
   127 				tileh == (SLOPE_STEEP | ComplementSlope(unsafe_slope[mode]))) {
   160 			_terraform_err_tile = tile;
   128 			_terraform_err_tile = tile;
   161 			_error_message = STR_1008_MUST_REMOVE_RAILROAD_TRACK;
   129 			_error_message = STR_1008_MUST_REMOVE_RAILROAD_TRACK;
   162 			return -1;
   130 			return CMD_ERROR;
   163 		}
   131 		}
   164 
   132 
   165 		/* If we have a single diagonal track there, the other side of
   133 		/* If we have a single diagonal track there, the other side of
   166 		 * tile can be terraformed. */
   134 		 * tile can be terraformed. */
   167 		if (IsPlainRailTile(tile) && GetTrackBits(tile) == safe_track[mode]) {
   135 		if (IsPlainRailTile(tile) && GetTrackBits(tile) == safe_track[mode]) {
   168 			/* If terraforming downwards prevent damaging a potential tunnel below.
   136 			/* Allow terraforming. */
   169 			 * This check is only necessary for flat tiles, because if the tile is
   137 			return CommandCost();
   170 			 * non-flat, then the corner opposing the rail is raised. Only this corner
       
   171 			 * can be lowered and this is a safe action
       
   172 			 */
       
   173 			if (tileh == SLOPE_FLAT &&
       
   174 					ts->direction == -1 &&
       
   175 					IsTunnelInWay(tile, z - TILE_HEIGHT)) {
       
   176 				_terraform_err_tile = tile;
       
   177 				_error_message = STR_1002_EXCAVATION_WOULD_DAMAGE;
       
   178 				return -1;
       
   179 			}
       
   180 			/* Allow terraforming.
       
   181 			 * The tile is not added to the "dirty"-list, because it needs to be checked again in further terraforming steps.
       
   182 			 * However in the end it is missing in the list, so we have to add it later.
       
   183 			 */
       
   184 			return 0;
       
   185 		}
   138 		}
   186 	}
   139 	}
   187 
   140 
   188 	/* Canals can't be terraformed */
   141 	/* Canals can't be terraformed */
   189 	if (IsClearWaterTile(tile) && IsCanal(tile)) {
   142 	if (IsClearWaterTile(tile) && IsCanal(tile)) {
   190 		_terraform_err_tile = tile;
   143 		_terraform_err_tile = tile;
   191 		_error_message = STR_MUST_DEMOLISH_CANAL_FIRST;
   144 		_error_message = STR_MUST_DEMOLISH_CANAL_FIRST;
   192 		return -1;
   145 		return CMD_ERROR;
   193 	}
   146 	}
   194 
   147 
   195 	/* Try to clear the tile. If the tile can be cleared, add the cost to the terraforming cost, else the terraforming fails. */
   148 	/* Try to clear the tile. If the tile can be cleared, add the cost to the terraforming cost, else the terraforming fails. */
   196 	ret = DoCommand(tile, 0, 0, ts->flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR);
   149 	return DoCommand(tile, 0, 0, ts->flags, CMD_LANDSCAPE_CLEAR);
   197 
       
   198 	if (CmdFailed(ret)) {
       
   199 		_terraform_err_tile = tile;
       
   200 		return -1;
       
   201 	}
       
   202 
       
   203 	ts->cost.AddCost(ret.GetCost());
       
   204 
       
   205 	/* Add the tile to the "dirty" list. We know already that it is not there, so append it at the end. */
       
   206 	assert(ts->tile_table_count < TERRAFORMER_TILE_TABLE_SIZE);
       
   207 	ts->tile_table[ts->tile_table_count++] = tile;
       
   208 
       
   209 	return 0;
       
   210 }
   150 }
   211 
   151 
   212 /**
   152 /**
   213  * Terraform the north corner of a tile to a specific height.
   153  * Terraform the north corner of a tile to a specific height.
   214  *
   154  *
   236 	 * This can only be true, if multiple corners of the start-tile are terraformed (i.e. the terraforming is done by towns/industries etc.).
   176 	 * This can only be true, if multiple corners of the start-tile are terraformed (i.e. the terraforming is done by towns/industries etc.).
   237 	 * In this case the terraforming should fail. (Don't know why.)
   177 	 * In this case the terraforming should fail. (Don't know why.)
   238 	 */
   178 	 */
   239 	if (height == TerraformGetHeightOfTile(ts, tile)) return false;
   179 	if (height == TerraformGetHeightOfTile(ts, tile)) return false;
   240 
   180 
   241 	/* Check if the incident tiles of the corner can be terraformed. Compute extra costs (like tile clearing) */
   181 	/* Check "too close to edge of map" */
   242 	if (TerraformProc(ts, tile, 0) < 0) return false;
   182 	uint x = TileX(tile);
   243 	if (TerraformProc(ts, tile + TileDiffXY( 0, -1), 1) < 0) return false;
   183 	uint y = TileY(tile);
   244 	if (TerraformProc(ts, tile + TileDiffXY(-1, -1), 2) < 0) return false;
   184 	if ((x <= 1) || (y <= 1) || (x >= MapMaxX() - 1) || (y >= MapMaxY() - 1)) {
   245 	if (TerraformProc(ts, tile + TileDiffXY(-1,  0), 3) < 0) return false;
   185 		/*
       
   186 		 * Determine a sensible error tile
       
   187 		 * Note: If x and y are both zero this will disable the error tile. (Tile 0 cannot be highlighted :( )
       
   188 		 */
       
   189 		if ((x == 1) && (y != 0)) x = 0;
       
   190 		if ((y == 1) && (x != 0)) y = 0;
       
   191 		_terraform_err_tile = TileXY(x, y);
       
   192 		_error_message = STR_0002_TOO_CLOSE_TO_EDGE_OF_MAP;
       
   193 		return false;
       
   194 	}
       
   195 
       
   196 	/* Mark incident tiles, that are involved in the terraforming */
       
   197 	TerraformAddDirtyTileAround(ts, tile);
   246 
   198 
   247 	/* Store the height modification */
   199 	/* Store the height modification */
   248 
   200 
   249 	/* Find tile in the "modheight" table.
   201 	/* Find tile in the "modheight" table.
   250 	 * Note: In a normal user-terraform command the tile will not be found in the "modheight" table.
   202 	 * Note: In a normal user-terraform command the tile will not be found in the "modheight" table.
   352 		if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
   304 		if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
   353 			return CMD_ERROR;
   305 			return CMD_ERROR;
   354 		}
   306 		}
   355 	}
   307 	}
   356 
   308 
   357 	/* Check if the terraforming is valid wrt. tunnels and bridges */
   309 	/* Check if the terraforming is valid wrt. tunnels, bridges and objects on the surface */
   358 	{
   310 	{
   359 		int count;
   311 		int count;
   360 		TileIndex *ti = ts.tile_table;
   312 		TileIndex *ti = ts.tile_table;
   361 
   313 
   362 		for (count = ts.tile_table_count; count != 0; count--, ti++) {
   314 		for (count = ts.tile_table_count; count != 0; count--, ti++) {
   363 			TileIndex tile = *ti;
   315 			TileIndex tile = *ti;
   364 
   316 
       
   317 			/* Find new heights of tile corners */
       
   318 			uint z_N = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0));
       
   319 			uint z_W = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0));
       
   320 			uint z_S = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1));
       
   321 			uint z_E = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 1));
       
   322 
   365 			/* Find min and max height of tile */
   323 			/* Find min and max height of tile */
   366 			uint z_min = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0));
   324 			uint z_min = min(min(z_N, z_W), min(z_S, z_E));
   367 			uint z_max = z_min;
   325 			uint z_max = max(max(z_N, z_W), max(z_S, z_E));
   368 			uint t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0));
       
   369 			z_min = min(z_min, t);
       
   370 			z_max = max(z_max, t);
       
   371 			t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1));
       
   372 			z_min = min(z_min, t);
       
   373 			z_max = max(z_max, t);
       
   374 			t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 1));
       
   375 			z_min = min(z_min, t);
       
   376 			z_max = max(z_max, t);
       
   377 
   326 
   378 			/* Check if bridge would take damage */
   327 			/* Check if bridge would take damage */
   379 			if (direction == 1 && MayHaveBridgeAbove(tile) && IsBridgeAbove(tile) &&
   328 			if (direction == 1 && MayHaveBridgeAbove(tile) && IsBridgeAbove(tile) &&
   380 					GetBridgeHeight(GetSouthernBridgeEnd(tile)) <= z_max * TILE_HEIGHT) {
   329 					GetBridgeHeight(GetSouthernBridgeEnd(tile)) <= z_max * TILE_HEIGHT) {
   381 				_terraform_err_tile = *ti; // highlight the tile under the bridge
   330 				_terraform_err_tile = *ti; // highlight the tile under the bridge
   384 			/* Check if tunnel would take damage */
   333 			/* Check if tunnel would take damage */
   385 			if (direction == -1 && IsTunnelInWay(tile, z_min * TILE_HEIGHT)) {
   334 			if (direction == -1 && IsTunnelInWay(tile, z_min * TILE_HEIGHT)) {
   386 				_terraform_err_tile = *ti; // highlight the tile above the tunnel
   335 				_terraform_err_tile = *ti; // highlight the tile above the tunnel
   387 				return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE);
   336 				return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE);
   388 			}
   337 			}
       
   338 			/* Check tiletype-specific things, and add extra-cost */
       
   339 			/* Start of temporary solution until TerraformProc() gets replaced with a TileTypeProc */
       
   340 			_terraform_err_tile = tile;
       
   341 			CommandCost cost_N = CommandCost();
       
   342 			CommandCost cost_W = CommandCost();
       
   343 			CommandCost cost_S = CommandCost();
       
   344 			CommandCost cost_E = CommandCost();
       
   345 
       
   346 			if (z_N != TileHeight(tile)) cost_N = TerraformProc(&ts, tile, 0);
       
   347 			if (CmdFailed(cost_N)) return cost_N;
       
   348 
       
   349 			if (z_E != TileHeight(tile + TileDiffXY(0, 1))) cost_E = TerraformProc(&ts, tile, 1);
       
   350 			if (CmdFailed(cost_E)) return cost_E;
       
   351 
       
   352 			if (z_S != TileHeight(tile + TileDiffXY(1, 1))) cost_S = TerraformProc(&ts, tile, 2);
       
   353 			if (CmdFailed(cost_S)) return cost_S;
       
   354 
       
   355 			if (z_W != TileHeight(tile + TileDiffXY(1, 0))) cost_W = TerraformProc(&ts, tile, 3);
       
   356 			if (CmdFailed(cost_W)) return cost_W;
       
   357 
       
   358 			_terraform_err_tile = 0; // no error, reset error tile.
       
   359 			/* Add extra cost. Currently this may only be for clearing the tile. And we only want to clear it once. */
       
   360 			CommandCost cost = CommandCost();
       
   361 			cost.AddCost(cost_N);
       
   362 			if (cost.GetCost() == 0) cost.AddCost(cost_W);
       
   363 			if (cost.GetCost() == 0) cost.AddCost(cost_S);
       
   364 			if (cost.GetCost() == 0) cost.AddCost(cost_E);
       
   365 			ts.cost.AddCost(cost);
       
   366 			/* End of temporary solution */
   389 		}
   367 		}
   390 	}
   368 	}
   391 
   369 
   392 	if (flags & DC_EXEC) {
   370 	if (flags & DC_EXEC) {
   393 		/* Clear the landscape at the tiles */
       
   394 		{
       
   395 			int count;
       
   396 			TileIndex *ti = ts.tile_table;
       
   397 			for (count = ts.tile_table_count; count != 0; count--, ti++) {
       
   398 				DoCommand(*ti, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
       
   399 			}
       
   400 		}
       
   401 
       
   402 		/* change the height */
   371 		/* change the height */
   403 		{
   372 		{
   404 			int count;
   373 			int count;
   405 			TerraformerHeightMod *mod;
   374 			TerraformerHeightMod *mod;
   406 
   375 
   407 			mod = ts.modheight;
   376 			mod = ts.modheight;
   408 			for (count = ts.modheight_count; count != 0; count--, mod++) {
   377 			for (count = ts.modheight_count; count != 0; count--, mod++) {
   409 				TileIndex til = mod->tile;
   378 				TileIndex til = mod->tile;
   410 
   379 
   411 				SetTileHeight(til, mod->height);
   380 				SetTileHeight(til, mod->height);
   412 				TerraformAddDirtyTileAround(&ts, til); // add the dirty tiles, we forgot above
       
   413 			}
   381 			}
   414 		}
   382 		}
   415 
   383 
   416 		/* finally mark the dirty tiles dirty */
   384 		/* finally mark the dirty tiles dirty */
   417 		{
   385 		{