changeset 18570:bbdb2c13eb92 draft

(svn r23415) -Feature: Infrastructure maintenance costs.
author michi_cc <michi_cc@openttd.org>
date Sat, 03 Dec 2011 23:40:46 +0000
parents 42709d5de004
children 820ed7ca5fcc
files src/company_gui.cpp src/core/math_func.cpp src/core/math_func.hpp src/economy.cpp src/economy_type.h src/lang/english.txt src/newgrf.cpp src/newgrf_airport.h src/rail.h src/road_func.h src/saveload/saveload.cpp src/settings.cpp src/settings_gui.cpp src/settings_type.h src/station.cpp src/station_func.h src/table/airport_defaults.h src/table/pricebase.h src/table/railtypes.h src/table/settings.ini src/water.h
diffstat 21 files changed, 300 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/src/company_gui.cpp
+++ b/src/company_gui.cpp
@@ -34,6 +34,9 @@
 #include "rail.h"
 #include "engine_base.h"
 #include "window_func.h"
+#include "road_func.h"
+#include "water.h"
+#include "station_func.h"
 
 #include "table/strings.h"
 
@@ -1624,6 +1627,8 @@
 	CIW_WIDGET_WATER_COUNT,
 	CIW_WIDGET_STATION_DESC,
 	CIW_WIDGET_STATION_COUNT,
+	CIW_WIDGET_TOTAL_DESC,
+	CIW_WIDGET_TOTAL,
 };
 
 static const NWidgetPart _nested_company_infrastructure_widgets[] = {
@@ -1651,6 +1656,10 @@
 				NWidget(WWT_EMPTY, COLOUR_GREY, CIW_WIDGET_STATION_DESC), SetMinimalTextLines(3, 0), SetFill(1, 0),
 				NWidget(WWT_EMPTY, COLOUR_GREY, CIW_WIDGET_STATION_COUNT), SetMinimalTextLines(3, 0), SetFill(0, 1),
 			EndContainer(),
+			NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
+				NWidget(WWT_EMPTY, COLOUR_GREY, CIW_WIDGET_TOTAL_DESC), SetFill(1, 0),
+				NWidget(WWT_EMPTY, COLOUR_GREY, CIW_WIDGET_TOTAL), SetFill(0, 1),
+			EndContainer(),
 		EndContainer(),
 	EndContainer(),
 };
@@ -1663,6 +1672,8 @@
 	RailTypes railtypes; ///< Valid railtypes.
 	RoadTypes roadtypes; ///< Valid roadtypes.
 
+	uint total_width; ///< String width of the total cost line.
+
 	CompanyInfrastructureWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
 	{
 		this->UpdateRailRoadTypes();
@@ -1697,6 +1708,27 @@
 		}
 	}
 
+	/** Get total infrastructure maintenance cost. */
+	Money GetTotalMaintenanceCost() const
+	{
+		const Company *c = Company::Get((CompanyID)this->window_number);
+		Money total;
+
+		for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
+			if (HasBit(this->railtypes, rt)) total += RailMaintenanceCost(rt, c->infrastructure.rail[rt]);
+		}
+		total += SignalMaintenanceCost(c->infrastructure.signal);
+
+		if (HasBit(this->roadtypes, ROADTYPE_ROAD)) total += RoadMaintenanceCost(ROADTYPE_ROAD, c->infrastructure.road[ROADTYPE_ROAD]);
+		if (HasBit(this->roadtypes, ROADTYPE_TRAM)) total += RoadMaintenanceCost(ROADTYPE_TRAM, c->infrastructure.road[ROADTYPE_TRAM]);
+
+		total += CanalMaintenanceCost(c->infrastructure.water);
+		total += StationMaintenanceCost(c->infrastructure.station);
+		total += AirportMaintenanceCost(c->index);
+
+		return total;
+	}
+
 	virtual void SetStringParameters(int widget) const
 	{
 		switch (widget) {
@@ -1749,22 +1781,42 @@
 			case CIW_WIDGET_RAIL_COUNT:
 			case CIW_WIDGET_ROAD_COUNT:
 			case CIW_WIDGET_WATER_COUNT:
-			case CIW_WIDGET_STATION_COUNT: {
+			case CIW_WIDGET_STATION_COUNT:
+			case CIW_WIDGET_TOTAL: {
 				/* Find the maximum count that is displayed. */
 				uint32 max_val = 100000; // Some random number to reserve enough space.
+				Money max_cost = 100000; // Some random number to reserve enough space.
 				for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
 					max_val = max(max_val, c->infrastructure.rail[rt]);
+					max_cost = max(max_cost, RailMaintenanceCost(rt, c->infrastructure.rail[rt]));
 				}
 				max_val = max(max_val, c->infrastructure.signal);
+				max_cost = max(max_cost, SignalMaintenanceCost(c->infrastructure.signal));
 				for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
 					max_val = max(max_val, c->infrastructure.road[rt]);
+					max_cost = max(max_cost, RoadMaintenanceCost(rt, c->infrastructure.road[rt]));
 				}
 				max_val = max(max_val, c->infrastructure.water);
+				max_cost = max(max_cost, CanalMaintenanceCost(c->infrastructure.water));
 				max_val = max(max_val, c->infrastructure.station);
+				max_cost = max(max_cost, StationMaintenanceCost(c->infrastructure.station));
 				max_val = max(max_val, c->infrastructure.airport);
+				max_cost = max(max_cost, AirportMaintenanceCost(c->index));
 
 				SetDParam(0, max_val);
-				size->width = max(size->width, GetStringBoundingBox(STR_WHITE_COMMA).width + 15); // Reserve some wiggle room.
+				SetDParam(1, max_cost * 12); // Convert to per year
+				size->width = max(size->width, GetStringBoundingBox(_settings_game.economy.infrastructure_maintenance ? STR_COMPANY_INFRASTRUCTURE_VIEW_COST : STR_WHITE_COMMA).width + 20); // Reserve some wiggle room.
+
+				if (_settings_game.economy.infrastructure_maintenance) {
+					SetDParam(0, this->GetTotalMaintenanceCost());
+					this->total_width = GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL).width + 20;
+					size->width = max(size->width, this->total_width);
+				}
+
+				/* Set height of the total line. */
+				if (widget == CIW_WIDGET_TOTAL) {
+					size->height = _settings_game.economy.infrastructure_maintenance ? max(size->height, EXP_LINESPACE + FONT_HEIGHT_NORMAL) : 0;
+				}
 				break;
 			}
 		}
