00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "command_func.h"
00014 #include "tunnel_map.h"
00015 #include "bridge_map.h"
00016 #include "viewport_func.h"
00017 #include "economy_func.h"
00018 #include "genworld.h"
00019 #include "object_base.h"
00020 #include "company_base.h"
00021 #include "company_func.h"
00022 #include "tunnelbridge.h"
00023
00024 #include "table/strings.h"
00025
00026 #include <map>
00027 #include <set>
00028
00029 struct TerraformerState {
00030 int modheight_count;
00031 std::set<TileIndex> dirty_tiles;
00032
00033 std::map<TileIndex, uint16> tile_to_new_height;
00034 };
00035
00036 TileIndex _terraform_err_tile;
00037
00045 static int TerraformGetHeightOfTile(TerraformerState *ts, TileIndex tile)
00046 {
00047 if (ts->tile_to_new_height.find(tile) != ts->tile_to_new_height.end()) {
00048 return ts->tile_to_new_height[tile];
00049 } else {
00050 return TileHeight(tile);
00051 }
00052 }
00053
00061 static void TerraformSetHeightOfTile(TerraformerState *ts, TileIndex tile, int height)
00062 {
00063 ts->tile_to_new_height[tile] = (uint16)height;
00064 }
00065
00073 static void TerraformAddDirtyTile(TerraformerState *ts, TileIndex tile)
00074 {
00075 ts->dirty_tiles.insert(tile);
00076 }
00077
00085 static void TerraformAddDirtyTileAround(TerraformerState *ts, TileIndex tile)
00086 {
00087
00088 if (TileY(tile) >= 1) TerraformAddDirtyTile(ts, tile + TileDiffXY( 0, -1));
00089 if (TileY(tile) >= 1 && TileX(tile) >= 1) TerraformAddDirtyTile(ts, tile + TileDiffXY(-1, -1));
00090 if (TileX(tile) >= 1) TerraformAddDirtyTile(ts, tile + TileDiffXY(-1, 0));
00091 TerraformAddDirtyTile(ts, tile);
00092 }
00093
00102 static CommandCost TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height)
00103 {
00104 assert(tile < MapSize());
00105
00106
00107 if (height < 0) return_cmd_error(STR_ERROR_ALREADY_AT_SEA_LEVEL);
00108 if (height > (int)GetMaxTileHeight()) return_cmd_error(STR_ERROR_TOO_HIGH);
00109
00110
00111
00112
00113
00114
00115 if (height == TerraformGetHeightOfTile(ts, tile)) return CMD_ERROR;
00116
00117
00118 uint x = TileX(tile);
00119 uint y = TileY(tile);
00120 if (!_settings_game.construction.freeform_edges && ((x <= 1) || (y <= 1) || (x >= MapMaxX() - 1) || (y >= MapMaxY() - 1))) {
00121
00122
00123
00124 if (x == 1) x = 0;
00125 if (y == 1) y = 0;
00126 _terraform_err_tile = TileXY(x, y);
00127 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
00128 }
00129
00130
00131 TerraformAddDirtyTileAround(ts, tile);
00132
00133
00134 TerraformSetHeightOfTile(ts, tile, height);
00135
00136 CommandCost total_cost(EXPENSES_CONSTRUCTION);
00137
00138
00139 total_cost.AddCost(_price[PR_TERRAFORM]);
00140
00141
00142 {
00143 const TileIndexDiffC *ttm;
00144
00145 TileIndex orig_tile = tile;
00146 static const TileIndexDiffC _terraform_tilepos[] = {
00147 { 1, 0},
00148 {-2, 0},
00149 { 1, 1},
00150 { 0, -2}
00151 };
00152
00153 for (ttm = _terraform_tilepos; ttm != endof(_terraform_tilepos); ttm++) {
00154 tile += ToTileIndexDiff(*ttm);
00155
00156 if (tile >= MapSize()) continue;
00157
00158 if (Delta(TileX(orig_tile), TileX(tile)) == MapSizeX() - 1) continue;
00159 if (Delta(TileY(orig_tile), TileY(tile)) == MapSizeY() - 1) continue;
00160
00161
00162 int r = TerraformGetHeightOfTile(ts, tile);
00163 int height_diff = height - r;
00164
00165
00166 if (abs(height_diff) > 1) {
00167
00168 height_diff += (height_diff < 0 ? 1 : -1);
00169 CommandCost cost = TerraformTileHeight(ts, tile, r + height_diff);
00170 if (cost.Failed()) return cost;
00171 total_cost.AddCost(cost);
00172 }
00173 }
00174 }
00175
00176 return total_cost;
00177 }
00178
00179 static void MarkTilesDirty(TerraformerState &ts)
00180 {
00181 for (std::set<TileIndex>::const_iterator it = ts.dirty_tiles.begin(); it != ts.dirty_tiles.end(); it++) {
00182 TileIndex ti = *it;
00183 MarkTileDirtyByTile(ti);
00184 int height = TerraformGetHeightOfTile(&ts, ti);
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198 int x = TileX(ti);
00199 int y = TileY(ti);
00200
00201 if (x == 0) {
00202
00203 if (y == 0) {
00204
00205 for (int cx = 0; cx >= -height - 1; cx--) {
00206 for (int cy = 0; cy >= -height - 1; cy--) {
00207
00208
00209
00210 if (cx + cy >= -height - 2) {
00211
00212 MarkTileDirtyByTileOutsideMap(cx, cy);
00213 }
00214 }
00215 }
00216 } else if (y < (int)MapMaxY()) {
00217 for (int cx = 0; cx >= -height - 1; cx--) {
00218 MarkTileDirtyByTileOutsideMap(cx, y);
00219 }
00220 } else {
00221 for (int cx = 0; cx >= -height - 1; cx--) {
00222 for (int cy = (int)MapMaxY(); cy <= (int)MapMaxY() + height + 1; cy++) {
00223
00224 if (cx + ((int)MapMaxY() - cy) >= -height - 2) {
00225 MarkTileDirtyByTileOutsideMap(cx, cy);
00226 }
00227 }
00228 }
00229 }
00230
00231 } else if (x < (int)MapMaxX()) {
00232
00233 if (y == 0) {
00234 for (int cy = 0; cy >= -height - 1; cy--) {
00235 MarkTileDirtyByTileOutsideMap(x, cy);
00236 }
00237 } else if (y < (int)MapMaxY()) {
00238
00239 } else {
00240 for (int cy = (int)MapMaxY(); cy <= (int)MapMaxY() + height + 1; cy++) {
00241 MarkTileDirtyByTileOutsideMap(x, cy);
00242 }
00243 }
00244
00245 } else {
00246
00247 if (y == 0) {
00248 for (int cx = (int)MapMaxX(); cx <= (int)MapMaxX() + height + 1; cx++) {
00249 for (int cy = 0; cy >= -height - 1; cy--) {
00250 if (((int)MapMaxX() - cx) + cy >= -height - 2) {
00251 MarkTileDirtyByTileOutsideMap(cx, cy);
00252 }
00253 }
00254 }
00255 } else if (y < (int)MapMaxY()) {
00256 for (int cx = (int)MapMaxX(); cx <= (int)MapMaxX() + height + 1; cx++) {
00257 MarkTileDirtyByTileOutsideMap(cx, y);
00258 }
00259 } else {
00260 for (int cx = (int)MapMaxX(); cx <= (int)MapMaxX() + height + 1; cx++) {
00261 for (int cy = (int)MapMaxY(); cy <= (int)MapMaxY() + height + 1; cy++) {
00262
00263 if (((int)MapMaxX() - cx) + ((int)MapMaxY() - cy) >= -height - 2) {
00264 MarkTileDirtyByTileOutsideMap(cx, cy);
00265 }
00266 }
00267 }
00268 }
00269 }
00270 }
00271 }
00272
00282 CommandCost CmdTerraformLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00283 {
00284 _terraform_err_tile = INVALID_TILE;
00285
00286 CommandCost total_cost(EXPENSES_CONSTRUCTION);
00287 int direction = (p2 != 0 ? 1 : -1);
00288 TerraformerState ts;
00289
00290 ts.modheight_count = 0;
00291
00292
00293 if ((p1 & SLOPE_W) != 0 && tile + TileDiffXY(1, 0) < MapSize()) {
00294 TileIndex t = tile + TileDiffXY(1, 0);
00295 CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction);
00296 if (cost.Failed()) return cost;
00297 total_cost.AddCost(cost);
00298 }
00299
00300 if ((p1 & SLOPE_S) != 0 && tile + TileDiffXY(1, 1) < MapSize()) {
00301 TileIndex t = tile + TileDiffXY(1, 1);
00302 CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction);
00303 if (cost.Failed()) return cost;
00304 total_cost.AddCost(cost);
00305 }
00306
00307 if ((p1 & SLOPE_E) != 0 && tile + TileDiffXY(0, 1) < MapSize()) {
00308 TileIndex t = tile + TileDiffXY(0, 1);
00309 CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction);
00310 if (cost.Failed()) return cost;
00311 total_cost.AddCost(cost);
00312 }
00313
00314 if ((p1 & SLOPE_N) != 0) {
00315 TileIndex t = tile + TileDiffXY(0, 0);
00316 CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction);
00317 if (cost.Failed()) return cost;
00318 total_cost.AddCost(cost);
00319 }
00320
00321
00322
00323
00324 for (int pass = 0; pass < 2; pass++) {
00325 for (std::set<TileIndex>::const_iterator it = ts.dirty_tiles.begin(); it != ts.dirty_tiles.end(); it++) {
00326 TileIndex tile = *it;
00327
00328 assert(tile < MapSize());
00329
00330
00331 if (IsTileType(tile, MP_VOID)) continue;
00332
00333
00334 uint z_N = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0));
00335 uint z_W = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0));
00336 uint z_S = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1));
00337 uint z_E = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 1));
00338
00339
00340 uint z_min = min(min(z_N, z_W), min(z_S, z_E));
00341 uint z_max = max(max(z_N, z_W), max(z_S, z_E));
00342
00343
00344 Slope tileh = (z_max > z_min + 1 ? SLOPE_STEEP : SLOPE_FLAT);
00345 if (z_W > z_min) tileh |= SLOPE_W;
00346 if (z_S > z_min) tileh |= SLOPE_S;
00347 if (z_E > z_min) tileh |= SLOPE_E;
00348 if (z_N > z_min) tileh |= SLOPE_N;
00349
00350 if (pass == 0) {
00351
00352 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) {
00353 uint bridge_height = GetBridgeHeight(GetSouthernBridgeEnd(tile));
00354
00355 if (direction == 1 && bridge_height <= z_max * TILE_HEIGHT) {
00356 _terraform_err_tile = tile;
00357 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00358 }
00359
00360
00361
00362 if (direction == -1 && bridge_height > (z_min + MAX_BRIDGE_HEIGHT) * TILE_HEIGHT) {
00363 _terraform_err_tile = tile;
00364 return_cmd_error(STR_ERROR_BRIDGE_TOO_HIGH_AFTER_LOWER_LAND);
00365 }
00366 }
00367
00368
00369 if (direction == 1 && MayHaveBridgeAbove(tile) && IsBridgeAbove(tile) &&
00370 GetBridgeHeight(GetSouthernBridgeEnd(tile)) <= z_max * TILE_HEIGHT) {
00371 _terraform_err_tile = tile;
00372 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00373 }
00374
00375
00376 if (direction == -1 && IsTunnelInWay(tile, z_min * TILE_HEIGHT)) {
00377 _terraform_err_tile = tile;
00378 return_cmd_error(STR_ERROR_EXCAVATION_WOULD_DAMAGE);
00379 }
00380 }
00381
00382
00383 const ClearedObjectArea *coa = FindClearedObject(tile);
00384 bool indirectly_cleared = coa != NULL && coa->first_tile != tile;
00385
00386
00387 const bool curr_gen = _generating_world;
00388 if (_game_mode == GM_EDITOR) _generating_world = true;
00389 DoCommandFlag tile_flags = flags | DC_AUTO | DC_FORCE_CLEAR_TILE;
00390 if (pass == 0) {
00391 tile_flags &= ~DC_EXEC;
00392 tile_flags |= DC_NO_MODIFY_TOWN_RATING;
00393 }
00394 CommandCost cost;
00395 if (indirectly_cleared) {
00396 cost = DoCommand(tile, 0, 0, tile_flags, CMD_LANDSCAPE_CLEAR);
00397 } else {
00398 cost = _tile_type_procs[GetTileType(tile)]->terraform_tile_proc(tile, tile_flags, z_min * TILE_HEIGHT, tileh);
00399 }
00400 _generating_world = curr_gen;
00401 if (cost.Failed()) {
00402 _terraform_err_tile = tile;
00403 return cost;
00404 }
00405 if (pass == 1) total_cost.AddCost(cost);
00406 }
00407 }
00408
00409 Company *c = Company::GetIfValid(_current_company);
00410 if (c != NULL && (int)GB(c->terraform_limit, 16, 16) < ts.modheight_count) {
00411 return_cmd_error(STR_ERROR_TERRAFORM_LIMIT_REACHED);
00412 }
00413
00414 if (flags & DC_EXEC) {
00415
00416 {
00417 for (std::map<TileIndex, uint16>::const_iterator it = ts.tile_to_new_height.begin();
00418 it != ts.tile_to_new_height.end(); it++) {
00419 TileIndex tile = it->first;
00420 uint16 height = it->second;
00421
00422 ts.modheight_count += 1;
00423
00424 SetTileHeight(tile, height);
00425 }
00426 }
00427
00428
00429 {
00430 MarkTilesDirty(ts);
00431 }
00432
00433 if (c != NULL) c->terraform_limit -= ts.modheight_count << 16;
00434 }
00435 return total_cost;
00436 }
00437
00449 CommandCost CmdLevelLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00450 {
00451 if (p1 >= MapSize()) return CMD_ERROR;
00452
00453 _terraform_err_tile = INVALID_TILE;
00454
00455
00456 uint oldh = TileHeight(p1);
00457
00458
00459 uint h = oldh;
00460 LevelMode lm = (LevelMode)GB(p2, 1, 2);
00461 switch (lm) {
00462 case LM_LEVEL: break;
00463 case LM_RAISE: h++; break;
00464 case LM_LOWER: h--; break;
00465 default: return CMD_ERROR;
00466 }
00467
00468
00469 if (h > GetMaxTileHeight()) return_cmd_error((oldh == 0) ? STR_ERROR_ALREADY_AT_SEA_LEVEL : STR_ERROR_TOO_HIGH);
00470
00471 Money money = GetAvailableMoneyForCommand();
00472 CommandCost cost(EXPENSES_CONSTRUCTION);
00473 CommandCost last_error(lm == LM_LEVEL ? STR_ERROR_ALREADY_LEVELLED : INVALID_STRING_ID);
00474 bool had_success = false;
00475
00476 const Company *c = Company::GetIfValid(_current_company);
00477 int limit = (c == NULL ? INT32_MAX : GB(c->terraform_limit, 16, 16));
00478 if (limit == 0) return_cmd_error(STR_ERROR_TERRAFORM_LIMIT_REACHED);
00479
00480 TileArea ta(tile, p1);
00481 TileIterator *iter = HasBit(p2, 0) ? (TileIterator *)new DiagonalTileIterator(tile, p1) : new OrthogonalTileIterator(ta);
00482 for (; *iter != INVALID_TILE; ++(*iter)) {
00483 TileIndex t = *iter;
00484 uint curh = TileHeight(t);
00485 while (curh != h) {
00486 CommandCost ret = DoCommand(t, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND);
00487 if (ret.Failed()) {
00488 last_error = ret;
00489
00490
00491 if (ret.GetErrorMessage() == STR_ERROR_TERRAFORM_LIMIT_REACHED) limit = 0;
00492 break;
00493 }
00494
00495 if (flags & DC_EXEC) {
00496 money -= ret.GetCost();
00497 if (money < 0) {
00498 _additional_cash_required = ret.GetCost();
00499 delete iter;
00500 return cost;
00501 }
00502 DoCommand(t, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND);
00503 } else {
00504
00505
00506
00507
00508
00509 if (--limit <= 0) {
00510 had_success = true;
00511 break;
00512 }
00513 }
00514
00515 cost.AddCost(ret);
00516 curh += (curh > h) ? -1 : 1;
00517 had_success = true;
00518
00519 limit -= 1;
00520 }
00521 if (limit <= 0) break;
00522 }
00523
00524 delete iter;
00525 return had_success ? cost : last_error;
00526 }