33 TileIndex tile; ///< Referenced tile. |
33 TileIndex tile; ///< Referenced tile. |
34 byte height; ///< New TileHeight (height of north corner) of the tile. |
34 byte height; ///< New TileHeight (height of north corner) of the tile. |
35 }; |
35 }; |
36 |
36 |
37 struct TerraformerState { |
37 struct TerraformerState { |
38 int modheight_count; ///< amount of entries in "modheight". |
38 int modheight_count; ///< amount of entries in "modheight". |
39 int tile_table_count; ///< amount of entries in "tile_table". |
39 int tile_table_count; ///< amount of entries in "tile_table". |
40 |
40 |
41 /** |
41 /** |
42 * Dirty tiles, i.e.\ at least one corner changed. |
42 * Dirty tiles, i.e.\ at least one corner changed. |
43 * |
43 * |
44 * This array contains the tiles which are or will be marked as dirty. |
44 * This array contains the tiles which are or will be marked as dirty. |
47 */ |
47 */ |
48 TileIndex tile_table[TERRAFORMER_TILE_TABLE_SIZE]; |
48 TileIndex tile_table[TERRAFORMER_TILE_TABLE_SIZE]; |
49 TerraformerHeightMod modheight[TERRAFORMER_MODHEIGHT_SIZE]; ///< Height modifications. |
49 TerraformerHeightMod modheight[TERRAFORMER_MODHEIGHT_SIZE]; ///< Height modifications. |
50 }; |
50 }; |
51 |
51 |
52 TileIndex _terraform_err_tile; |
52 TileIndex _terraform_err_tile; ///< first tile we couldn't terraform |
53 |
53 |
54 /** |
54 /** |
55 * Gets the TileHeight (height of north corner) of a tile as of current terraforming progress. |
55 * Gets the TileHeight (height of north corner) of a tile as of current terraforming progress. |
56 * |
56 * |
57 * @param ts TerraformerState. |
57 * @param ts TerraformerState. |
58 * @param tile Tile. |
58 * @param tile Tile. |
59 * @return TileHeight. |
59 * @return TileHeight. |
60 */ |
60 */ |
61 static int TerraformGetHeightOfTile(TerraformerState *ts, TileIndex tile) |
61 static int TerraformGetHeightOfTile(const TerraformerState *ts, TileIndex tile) |
62 { |
62 { |
63 TerraformerHeightMod *mod = ts->modheight; |
63 const TerraformerHeightMod *mod = ts->modheight; |
64 int count; |
64 |
65 |
65 for (int count = ts->modheight_count; count != 0; count--, mod++) { |
66 for (count = ts->modheight_count; count != 0; count--, mod++) { |
|
67 if (mod->tile == tile) return mod->height; |
66 if (mod->tile == tile) return mod->height; |
68 } |
67 } |
69 |
68 |
70 /* TileHeight unchanged so far, read value from map. */ |
69 /* TileHeight unchanged so far, read value from map. */ |
71 return TileHeight(tile); |
70 return TileHeight(tile); |
147 * @param height Aimed height. |
144 * @param height Aimed height. |
148 * @param return Error code or cost. |
145 * @param return Error code or cost. |
149 */ |
146 */ |
150 static CommandCost TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height) |
147 static CommandCost TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height) |
151 { |
148 { |
152 CommandCost total_cost(EXPENSES_CONSTRUCTION); |
|
153 |
|
154 assert(tile < MapSize()); |
149 assert(tile < MapSize()); |
155 |
150 |
156 /* Check range of destination height */ |
151 /* Check range of destination height */ |
157 if (height < 0) return_cmd_error(STR_1003_ALREADY_AT_SEA_LEVEL); |
152 if (height < 0) return_cmd_error(STR_1003_ALREADY_AT_SEA_LEVEL); |
158 if (height > MAX_TILE_HEIGHT) return_cmd_error(STR_1004_TOO_HIGH); |
153 if (height > MAX_TILE_HEIGHT) return_cmd_error(STR_1004_TOO_HIGH); |
226 * @param p2 direction; eg up (non-zero) or down (zero) |
223 * @param p2 direction; eg up (non-zero) or down (zero) |
227 * @return error or cost of terraforming |
224 * @return error or cost of terraforming |
228 */ |
225 */ |
229 CommandCost CmdTerraformLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
226 CommandCost CmdTerraformLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
230 { |
227 { |
231 TerraformerState ts; |
228 /* Make an extra check for map-bounds cause we add tiles to the originating tile */ |
|
229 if (tile + TileDiffXY(1, 1) >= MapSize()) return CMD_ERROR; |
|
230 |
|
231 _terraform_err_tile = INVALID_TILE; |
|
232 |
232 CommandCost total_cost(EXPENSES_CONSTRUCTION); |
233 CommandCost total_cost(EXPENSES_CONSTRUCTION); |
233 int direction = (p2 != 0 ? 1 : -1); |
234 int direction = (p2 != 0 ? 1 : -1); |
234 |
235 TerraformerState ts; |
235 _terraform_err_tile = 0; |
|
236 |
236 |
237 ts.modheight_count = ts.tile_table_count = 0; |
237 ts.modheight_count = ts.tile_table_count = 0; |
238 |
|
239 /* Make an extra check for map-bounds cause we add tiles to the originating tile */ |
|
240 if (tile + TileDiffXY(1, 1) >= MapSize()) return CMD_ERROR; |
|
241 |
238 |
242 /* Compute the costs and the terraforming result in a model of the landscape */ |
239 /* Compute the costs and the terraforming result in a model of the landscape */ |
243 if ((p1 & SLOPE_W) != 0) { |
240 if ((p1 & SLOPE_W) != 0) { |
244 TileIndex t = tile + TileDiffXY(1, 0); |
241 TileIndex t = tile + TileDiffXY(1, 0); |
245 CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction); |
242 CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction); |
268 total_cost.AddCost(cost); |
265 total_cost.AddCost(cost); |
269 } |
266 } |
270 |
267 |
271 /* Check if the terraforming is valid wrt. tunnels, bridges and objects on the surface */ |
268 /* Check if the terraforming is valid wrt. tunnels, bridges and objects on the surface */ |
272 { |
269 { |
273 int count; |
|
274 TileIndex *ti = ts.tile_table; |
270 TileIndex *ti = ts.tile_table; |
275 |
271 |
276 for (count = ts.tile_table_count; count != 0; count--, ti++) { |
272 for (int count = ts.tile_table_count; count != 0; count--, ti++) { |
277 TileIndex tile = *ti; |
273 TileIndex tile = *ti; |
278 |
274 |
279 /* Find new heights of tile corners */ |
275 /* Find new heights of tile corners */ |
280 uint z_N = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0)); |
276 uint z_N = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0)); |
281 uint z_W = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0)); |
277 uint z_W = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0)); |
285 /* Find min and max height of tile */ |
281 /* Find min and max height of tile */ |
286 uint z_min = min(min(z_N, z_W), min(z_S, z_E)); |
282 uint z_min = min(min(z_N, z_W), min(z_S, z_E)); |
287 uint z_max = max(max(z_N, z_W), max(z_S, z_E)); |
283 uint z_max = max(max(z_N, z_W), max(z_S, z_E)); |
288 |
284 |
289 /* Compute tile slope */ |
285 /* Compute tile slope */ |
290 uint tileh = (z_max > z_min + 1 ? SLOPE_STEEP : SLOPE_FLAT); |
286 Slope tileh = (z_max > z_min + 1 ? SLOPE_STEEP : SLOPE_FLAT); |
291 if (z_W > z_min) tileh += SLOPE_W; |
287 if (z_W > z_min) tileh |= SLOPE_W; |
292 if (z_S > z_min) tileh += SLOPE_S; |
288 if (z_S > z_min) tileh |= SLOPE_S; |
293 if (z_E > z_min) tileh += SLOPE_E; |
289 if (z_E > z_min) tileh |= SLOPE_E; |
294 if (z_N > z_min) tileh += SLOPE_N; |
290 if (z_N > z_min) tileh |= SLOPE_N; |
295 |
291 |
296 /* Check if bridge would take damage */ |
292 /* Check if bridge would take damage */ |
297 if (direction == 1 && MayHaveBridgeAbove(tile) && IsBridgeAbove(tile) && |
293 if (direction == 1 && MayHaveBridgeAbove(tile) && IsBridgeAbove(tile) && |
298 GetBridgeHeight(GetSouthernBridgeEnd(tile)) <= z_max * TILE_HEIGHT) { |
294 GetBridgeHeight(GetSouthernBridgeEnd(tile)) <= z_max * TILE_HEIGHT) { |
299 _terraform_err_tile = tile; // highlight the tile under the bridge |
295 _terraform_err_tile = tile; // highlight the tile under the bridge |
303 if (direction == -1 && IsTunnelInWay(tile, z_min * TILE_HEIGHT)) { |
299 if (direction == -1 && IsTunnelInWay(tile, z_min * TILE_HEIGHT)) { |
304 _terraform_err_tile = tile; // highlight the tile above the tunnel |
300 _terraform_err_tile = tile; // highlight the tile above the tunnel |
305 return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE); |
301 return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE); |
306 } |
302 } |
307 /* Check tiletype-specific things, and add extra-cost */ |
303 /* Check tiletype-specific things, and add extra-cost */ |
308 CommandCost cost = _tile_type_procs[GetTileType(tile)]->terraform_tile_proc(tile, flags | DC_AUTO, z_min * TILE_HEIGHT, (Slope) tileh); |
304 CommandCost cost = _tile_type_procs[GetTileType(tile)]->terraform_tile_proc(tile, flags | DC_AUTO, z_min * TILE_HEIGHT, tileh); |
309 if (CmdFailed(cost)) { |
305 if (CmdFailed(cost)) { |
310 _terraform_err_tile = tile; |
306 _terraform_err_tile = tile; |
311 return cost; |
307 return cost; |
312 } |
308 } |
313 total_cost.AddCost(cost); |
309 total_cost.AddCost(cost); |
348 * @param p2 height difference; eg raise (+1), lower (-1) or level (0) |
344 * @param p2 height difference; eg raise (+1), lower (-1) or level (0) |
349 * @return error or cost of terraforming |
345 * @return error or cost of terraforming |
350 */ |
346 */ |
351 CommandCost CmdLevelLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
347 CommandCost CmdLevelLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
352 { |
348 { |
353 int size_x, size_y; |
|
354 int ex; |
|
355 int ey; |
|
356 int sx, sy; |
|
357 uint h, oldh, curh; |
|
358 CommandCost money; |
|
359 CommandCost ret; |
|
360 CommandCost cost(EXPENSES_CONSTRUCTION); |
|
361 |
|
362 if (p1 >= MapSize()) return CMD_ERROR; |
349 if (p1 >= MapSize()) return CMD_ERROR; |
363 |
350 |
364 /* remember level height */ |
351 /* remember level height */ |
365 oldh = TileHeight(p1); |
352 uint oldh = TileHeight(p1); |
366 |
353 |
367 /* compute new height */ |
354 /* compute new height */ |
368 h = oldh + p2; |
355 uint h = oldh + p2; |
369 |
356 |
370 /* Check range of destination height */ |
357 /* Check range of destination height */ |
371 if (h > MAX_TILE_HEIGHT) return_cmd_error((oldh == 0) ? STR_1003_ALREADY_AT_SEA_LEVEL : STR_1004_TOO_HIGH); |
358 if (h > MAX_TILE_HEIGHT) return_cmd_error((oldh == 0) ? STR_1003_ALREADY_AT_SEA_LEVEL : STR_1004_TOO_HIGH); |
372 |
359 |
373 /* make sure sx,sy are smaller than ex,ey */ |
360 /* make sure sx,sy are smaller than ex,ey */ |
374 ex = TileX(tile); |
361 int ex = TileX(tile); |
375 ey = TileY(tile); |
362 int ey = TileY(tile); |
376 sx = TileX(p1); |
363 int sx = TileX(p1); |
377 sy = TileY(p1); |
364 int sy = TileY(p1); |
378 if (ex < sx) Swap(ex, sx); |
365 if (ex < sx) Swap(ex, sx); |
379 if (ey < sy) Swap(ey, sy); |
366 if (ey < sy) Swap(ey, sy); |
380 tile = TileXY(sx, sy); |
367 tile = TileXY(sx, sy); |
381 |
368 |
382 size_x = ex - sx + 1; |
369 int size_x = ex - sx + 1; |
383 size_y = ey - sy + 1; |
370 int size_y = ey - sy + 1; |
384 |
371 |
385 money.AddCost(GetAvailableMoneyForCommand()); |
372 Money money = GetAvailableMoneyForCommand(); |
|
373 CommandCost cost(EXPENSES_CONSTRUCTION); |
386 |
374 |
387 BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) { |
375 BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) { |
388 curh = TileHeight(tile2); |
376 uint curh = TileHeight(tile2); |
389 while (curh != h) { |
377 while (curh != h) { |
390 ret = DoCommand(tile2, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND); |
378 CommandCost ret = DoCommand(tile2, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND); |
391 if (CmdFailed(ret)) break; |
379 if (CmdFailed(ret)) break; |
392 |
380 |
393 if (flags & DC_EXEC) { |
381 if (flags & DC_EXEC) { |
394 money.AddCost(-ret.GetCost()); |
382 money -= ret.GetCost(); |
395 if (money.GetCost() < 0) { |
383 if (money < 0) { |
396 _additional_cash_required = ret.GetCost(); |
384 _additional_cash_required = ret.GetCost(); |
397 return cost; |
385 return cost; |
398 } |
386 } |
399 DoCommand(tile2, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND); |
387 DoCommand(tile2, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND); |
400 } |
388 } |