@@ -1803,12 +1855,14 @@
 				for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
 					if (HasBit(this->railtypes, rt)) {
 						SetDParam(0, c->infrastructure.rail[rt]);
-						DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_COMMA);
+						SetDParam(1, RailMaintenanceCost(rt, c->infrastructure.rail[rt]) * 12); // Convert to per year
+						DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, _settings_game.economy.infrastructure_maintenance ? STR_COMPANY_INFRASTRUCTURE_VIEW_COST : STR_WHITE_COMMA);
 					}
 				}
 				if (this->railtypes != RAILTYPES_NONE) {
 					SetDParam(0, c->infrastructure.signal);
-					DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_COMMA);
+					SetDParam(1, SignalMaintenanceCost(c->infrastructure.signal) * 12); // Convert to per year
+					DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, _settings_game.economy.infrastructure_maintenance ? STR_COMPANY_INFRASTRUCTURE_VIEW_COST : STR_WHITE_COMMA);
 				}
 				break;
 
@@ -1828,11 +1882,13 @@
 			case CIW_WIDGET_ROAD_COUNT:
 				if (HasBit(this->roadtypes, ROADTYPE_ROAD)) {
 					SetDParam(0, c->infrastructure.road[ROADTYPE_ROAD]);
-					DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_COMMA);
+					SetDParam(1, RoadMaintenanceCost(ROADTYPE_ROAD, c->infrastructure.road[ROADTYPE_ROAD]) * 12); // Convert to per year
+					DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, _settings_game.economy.infrastructure_maintenance ? STR_COMPANY_INFRASTRUCTURE_VIEW_COST : STR_WHITE_COMMA);
 				}
 				if (HasBit(this->roadtypes, ROADTYPE_TRAM)) {
 					SetDParam(0, c->infrastructure.road[ROADTYPE_TRAM]);
-					DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_COMMA);
+					SetDParam(1, RoadMaintenanceCost(ROADTYPE_TRAM, c->infrastructure.road[ROADTYPE_TRAM]) * 12); // Convert to per year
+					DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, _settings_game.economy.infrastructure_maintenance ? STR_COMPANY_INFRASTRUCTURE_VIEW_COST : STR_WHITE_COMMA);
 				}
 				break;
 
@@ -1843,7 +1899,17 @@
 
 			case CIW_WIDGET_WATER_COUNT:
 				SetDParam(0, c->infrastructure.water);
-				DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_COMMA);
+				SetDParam(1, CanalMaintenanceCost(c->infrastructure.water) * 12); // Convert to per year
+				DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, _settings_game.economy.infrastructure_maintenance ? STR_COMPANY_INFRASTRUCTURE_VIEW_COST : STR_WHITE_COMMA);
+				break;
+
+			case CIW_WIDGET_TOTAL:
+				if (_settings_game.economy.infrastructure_maintenance) {
+					GfxFillRect(r.left, y, r.left + this->total_width, y, PC_WHITE);
+					y += EXP_LINESPACE;
+					SetDParam(0, this->GetTotalMaintenanceCost() * 12); // Convert to per year
+					DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL);
+				}
 				break;
 
 			case CIW_WIDGET_STATION_DESC:
