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 { |