# HG changeset patch # User rubidium # Date 2011-01-04 22:50:09 # Node ID 4a4e85b95d1e41c848dd6ac38985c168ff481917 # Parent 560772a93cc2778acb4fb75da902999c5d612d2e (svn r21728) -Fix/Feature [FS#4331]: (configurably) limit amount of tiles that can be cleared/terraformed by a company diff --git a/src/company_base.h b/src/company_base.h --- a/src/company_base.h +++ b/src/company_base.h @@ -69,6 +69,9 @@ struct CompanyProperties { int16 bankrupt_timeout; ///< If bigger than \c 0, amount of time to wait for an answer on an offer to buy this company. Money bankrupt_value; + uint32 terraform_limit; ///< Amount of tileheights we can (still) terraform (times 65536). + uint32 clear_limit; ///< Amount of tiles we can (still) clear (times 65536). + /** * If \c true, the company is (also) controlled by the computer (a NoAI program). * @note It is possible that the user is also participating in such a company. diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -58,6 +58,9 @@ Company::Company(uint16 name_1, bool is_ this->name_1 = name_1; this->location_of_HQ = INVALID_TILE; this->is_ai = is_ai; + this->terraform_limit = _settings_game.construction.terraform_frame_burst << 16; + this->clear_limit = _settings_game.construction.clear_frame_burst << 16; + for (uint j = 0; j < 4; j++) this->share_owners[j] = COMPANY_SPECTATOR; InvalidateWindowData(WC_PERFORMANCE_DETAIL, 0, INVALID_COMPANY); } @@ -252,6 +255,16 @@ void SubtractMoneyFromCompanyFract(Compa if (cost != 0) SubtractMoneyFromAnyCompany(c, CommandCost(cst.GetExpensesType(), cost)); } +/** Update the landscaping limits per company. */ +void UpdateLandscapingLimits() +{ + Company *c; + FOR_ALL_COMPANIES(c) { + c->terraform_limit = min(c->terraform_limit + _settings_game.construction.terraform_per_64k_frames, _settings_game.construction.terraform_frame_burst << 16); + c->clear_limit = min(c->clear_limit + _settings_game.construction.clear_per_64k_frames, _settings_game.construction.clear_frame_burst << 16); + } +} + /** * Set the right DParams to get the name of an owner. * @param owner the owner to get the name of. diff --git a/src/company_func.h b/src/company_func.h --- a/src/company_func.h +++ b/src/company_func.h @@ -23,6 +23,7 @@ void SetLocalCompany(CompanyID new_compa void ShowBuyCompanyDialog(CompanyID company); void CompanyAdminUpdate(const Company *company); void CompanyAdminBankrupt(CompanyID company_id); +void UpdateLandscapingLimits(); extern CompanyByte _local_company; extern CompanyByte _current_company; diff --git a/src/landscape.cpp b/src/landscape.cpp --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -32,6 +32,7 @@ #include "object_base.h" #include "water_map.h" #include "economy_func.h" +#include "company_func.h" #include "table/strings.h" #include "table/sprites.h" @@ -616,6 +617,11 @@ CommandCost CmdLandscapeClear(TileIndex cost.AddCost(GetWaterClass(tile) == WATER_CLASS_CANAL ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER]); } + Company *c = (flags & DC_AUTO) ? NULL : Company::GetIfValid(_current_company); + if (c != NULL && (int)GB(c->clear_limit, 16, 16) < 1) { + return_cmd_error(STR_ERROR_CLEARING_LIMIT_REACHED); + } + const ClearedObjectArea *coa = FindClearedObject(tile); /* If this tile was the first tile which caused object destruction, always @@ -633,7 +639,10 @@ CommandCost CmdLandscapeClear(TileIndex cost.AddCost(_tile_type_procs[GetTileType(tile)]->clear_tile_proc(tile, flags)); } - if (do_clear && (flags & DC_EXEC)) DoClearSquare(tile); + if (flags & DC_EXEC) { + if (c != NULL) c->clear_limit -= 1 << 16; + if (do_clear) DoClearSquare(tile); + } return cost; } @@ -656,6 +665,9 @@ CommandCost CmdClearArea(TileIndex tile, CommandCost last_error = CMD_ERROR; bool had_success = false; + const Company *c = (flags & DC_AUTO) ? NULL : Company::GetIfValid(_current_company); + int limit = (c == NULL ? INT32_MAX : GB(c->clear_limit, 16, 16)); + TileArea ta(tile, p1); TileIterator *iter = HasBit(p2, 0) ? (TileIterator *)new DiagonalTileIterator(tile, p1) : new OrthogonalTileIterator(ta); for (; *iter != INVALID_TILE; ++(*iter)) { @@ -663,6 +675,9 @@ CommandCost CmdClearArea(TileIndex tile, CommandCost ret = DoCommand(t, 0, 0, flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) { last_error = ret; + + /* We may not clear more tiles. */ + if (c != NULL && GB(c->clear_limit, 16, 16) < 1) break; continue; } @@ -684,6 +699,9 @@ CommandCost CmdClearArea(TileIndex tile, ta.w == 1 && ta.h == 1 ? EV_EXPLOSION_SMALL : EV_EXPLOSION_LARGE ); } + } else { + /* When we're at the clearing limit we better bail (unneed) testing as well. */ + if (ret.GetCost() != 0 && --limit <= 0) break; } cost.AddCost(ret); } diff --git a/src/lang/english.txt b/src/lang/english.txt --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3476,6 +3476,8 @@ STR_ERROR_SITE_UNSUITABLE STR_ERROR_ALREADY_BUILT :{WHITE}... already built STR_ERROR_OWNED_BY :{WHITE}... owned by {STRING2} STR_ERROR_AREA_IS_OWNED_BY_ANOTHER :{WHITE}... area is owned by another company +STR_ERROR_TERRAFORM_LIMIT_REACHED :{WHITE}... terraforming limit reached +STR_ERROR_CLEARING_LIMIT_REACHED :{WHITE}... tile clearing limit reached STR_ERROR_NAME_MUST_BE_UNIQUE :{WHITE}Name must be unique STR_ERROR_GENERIC_OBJECT_IN_THE_WAY :{WHITE}{1:STRING} in the way STR_ERROR_NOT_ALLOWED_WHILE_PAUSED :{WHITE}Not allowed while paused diff --git a/src/openttd.cpp b/src/openttd.cpp --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1236,6 +1236,7 @@ void StateGameLoop() { /* dont execute the state loop during pause */ if (_pause_mode != PM_UNPAUSED) { + UpdateLandscapingLimits(); CallWindowTickEvent(); return; } @@ -1248,6 +1249,7 @@ void StateGameLoop() CallVehicleTicks(); CallLandscapeTick(); ClearStorageChanges(true); + UpdateLandscapingLimits(); CallWindowTickEvent(); NewsLoop(); @@ -1273,6 +1275,7 @@ void StateGameLoop() ClearStorageChanges(true); AI::GameLoop(); + UpdateLandscapingLimits(); CallWindowTickEvent(); NewsLoop(); diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2405,6 +2405,13 @@ bool AfterLoadGame() ClrBit(t->flags, 5); SetBit(t->vehicle_flags, VF_PATHFINDER_LOST); } + + /* Introduced terraform/clear limits. */ + Company *c; + FOR_ALL_COMPANIES(c) { + c->terraform_limit = _settings_game.construction.terraform_frame_burst << 16; + c->clear_limit = _settings_game.construction.clear_frame_burst << 16; + } } /* Road stops is 'only' updating some caches */ diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -140,6 +140,9 @@ static const SaveLoad _company_desc[] = SLE_CONDNULL(1, 107, 111), ///< is_noai SLE_CONDNULL(1, 4, 99), + SLE_CONDVAR(CompanyProperties, terraform_limit, SLE_UINT32, 156, SL_MAX_VERSION), + SLE_CONDVAR(CompanyProperties, clear_limit, SLE_UINT32, 156, SL_MAX_VERSION), + SLE_END() }; diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -220,8 +220,9 @@ * 153 21263 * 154 21426 * 155 21453 + * 156 !!TODO!! */ -extern const uint16 SAVEGAME_VERSION = 155; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 156; ///< Current savegame version of OpenTTD. SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/settings_type.h b/src/settings_type.h --- a/src/settings_type.h +++ b/src/settings_type.h @@ -214,6 +214,11 @@ struct ConstructionSettings { bool freeform_edges; ///< allow terraforming the tiles at the map edges uint8 extra_tree_placement; ///< (dis)allow building extra trees in-game uint8 command_pause_level; ///< level/amount of commands that can't be executed while paused + + uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames? + uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed? + uint32 clear_per_64k_frames; ///< how many tiles may, over a long period, be cleared per 65536 frames? + uint16 clear_frame_burst; ///< how many tiles may, over a short period, be cleared? }; /** Settings related to the AI. */ diff --git a/src/table/settings.h b/src/table/settings.h --- a/src/table/settings.h +++ b/src/table/settings.h @@ -371,7 +371,11 @@ const SettingDesc _settings[] = { SDT_CONDOMANY(GameSettings, vehicle.road_side, SLE_UINT8, 97, SL_MAX_VERSION, 0,NN, 1, 1, _roadsides, STR_NULL, CheckRoadSide, NULL), SDT_BOOL(GameSettings, construction.build_on_slopes, 0,NN, true, STR_CONFIG_SETTING_BUILDONSLOPES, NULL), - SDT_CONDVAR(GameSettings, construction.command_pause_level, SLE_UINT8,154, SL_MAX_VERSION, 0,MS|NN, 1, 0, 3, 1, STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL, NULL), + SDT_CONDVAR(GameSettings, construction.command_pause_level, SLE_UINT8,154, SL_MAX_VERSION, 0,MS|NN, 1, 0, 3, 1, STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL, NULL), + SDT_CONDVAR(GameSettings, construction.terraform_per_64k_frames,SLE_UINT32,156,SL_MAX_VERSION, 0, 0, 64 << 16, 0, 1 << 30, 1, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, construction.terraform_frame_burst, SLE_UINT16,156, SL_MAX_VERSION, 0, 0, 4096, 0, 1 << 30, 1, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, construction.clear_per_64k_frames, SLE_UINT32,156, SL_MAX_VERSION, 0, 0, 64 << 16, 0, 1 << 30, 1, STR_NULL, NULL), + SDT_CONDVAR(GameSettings, construction.clear_frame_burst, SLE_UINT16,156, SL_MAX_VERSION, 0, 0, 4096, 0, 1 << 30, 1, STR_NULL, NULL), SDT_CONDBOOL(GameSettings, construction.autoslope, 75, SL_MAX_VERSION, 0, 0, true, STR_CONFIG_SETTING_AUTOSLOPE, NULL), SDT_BOOL(GameSettings, construction.extra_dynamite, 0, 0, true, STR_CONFIG_SETTING_EXTRADYNAMITE, NULL), SDT_BOOL(GameSettings, construction.longbridges, 0,NN, true, STR_CONFIG_SETTING_LONGBRIDGES, NULL), diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp --- a/src/terraform_cmd.cpp +++ b/src/terraform_cmd.cpp @@ -18,6 +18,8 @@ #include "economy_func.h" #include "genworld.h" #include "object_base.h" +#include "company_base.h" +#include "company_func.h" #include "table/strings.h" @@ -346,6 +348,11 @@ CommandCost CmdTerraformLand(TileIndex t } } + Company *c = Company::GetIfValid(_current_company); + if (c != NULL && (int)GB(c->terraform_limit, 16, 16) < ts.modheight_count) { + return_cmd_error(STR_ERROR_TERRAFORM_LIMIT_REACHED); + } + if (flags & DC_EXEC) { /* change the height */ { @@ -368,6 +375,8 @@ CommandCost CmdTerraformLand(TileIndex t MarkTileDirtyByTile(*ti); } } + + if (c != NULL) c->terraform_limit -= ts.modheight_count << 16; } return total_cost; } @@ -411,6 +420,9 @@ CommandCost CmdLevelLand(TileIndex tile, CommandCost last_error(lm == LM_LEVEL ? STR_ERROR_ALREADY_LEVELLED : INVALID_STRING_ID); bool had_success = false; + const Company *c = Company::GetIfValid(_current_company); + int limit = (c == NULL ? INT32_MAX : GB(c->terraform_limit, 16, 16)); + TileArea ta(tile, p1); TileIterator *iter = HasBit(p2, 0) ? (TileIterator *)new DiagonalTileIterator(tile, p1) : new OrthogonalTileIterator(ta); for (; *iter != INVALID_TILE; ++(*iter)) { @@ -420,6 +432,9 @@ CommandCost CmdLevelLand(TileIndex tile, CommandCost ret = DoCommand(t, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND); if (ret.Failed()) { last_error = ret; + + /* Did we reach the limit? */ + if (ret.GetErrorMessage() == STR_ERROR_TERRAFORM_LIMIT_REACHED) limit = 0; break; } @@ -431,12 +446,21 @@ CommandCost CmdLevelLand(TileIndex tile, return cost; } DoCommand(t, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND); + } else { + /* When we're at the terraform limit we better bail (unneeded) testing as well. + * This will probably cause the terraforming cost to be underestimated, but only + * when it's near the terraforming limit. Even then, the estimation is + * completely off due to it basically counting terraforming double, so it being + * cut off earlier might even give a better estimate in some cases. */ + if (--limit <= 0) break; } cost.AddCost(ret); curh += (curh > h) ? -1 : 1; had_success = true; } + + if (limit <= 0) break; } delete iter;