@@ -1854,9 +1920,11 @@
 
 			case CIW_WIDGET_STATION_COUNT:
 				SetDParam(0, c->infrastructure.station);
-				DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_COMMA);
+				SetDParam(1, StationMaintenanceCost(c->infrastructure.station) * 12); // Convert to per year
+				DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, _settings_game.economy.infrastructure_maintenance ? STR_COMPANY_INFRASTRUCTURE_VIEW_COST : STR_WHITE_COMMA);
 				SetDParam(0, c->infrastructure.airport);
-				DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, STR_WHITE_COMMA);
+				SetDParam(1, AirportMaintenanceCost(c->index) * 12); // Convert to per year
+				DrawString(r.left, r.right, y += FONT_HEIGHT_NORMAL, _settings_game.economy.infrastructure_maintenance ? STR_COMPANY_INFRASTRUCTURE_VIEW_COST : STR_WHITE_COMMA);
 				break;
 		}
 	}
--- a/src/core/math_func.cpp
+++ b/src/core/math_func.cpp
@@ -46,3 +46,33 @@
 	return a;
 
 }
+
+/**
+ * Compute the integer square root.
+ * @param num Radicand.
+ * @return Rounded integer square root.
+ * @note Algorithm taken from http://en.wikipedia.org/wiki/Methods_of_computing_square_roots
+ */
+uint32 IntSqrt(uint32 num)
+{
+	uint32 res = 0;
+	uint32 bit = 1UL << 30; // Second to top bit number.
+
+	/* 'bit' starts at the highest power of four <= the argument. */
+	while (bit > num) bit >>= 2;
+
+	while (bit != 0) {
+		if (num >= res + bit) {
+			num -= res + bit;
+			res = (res >> 1) + bit;
+		} else {
+			res >>= 1;
+		}
+		bit >>= 2;
+	}
+
+	/* Arithmetic rounding to nearest integer. */
+	if (num > res) res++;
+
+	return res;
+}
--- a/src/core/math_func.hpp
+++ b/src/core/math_func.hpp
@@ -346,4 +346,6 @@
 	}
 }
 
+uint32 IntSqrt(uint32 num);
+
 #endif /* MATH_FUNC_HPP */
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -46,6 +46,7 @@
 #include "core/pool_func.hpp"
 #include "newgrf.h"
 #include "core/backup_type.hpp"
+#include "water.h"
 
 #include "table/strings.h"
 #include "table/pricebase.h"
@@ -584,17 +585,39 @@
 	Station *st;
 
 	Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
-	FOR_ALL_STATIONS(st) {
-		cur_company.Change(st->owner);
-		CommandCost cost(EXPENSES_PROPERTY, _price[PR_STATION_VALUE] >> 1);
-		SubtractMoneyFromCompany(cost);
+	Company *c;
+
+	if (!_settings_game.economy.infrastructure_maintenance) {
+		FOR_ALL_STATIONS(st) {
+			cur_company.Change(st->owner);
+			CommandCost cost(EXPENSES_PROPERTY, _price[PR_STATION_VALUE] >> 1);
+			SubtractMoneyFromCompany(cost);
+		}
+	} else {
+		/* Improved monthly infrastructure costs. */
+		FOR_ALL_COMPANIES(c) {
+			cur_company.Change(c->index);
+
+			CommandCost cost(EXPENSES_PROPERTY);
+			for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
+				if (c->infrastructure.rail[rt] != 0) cost.AddCost(RailMaintenanceCost(rt, c->infrastructure.rail[rt]));
+			}
+			cost.AddCost(SignalMaintenanceCost(c->infrastructure.signal));
+			for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
+				if (c->infrastructure.road[rt] != 0) cost.AddCost(RoadMaintenanceCost(rt, c->infrastructure.road[rt]));
+			}
+			cost.AddCost(CanalMaintenanceCost(c->infrastructure.water));
+			cost.AddCost(StationMaintenanceCost(c->infrastructure.station));
+			cost.AddCost(AirportMaintenanceCost(c->index));
+
+			SubtractMoneyFromCompany(cost);
+		}
 	}
 	cur_company.Restore();
 
 	/* Only run the economic statics and update company stats every 3rd month (1st of quarter). */
 	if (!HasBit(1 << 0 | 1 << 3 | 1 << 6 | 1 << 9, _cur_month)) return;
 
