changeset 16992:e364b524e15a draft

(svn r21728) -Fix/Feature [FS#4331]: (configurably) limit amount of tiles that can be cleared/terraformed by a company
author rubidium <rubidium@openttd.org>
date Tue, 04 Jan 2011 22:50:09 +0000
parents b316e0552a7d
children 56795b8de540
files src/company_base.h src/company_cmd.cpp src/company_func.h src/landscape.cpp src/lang/english.txt src/openttd.cpp src/saveload/afterload.cpp src/saveload/company_sl.cpp src/saveload/saveload.cpp src/settings_type.h src/table/settings.h src/terraform_cmd.cpp
diffstat 12 files changed, 87 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/src/company_base.h
+++ b/src/company_base.h
@@ -69,6 +69,9 @@
 	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.
--- a/src/company_cmd.cpp
+++ b/src/company_cmd.cpp
@@ -58,6 +58,9 @@
 	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 @@
 	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.
--- a/src/company_func.h
+++ b/src/company_func.h
@@ -23,6 +23,7 @@
 void ShowBuyCompanyDialog(CompanyID company);
 void CompanyAdminUpdate(const Company *company);
 void CompanyAdminBankrupt(CompanyID company_id);
+void UpdateLandscapingLimits();
 
 extern CompanyByte _local_company;
 extern CompanyByte _current_company;
--- 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 @@
 		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 @@
 		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 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 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 @@
 					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);
 	}
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -3476,6 +3476,8 @@
 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
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -1236,6 +1236,7 @@
 {
 	/* dont execute the state loop during pause */
 	if (_pause_mode != PM_UNPAUSED) {
+		UpdateLandscapingLimits();
 		CallWindowTickEvent();
 		return;
 	}
@@ -1248,6 +1249,7 @@
 		CallVehicleTicks();
 		CallLandscapeTick();
 		ClearStorageChanges(true);
+		UpdateLandscapingLimits();
 
 		CallWindowTickEvent();
 		NewsLoop();
@@ -1273,6 +1275,7 @@
 		ClearStorageChanges(true);
 
 		AI::GameLoop();
+		UpdateLandscapingLimits();
 
 		CallWindowTickEvent();
 		NewsLoop();
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -2405,6 +2405,13 @@
 			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 */
--- a/src/saveload/company_sl.cpp
+++ b/src/saveload/company_sl.cpp
@@ -140,6 +140,9 @@
 	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()
 };
 
--- 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
 
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -214,6 +214,11 @@
 	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. */
--- a/src/table/settings.h
+++ b/src/table/settings.h
@@ -371,7 +371,11 @@
  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),
--- 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 @@
 		}
 	}
 
+	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 @@
 				MarkTileDirtyByTile(*ti);
 			}
 		}
+
+		if (c != NULL) c->terraform_limit -= ts.modheight_count << 16;
 	}
 	return total_cost;
 }
@@ -411,6 +420,9 @@
 	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 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 @@
 					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;