-	Company *c;
 	FOR_ALL_COMPANIES(c) {
 		memmove(&c->old_economy[1], &c->old_economy[0], sizeof(c->old_economy) - sizeof(c->old_economy[0]));
 		c->old_economy[0] = c->cur_economy;
@@ -713,6 +736,7 @@
 	SetWindowClassesDirty(WC_BUILD_VEHICLE);
 	SetWindowClassesDirty(WC_REPLACE_VEHICLE);
 	SetWindowClassesDirty(WC_VEHICLE_DETAILS);
+	SetWindowClassesDirty(WC_COMPANY_INFRASTRUCTURE);
 	InvalidateWindowData(WC_PAYMENT_RATES, 0);
 }
 
--- a/src/economy_type.h
+++ b/src/economy_type.h
@@ -132,6 +132,11 @@
 	PR_CLEAR_AQUEDUCT,
 	PR_BUILD_LOCK,
 	PR_CLEAR_LOCK,
+	PR_INFRASTRUCTURE_RAIL,
+	PR_INFRASTRUCTURE_ROAD,
+	PR_INFRASTRUCTURE_WATER,
+	PR_INFRASTRUCTURE_STATION,
+	PR_INFRASTRUCTURE_AIRPORT,
 
 	PR_END,
 	INVALID_PRICE = 0xFF
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1156,6 +1156,7 @@
 STR_CONFIG_SETTING_ADJACENT_STATIONS                            :{LTBLUE}Allow building adjacent stations: {ORANGE}{STRING}
 STR_CONFIG_SETTING_DYNAMIC_ENGINES                              :{LTBLUE}Enable multiple NewGRF engine sets: {ORANGE}{STRING}
 STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES            :{WHITE}Changing this setting is not possible when there are vehicles
+STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE                   :{LTBLUE}Infrastructure maintenance: {ORANGE}{STRING1}
 
 STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS                        :{LTBLUE}Airports never expire: {ORANGE}{STRING1}
 
@@ -2788,6 +2789,8 @@
 STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT                    :{GOLD}Stations:
 STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS                        :{WHITE}Station tiles
 STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS                        :{WHITE}Airports
+STR_COMPANY_INFRASTRUCTURE_VIEW_COST                            :{WHITE}{1:CURRENCY_LONG}/yr ({0:COMMA})
+STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL                           :{WHITE}{CURRENCY_LONG}/yr
 
 # Industry directory
 STR_INDUSTRY_DIRECTORY_CAPTION                                  :{WHITE}Industries
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -3635,6 +3635,10 @@
 				_string_to_grf_mapping[&as->name] = _cur.grffile->grfid;
 				break;
 
+			case 0x11: // Maintenance cost factor
+				as->maintenance_cost = buf->ReadWord();
+				break;
+
 			default:
 				ret = CIR_UNKNOWN;
 				break;
@@ -3941,6 +3945,10 @@
 				_string_to_grf_mapping[&rti->strings.name] = _cur.grffile->grfid;
 				break;
 
+			case 0x1C: // Maintenance cost factor
+				rti->maintenance_multiplier = buf->ReadWord();
+				break;
+
 			default:
 				ret = CIR_UNKNOWN;
 				break;
@@ -3984,6 +3992,7 @@
 			case 0x13: // Construction cost
 			case 0x14: // Speed limit
 			case 0x1B: // Name of railtype
+			case 0x1C: // Maintenance cost factor
 				buf->ReadWord();
 				break;
 
--- a/src/newgrf_airport.h
+++ b/src/newgrf_airport.h
@@ -76,6 +76,7 @@
 	TTDPAirportType ttd_airport_type;      ///< ttdpatch airport type (Small/Large/Helipad/Oilrig)
 	AirportClassID cls_id;                 ///< the class to which this airport type belongs
 	SpriteID preview_sprite;               ///< preview sprite for this airport
+	uint16 maintenance_cost;               ///< maintenance cost mulltiplier
 	/* Newgrf data */
 	bool enabled;                          ///< entity still avaible (by default true).newgrf can disable it, though
 	struct GRFFileProps grf_prop;          ///< properties related the the grf file
--- a/src/rail.h
+++ b/src/rail.h
@@ -20,6 +20,7 @@
 #include "slope_type.h"
 #include "strings_type.h"
 #include "date_type.h"
+#include "core/math_func.hpp"
 
 /** Railtype flags. */
 enum RailTypeFlags {
@@ -189,6 +190,11 @@
 	uint16 cost_multiplier;
 
 	/**
+	 * Cost multiplier for maintenance of this rail type
+	 */
+	uint16 maintenance_multiplier;
+
+	/**
 	 * Acceleration type of this rail type
 	 */
 	uint8 acceleration_type;
@@ -363,6 +369,28 @@
 	return rebuildcost;
 }
 
+/**
+ * Calculates the maintenance cost of a number of track bits.
+ * @param railtype The railtype to get the cost of.
+ * @param num Number of track bits.
+ * @return Total cost.
+ */
+static inline Money RailMaintenanceCost(RailType railtype, uint32 num)
+{
+	assert(railtype < RAILTYPE_END);
+	return (_price[PR_INFRASTRUCTURE_RAIL] * GetRailTypeInfo(railtype)->maintenance_multiplier * num * (1 + IntSqrt(num))) >> 11; // 4 bits fraction for the multiplier and 7 bits scaling.
+}
+
+/**
+ * Calculates the maintenance cost of a number of signals.
+ * @param num Number of signals.
+ * @return Total cost.
+ */
+static inline Money SignalMaintenanceCost(uint32 num)
+{
+	return (_price[PR_INFRASTRUCTURE_RAIL] * 15 * num * (1 + IntSqrt(num))) >> 8; // 1 bit fraction for the multiplier and 7 bits scaling.
+}
+
 void DrawTrainDepotSprite(int x, int y, int image, RailType railtype);
 int TicksToLeaveDepot(const Train *v);
 
--- a/src/road_func.h
+++ b/src/road_func.h
@@ -17,6 +17,7 @@
 #include "direction_type.h"
 #include "company_type.h"
 #include "tile_type.h"
+#include "economy_func.h"
 
 /**
  * Iterate through each set RoadType in a RoadTypes value.
@@ -148,6 +149,19 @@
 	return a == AXIS_X ? ROAD_X : ROAD_Y;
 }
 
+
+/**
+ * Calculates the maintenance cost of a number of road bits.
+ * @param roadtype Road type to get the cost for.
+ * @param num Number of road bits.
+ * @return Total cost.
+ */
+static inline Money RoadMaintenanceCost(RoadType roadtype, uint32 num)
+{
+	assert(roadtype < ROADTYPE_END);
+	return (_price[PR_INFRASTRUCTURE_ROAD] * (roadtype == ROADTYPE_TRAM ? 3 : 2) * num * (1 + IntSqrt(num))) >> 9; // 2 bits fraction for the multiplier and 7 bits scaling.
+}
+
 bool HasRoadTypesAvail(const CompanyID company, const RoadTypes rts);
 bool ValParamRoadType(const RoadType rt);
 RoadTypes GetCompanyRoadtypes(const CompanyID company);
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -230,8 +230,9 @@
  *  163   22767
  *  164   23290
  *  165   23304
+ *  166   23415
  */
-extern const uint16 SAVEGAME_VERSION = 165; ///< Current savegame version of OpenTTD.
+extern const uint16 SAVEGAME_VERSION = 166; ///< Current savegame version of OpenTTD.
 
 SavegameType _savegame_type; ///< type of savegame we are loading
 
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -946,6 +946,17 @@
 	return true;
 }
 
+/**
+ * Invalidate the company infrastructure details window after a infrastructure maintenance setting change.
+ * @param p1 Unused.
+ * @return Always true.
+ */
+static bool InvalidateCompanyInfrastructureWindow(int32 p1)
+{
+	InvalidateWindowClassesData(WC_COMPANY_INFRASTRUCTURE);
+	return true;
+}
+
 /*
  * A: competitors
  * B: competitor start time. Deprecated since savegame version 110.
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -1492,6 +1492,7 @@
 	SettingEntry("economy.inflation"),
 	SettingEntry("economy.smooth_economy"),
 	SettingEntry("economy.feeder_payment_share"),
+	SettingEntry("economy.infrastructure_maintenance"),
 };
 /** Economy sub-page */
 static SettingsPage _settings_economy_page = {_settings_economy, lengthof(_settings_economy)};
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -416,6 +416,7 @@
 	bool   station_noise_level;              ///< build new airports when the town noise level is still within accepted limits
 	uint16 town_noise_population[3];         ///< population to base decision on noise evaluation (@see town_council_tolerance)
 	bool   allow_town_level_crossings;       ///< towns are allowed to build level crossings
+	bool   infrastructure_maintenance;       ///< enable monthly maintenance fee for owner infrastructure
 };
 
 /** Settings related to stations. */
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -523,3 +523,22 @@
 	this->bottom = src.bottom;
 	return *this;
 }
+
+/**
+ * Calculates the maintenance cost of all airports of a company.
+ * @param owner Company.
+ * @return Total cost.
+ */
+Money AirportMaintenanceCost(Owner owner)
+{
+	Money total_cost = 0;
+
+	const Station *st;
+	FOR_ALL_STATIONS(st) {
+		if (st->owner == owner && (st->facilities & FACIL_AIRPORT)) {
+			total_cost += _price[PR_INFRASTRUCTURE_AIRPORT] * st->airport.GetSpec()->maintenance_cost;
+		}
+	}
+	/* 3 bits fraction for the maintenance cost factor. */
+	return total_cost >> 3;
+}
--- a/src/station_func.h
+++ b/src/station_func.h
@@ -18,6 +18,7 @@
 #include "road_type.h"
 #include "cargo_type.h"
 #include "company_type.h"
+#include "economy_func.h"
 
 void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius);
 
@@ -46,4 +47,16 @@
 
 void UpdateAirportsNoise();
 
+/**
+ * Calculates the maintenance cost of a number of station tiles.
+ * @param num Number of station tiles.
+ * @return Total cost.
+ */
+static inline Money StationMaintenanceCost(uint32 num)
+{
+	return (_price[PR_INFRASTRUCTURE_STATION] * num * (1 + IntSqrt(num))) >> 7; // 7 bits scaling.
+}
+
+Money AirportMaintenanceCost(Owner owner);
+
 #endif /* STATION_FUNC_H */
--- a/src/table/airport_defaults.h
+++ b/src/table/airport_defaults.h
@@ -381,36 +381,36 @@
 #undef MKEND
 
 /** General AirportSpec definition. */
-#define AS_GENERIC(fsm, att, rot, att_len, depot_tbl, num_depots, size_x, size_y, noise, catchment, min_year, max_year, ttdpatch_type, class_id, name, preview, enabled) \
-	{fsm, att, rot, att_len, depot_tbl, num_depots, size_x, size_y, noise, catchment, min_year, max_year, name, ttdpatch_type, class_id, preview, enabled, GRFFileProps(AT_INVALID)}
+#define AS_GENERIC(fsm, att, rot, att_len, depot_tbl, num_depots, size_x, size_y, noise, catchment, min_year, max_year, maint_cost, ttdpatch_type, class_id, name, preview, enabled) \
+	{fsm, att, rot, att_len, depot_tbl, num_depots, size_x, size_y, noise, catchment, min_year, max_year, name, ttdpatch_type, class_id, preview, maint_cost, enabled, GRFFileProps(AT_INVALID)}
 
 /** AirportSpec definition for airports without any depot. */
-#define AS_ND(ap_name, size_x, size_y, min_year, max_year, catchment, noise, ttdpatch_type, class_id, name, preview) \
+#define AS_ND(ap_name, size_x, size_y, min_year, max_year, catchment, noise, maint_cost, ttdpatch_type, class_id, name, preview) \
 	AS_GENERIC(&_airportfta_##ap_name, _tile_table_##ap_name, _default_airports_rotation, lengthof(_tile_table_##ap_name), NULL, 0, \
-		size_x, size_y, noise, catchment, min_year, max_year, ttdpatch_type, class_id, name, preview, true)
+		size_x, size_y, noise, catchment, min_year, max_year, maint_cost, ttdpatch_type, class_id, name, preview, true)
 
 /** AirportSpec definition for airports with at least one depot. */
-#define AS(ap_name, size_x, size_y, min_year, max_year, catchment, noise, ttdpatch_type, class_id, name, preview) \
+#define AS(ap_name, size_x, size_y, min_year, max_year, catchment, noise, maint_cost, ttdpatch_type, class_id, name, preview) \
 	AS_GENERIC(&_airportfta_##ap_name, _tile_table_##ap_name, _default_airports_rotation, lengthof(_tile_table_##ap_name), _airport_depots_##ap_name, lengthof(_airport_depots_##ap_name), \
-		size_x, size_y, noise, catchment, min_year, max_year, ttdpatch_type, class_id, name, preview, true)
+		size_x, size_y, noise, catchment, min_year, max_year, maint_cost, ttdpatch_type, class_id, name, preview, true)
 
 /* The helidepot and helistation have ATP_TTDP_SMALL because they are at ground level */
 extern const AirportSpec _origin_airport_specs[] = {
-	AS(country,          4, 3,     0,     1959,  4,  3, ATP_TTDP_SMALL,    APC_SMALL,    STR_AIRPORT_SMALL,            SPR_AIRPORT_PREVIEW_SMALL),
-	AS(city,             6, 6,  1955, MAX_YEAR,  5,  5, ATP_TTDP_LARGE,    APC_LARGE,    STR_AIRPORT_CITY,             SPR_AIRPORT_PREVIEW_LARGE),
-	AS_ND(heliport,      1, 1,  1963, MAX_YEAR,  4,  1, ATP_TTDP_HELIPORT, APC_HELIPORT, STR_AIRPORT_HELIPORT,         SPR_AIRPORT_PREVIEW_HELIPORT),
-	AS(metropolitan,     6, 6,  1980, MAX_YEAR,  6,  8, ATP_TTDP_LARGE,    APC_LARGE,    STR_AIRPORT_METRO,            SPR_AIRPORT_PREVIEW_METROPOLITAN),
-	AS(international,    7, 7,  1990, MAX_YEAR,  8, 17, ATP_TTDP_LARGE,    APC_HUB,      STR_AIRPORT_INTERNATIONAL,    SPR_AIRPORT_PREVIEW_INTERNATIONAL),
-	AS(commuter,         5, 4,  1983, MAX_YEAR,  4,  4, ATP_TTDP_SMALL,    APC_SMALL,    STR_AIRPORT_COMMUTER,         SPR_AIRPORT_PREVIEW_COMMUTER),
-	AS(helidepot,        2, 2,  1976, MAX_YEAR,  4,  2, ATP_TTDP_SMALL,    APC_HELIPORT, STR_AIRPORT_HELIDEPOT,        SPR_AIRPORT_PREVIEW_HELIDEPOT),
-	AS(intercontinental, 9, 11, 2002, MAX_YEAR, 10, 25, ATP_TTDP_LARGE,    APC_HUB,      STR_AIRPORT_INTERCONTINENTAL, SPR_AIRPORT_PREVIEW_INTERCONTINENTAL),
-	AS(helistation,      4, 2,  1980, MAX_YEAR,  4,  3, ATP_TTDP_SMALL,    APC_HELIPORT, STR_AIRPORT_HELISTATION,      SPR_AIRPORT_PREVIEW_HELISTATION),
-	AS_GENERIC(&_airportfta_oilrig, NULL, _default_airports_rotation, 0, NULL, 0, 1, 1, 0, 4, 0, 0, ATP_TTDP_OILRIG, APC_HELIPORT, STR_NULL, 0, false),
+	AS(country,          4, 3,     0,     1959,  4,  3,  7, ATP_TTDP_SMALL,    APC_SMALL,    STR_AIRPORT_SMALL,            SPR_AIRPORT_PREVIEW_SMALL),
+	AS(city,             6, 6,  1955, MAX_YEAR,  5,  5, 24, ATP_TTDP_LARGE,    APC_LARGE,    STR_AIRPORT_CITY,             SPR_AIRPORT_PREVIEW_LARGE),
+	AS_ND(heliport,      1, 1,  1963, MAX_YEAR,  4,  1,  4, ATP_TTDP_HELIPORT, APC_HELIPORT, STR_AIRPORT_HELIPORT,         SPR_AIRPORT_PREVIEW_HELIPORT),
+	AS(metropolitan,     6, 6,  1980, MAX_YEAR,  6,  8, 28, ATP_TTDP_LARGE,    APC_LARGE,    STR_AIRPORT_METRO,            SPR_AIRPORT_PREVIEW_METROPOLITAN),
+	AS(international,    7, 7,  1990, MAX_YEAR,  8, 17, 42, ATP_TTDP_LARGE,    APC_HUB,      STR_AIRPORT_INTERNATIONAL,    SPR_AIRPORT_PREVIEW_INTERNATIONAL),
+	AS(commuter,         5, 4,  1983, MAX_YEAR,  4,  4, 20, ATP_TTDP_SMALL,    APC_SMALL,    STR_AIRPORT_COMMUTER,         SPR_AIRPORT_PREVIEW_COMMUTER),
+	AS(helidepot,        2, 2,  1976, MAX_YEAR,  4,  2,  7, ATP_TTDP_SMALL,    APC_HELIPORT, STR_AIRPORT_HELIDEPOT,        SPR_AIRPORT_PREVIEW_HELIDEPOT),
+	AS(intercontinental, 9, 11, 2002, MAX_YEAR, 10, 25, 72, ATP_TTDP_LARGE,    APC_HUB,      STR_AIRPORT_INTERCONTINENTAL, SPR_AIRPORT_PREVIEW_INTERCONTINENTAL),
+	AS(helistation,      4, 2,  1980, MAX_YEAR,  4,  3, 14, ATP_TTDP_SMALL,    APC_HELIPORT, STR_AIRPORT_HELISTATION,      SPR_AIRPORT_PREVIEW_HELISTATION),
+	AS_GENERIC(&_airportfta_oilrig, NULL, _default_airports_rotation, 0, NULL, 0, 1, 1, 0, 4, 0, 0, 0, ATP_TTDP_OILRIG, APC_HELIPORT, STR_NULL, 0, false),
 };
 
 assert_compile(NEW_AIRPORT_OFFSET == lengthof(_origin_airport_specs));
 
-AirportSpec AirportSpec::dummy = AS_GENERIC(&_airportfta_dummy, NULL, _default_airports_rotation, 0, NULL, 0, 0, 0, 0, 0, MIN_YEAR, MIN_YEAR, ATP_TTDP_LARGE, APC_BEGIN, STR_NULL, 0, false);
+AirportSpec AirportSpec::dummy = AS_GENERIC(&_airportfta_dummy, NULL, _default_airports_rotation, 0, NULL, 0, 0, 0, 0, 0, MIN_YEAR, MIN_YEAR, 0, ATP_TTDP_LARGE, APC_BEGIN, STR_NULL, 0, false);
 
 #undef AS
 #undef AS_ND
--- a/src/table/pricebase.h
+++ b/src/table/pricebase.h
@@ -76,5 +76,10 @@
 	{   2000, PCAT_CONSTRUCTION, GSF_END,          PR_CLEAR_BRIDGE       }, ///< PR_CLEAR_AQUEDUCT
 	{   7500, PCAT_CONSTRUCTION, GSF_END,          PR_CLEAR_WATER        }, ///< PR_BUILD_LOCK
 	{   2000, PCAT_CONSTRUCTION, GSF_END,          PR_CLEAR_WATER        }, ///< PR_CLEAR_LOCK
+	{     12, PCAT_RUNNING,      GSF_END,          PR_BUILD_RAIL         }, ///< PR_INFRASTRUCTURE_RAIL
+	{     10, PCAT_RUNNING,      GSF_END,          PR_BUILD_ROAD         }, ///< PR_INFRASTRUCTURE_ROAD
+	{      8, PCAT_RUNNING,      GSF_END,          PR_BUILD_CANAL        }, ///< PR_INFRASTRUCTURE_WATER
+	{    100, PCAT_RUNNING,      GSF_END,          PR_STATION_VALUE      }, ///< PR_INFRASTRUCTURE_STATION
+	{   5000, PCAT_RUNNING,      GSF_END,          PR_BUILD_STATION_AIRPORT}, ///< PR_INFRASTRUCTURE_AIRPORT
 };
 assert_compile(lengthof(_price_base_specs) == PR_END);
--- a/src/table/railtypes.h
+++ b/src/table/railtypes.h
@@ -81,6 +81,9 @@
 		/* cost multiplier */
 		8,
 
+		/* maintenance cost multiplier */
+		8,
+
 		/* acceleration type */
 		0,
 
@@ -175,6 +178,9 @@
 		/* cost multiplier */
 		12,
 
+		/* maintenance cost multiplier */
+		12,
+
 		/* acceleration type */
 		0,
 
@@ -265,6 +271,9 @@
 		/* cost multiplier */
 		16,
 
+		/* maintenance cost multiplier */
+		16,
+
 		/* acceleration type */
 		1,
 
@@ -355,6 +364,9 @@
 		/* cost multiplier */
 		24,
 
+		/* maintenance cost multiplier */
+		24,
+
 		/* acceleration type */
 		2,
 
--- a/src/table/settings.ini
+++ b/src/table/settings.ini
@@ -39,6 +39,7 @@
 static bool InvalidateIndustryViewWindow(int32 p1);
 static bool InvalidateAISettingsWindow(int32 p1);
 static bool RedrawTownAuthority(int32 p1);
+static bool InvalidateCompanyInfrastructureWindow(int32 p1);
 extern bool UpdateNewGRFConfigPalette(int32 p1);
 static bool ZoomMinMaxChanged(int32 p1);
 
@@ -1237,6 +1238,14 @@
 min      = 800
 max      = 65535
 
+[SDT_BOOL]
+base     = GameSettings
+var      = economy.infrastructure_maintenance
+from     = 166
+def      = false
+str      = STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE
+proc     = InvalidateCompanyInfrastructureWindow
+
 ##
 [SDT_VAR]
 base     = GameSettings
--- a/src/water.h
+++ b/src/water.h
@@ -16,6 +16,8 @@
 #include "company_type.h"
 #include "slope_type.h"
 #include "water_map.h"
+#include "economy_func.h"
+#include "core/math_func.hpp"
 
 /**
  * Describes the behaviour of a tile during flooding.
@@ -43,4 +45,14 @@
 
 bool RiverModifyDesertZone(TileIndex tile, void *data);
 
+/**
+ * Calculates the maintenance cost of a number of canal tiles.
+ * @param num Number of canal tiles.
+ * @return Total cost.
+ */
+static inline Money CanalMaintenanceCost(uint32 num)
+{
+	return (_price[PR_INFRASTRUCTURE_WATER] * num * (1 + IntSqrt(num))) >> 6; // 6 bits scaling.
+}
+
 #endif /* WATER_H */