changeset 11997:e2187548a5e4 draft

(svn r16403) -Codechange: move code related to subsidies to separate file
author smatz <smatz@openttd.org>
date Sat, 23 May 2009 15:46:00 +0000
parents b43cf6c7ea61
children 683ebb3d3eb6
files projects/openttd_vs80.vcproj projects/openttd_vs90.vcproj source.list src/ai/api/ai_subsidy.cpp src/ai/api/ai_subsidylist.cpp src/date.cpp src/economy.cpp src/economy_func.h src/economy_type.h src/industry_cmd.cpp src/saveload/oldloader_sl.cpp src/saveload/subsidy_sl.cpp src/station.cpp src/subsidy.cpp src/subsidy_func.h src/subsidy_gui.cpp src/subsidy_type.h src/town_cmd.cpp
diffstat 18 files changed, 435 insertions(+), 354 deletions(-) [+]
line wrap: on
line diff
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -720,6 +720,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\subsidy.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\texteff.cpp"
 				>
 			</File>
@@ -1444,6 +1448,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\subsidy_func.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\subsidy_type.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\tar_type.h"
 				>
 			</File>
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -717,6 +717,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\subsidy.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\texteff.cpp"
 				>
 			</File>
@@ -1441,6 +1445,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\subsidy_func.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\subsidy_type.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\tar_type.h"
 				>
 			</File>
--- a/source.list
+++ b/source.list
@@ -74,6 +74,7 @@
 station.cpp
 string.cpp
 strings.cpp
+subsidy.cpp
 texteff.cpp
 tgp.cpp
 #if HAVE_THREAD
@@ -291,6 +292,8 @@
 string_type.h
 strings_func.h
 strings_type.h
+subsidy_func.h
+subsidy_type.h
 tar_type.h
 terraform_gui.h
 textbuf_gui.h
--- a/src/ai/api/ai_subsidy.cpp
+++ b/src/ai/api/ai_subsidy.cpp
@@ -4,7 +4,7 @@
 
 #include "ai_subsidy.hpp"
 #include "ai_date.hpp"
-#include "../../economy_func.h"
+#include "../../subsidy_type.h"
 #include "../../station_base.h"
 #include "../../cargotype.h"
 
--- a/src/ai/api/ai_subsidylist.cpp
+++ b/src/ai/api/ai_subsidylist.cpp
@@ -4,7 +4,7 @@
 
 #include "ai_subsidylist.hpp"
 #include "ai_subsidy.hpp"
-#include "../../economy_func.h"
+#include "../../subsidy_type.h"
 
 AISubsidyList::AISubsidyList()
 {
--- a/src/date.cpp
+++ b/src/date.cpp
@@ -163,6 +163,7 @@
 extern void TownsMonthlyLoop();
 extern void IndustryMonthlyLoop();
 extern void StationMonthlyLoop();
+extern void SubsidyMonthlyLoop();
 
 extern void CompaniesYearlyLoop();
 extern void VehiclesYearlyLoop();
@@ -249,6 +250,7 @@
 
 		InvalidateWindowClasses(WC_CHEATS);
 		CompaniesMonthlyLoop();
+		SubsidyMonthlyLoop();
 		EnginesMonthlyLoop();
 		TownsMonthlyLoop();
 		IndustryMonthlyLoop();
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -33,6 +33,7 @@
 #include "autoreplace_func.h"
 #include "company_gui.h"
 #include "signs_base.h"
+#include "subsidy_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -87,7 +88,6 @@
 
 int _score_part[MAX_COMPANIES][SCORE_END];
 Economy _economy;
-Subsidy _subsidies[MAX_COMPANIES];
 Prices _price;
 uint16 _price_frac[NUM_PRICES];
 Money  _cargo_payment_rates[NUM_CARGO];
@@ -857,274 +857,6 @@
 	return ((Money*)&_price)[index];
 }
 
-
-Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
-{
-	TileIndex tile;
-	TileIndex tile2;
-	Pair tp;
-
-	/* if mode is false, use the singular form */
-	const CargoSpec *cs = GetCargo(s->cargo_type);
-	SetDParam(0, mode ? cs->name : cs->name_single);
-
-	if (s->age < 12) {
-		if (cs->town_effect != TE_PASSENGERS && cs->town_effect != TE_MAIL) {
-			SetDParam(1, STR_INDUSTRY);
-			SetDParam(2, s->from);
-			tile = Industry::Get(s->from)->xy;
-
-			if (cs->town_effect != TE_GOODS && cs->town_effect != TE_FOOD) {
-				SetDParam(4, STR_INDUSTRY);
-				SetDParam(5, s->to);
-				tile2 = Industry::Get(s->to)->xy;
-			} else {
-				SetDParam(4, STR_TOWN);
-				SetDParam(5, s->to);
-				tile2 = Town::Get(s->to)->xy;
-			}
-		} else {
-			SetDParam(1, STR_TOWN);
-			SetDParam(2, s->from);
-			tile = Town::Get(s->from)->xy;
-
-			SetDParam(4, STR_TOWN);
-			SetDParam(5, s->to);
-			tile2 = Town::Get(s->to)->xy;
-		}
-	} else {
-		SetDParam(1, s->from);
-		tile = Station::Get(s->from)->xy;
-
-		SetDParam(2, s->to);
-		tile2 = Station::Get(s->to)->xy;
-	}
-
-	tp.a = tile;
-	tp.b = tile2;
-
-	return tp;
-}
-
-void DeleteSubsidyWithTown(TownID index)
-{
-	Subsidy *s;
-
-	for (s = _subsidies; s != endof(_subsidies); s++) {
-		if (s->cargo_type != CT_INVALID && s->age < 12) {
-			const CargoSpec *cs = GetCargo(s->cargo_type);
-			if (((cs->town_effect == TE_PASSENGERS || cs->town_effect == TE_MAIL) && (index == s->from || index == s->to)) ||
-				((cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) && index == s->to)) {
-				s->cargo_type = CT_INVALID;
-			}
-		}
-	}
-}
-
-void DeleteSubsidyWithIndustry(IndustryID index)
-{
-	Subsidy *s;
-
-	for (s = _subsidies; s != endof(_subsidies); s++) {
-		if (s->cargo_type != CT_INVALID && s->age < 12) {
-			const CargoSpec *cs = GetCargo(s->cargo_type);
-			if (cs->town_effect != TE_PASSENGERS && cs->town_effect != TE_MAIL &&
-				(index == s->from || (cs->town_effect != TE_GOODS && cs->town_effect != TE_FOOD && index == s->to))) {
-				s->cargo_type = CT_INVALID;
-			}
-		}
-	}
-}
-
-void DeleteSubsidyWithStation(StationID index)
-{
-	Subsidy *s;
-	bool dirty = false;
-
-	for (s = _subsidies; s != endof(_subsidies); s++) {
-		if (s->cargo_type != CT_INVALID && s->age >= 12 &&
-				(s->from == index || s->to == index)) {
-			s->cargo_type = CT_INVALID;
-			dirty = true;
-		}
-	}
-
-	if (dirty)
-		InvalidateWindow(WC_SUBSIDIES_LIST, 0);
-}
-
-struct FoundRoute {
-	uint distance;
-	CargoID cargo;
-	void *from;
-	void *to;
-};
-
-static void FindSubsidyPassengerRoute(FoundRoute *fr)
-{
-	Town *from, *to;
-
-	fr->distance = UINT_MAX;
-
-	fr->from = from = GetRandomTown();
-	if (from == NULL || from->population < 400) return;
-
-	fr->to = to = GetRandomTown();
-	if (from == to || to == NULL || to->population < 400 || to->pct_pass_transported > 42)
-		return;
-
-	fr->distance = DistanceManhattan(from->xy, to->xy);
-}
-
-static void FindSubsidyCargoRoute(FoundRoute *fr)
-{
-	Industry *i;
-	int trans, total;
-	CargoID cargo;
-
-	fr->distance = UINT_MAX;
-
-	fr->from = i = GetRandomIndustry();
-	if (i == NULL) return;
-
-	/* Randomize cargo type */
-	if (HasBit(Random(), 0) && i->produced_cargo[1] != CT_INVALID) {
-		cargo = i->produced_cargo[1];
-		trans = i->last_month_pct_transported[1];
-		total = i->last_month_production[1];
-	} else {
-		cargo = i->produced_cargo[0];
-		trans = i->last_month_pct_transported[0];
-		total = i->last_month_production[0];
-	}
-
-	/* Quit if no production in this industry
-	 * or if the cargo type is passengers
-	 * or if the pct transported is already large enough */
-	if (total == 0 || trans > 42 || cargo == CT_INVALID) return;
-
-	const CargoSpec *cs = GetCargo(cargo);
-	if (cs->town_effect == TE_PASSENGERS) return;
-
-	fr->cargo = cargo;
-
-	if (cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) {
-		/*  The destination is a town */
-		Town *t = GetRandomTown();
-
-		/* Only want big towns */
-		if (t == NULL || t->population < 900) return;
-
-		fr->distance = DistanceManhattan(i->xy, t->xy);
-		fr->to = t;
-	} else {
-		/* The destination is an industry */
-		Industry *i2 = GetRandomIndustry();
-
-		/* The industry must accept the cargo */
-		if (i2 == NULL || i == i2 ||
-				(cargo != i2->accepts_cargo[0] &&
-				cargo != i2->accepts_cargo[1] &&
-				cargo != i2->accepts_cargo[2])) {
-			return;
-		}
-		fr->distance = DistanceManhattan(i->xy, i2->xy);
-		fr->to = i2;
-	}
-}
-
-static bool CheckSubsidyDuplicate(Subsidy *s)
-{
-	const Subsidy *ss;
-
-	for (ss = _subsidies; ss != endof(_subsidies); ss++) {
-		if (s != ss &&
-				ss->from == s->from &&
-				ss->to == s->to &&
-				ss->cargo_type == s->cargo_type) {
-			s->cargo_type = CT_INVALID;
-			return true;
-		}
-	}
-	return false;
-}
-
-
-static void SubsidyMonthlyHandler()
-{
-	Subsidy *s;
-	Pair pair;
-	Station *st;
-	uint n;
-	FoundRoute fr;
-	bool modified = false;
-
-	for (s = _subsidies; s != endof(_subsidies); s++) {
-		if (s->cargo_type == CT_INVALID) continue;
-
-		if (s->age == 12 - 1) {
-			pair = SetupSubsidyDecodeParam(s, 1);
-			AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, pair.a, pair.b);
-			s->cargo_type = CT_INVALID;
-			modified = true;
-			AI::BroadcastNewEvent(new AIEventSubsidyOfferExpired(s - _subsidies));
-		} else if (s->age == 2 * 12 - 1) {
-			st = Station::Get(s->to);
-			if (st->owner == _local_company) {
-				pair = SetupSubsidyDecodeParam(s, 1);
-				AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, pair.a, pair.b);
-			}
-			s->cargo_type = CT_INVALID;
-			modified = true;
-			AI::BroadcastNewEvent(new AIEventSubsidyExpired(s - _subsidies));
-		} else {
-			s->age++;
-		}
-	}
-
-	/* 25% chance to go on */
-	if (Chance16(1, 4)) {
-		/*  Find a free slot*/
-		s = _subsidies;
-		while (s->cargo_type != CT_INVALID) {
-			if (++s == endof(_subsidies))
-				goto no_add;
-		}
-
-		n = 1000;
-		do {
-			FindSubsidyPassengerRoute(&fr);
-			if (fr.distance <= 70) {
-				s->cargo_type = CT_PASSENGERS;
-				s->from = ((Town*)fr.from)->index;
-				s->to = ((Town*)fr.to)->index;
-				goto add_subsidy;
-			}
-			FindSubsidyCargoRoute(&fr);
-			if (fr.distance <= 70) {
-				s->cargo_type = fr.cargo;
-				s->from = ((Industry*)fr.from)->index;
-				{
-					const CargoSpec *cs = GetCargo(fr.cargo);
-					s->to = (cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) ? ((Town*)fr.to)->index : ((Industry*)fr.to)->index;
-				}
-	add_subsidy:
-				if (!CheckSubsidyDuplicate(s)) {
-					s->age = 0;
-					pair = SetupSubsidyDecodeParam(s, 0);
-					AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, pair.a, pair.b);
-					AI::BroadcastNewEvent(new AIEventSubsidyOffer(s - _subsidies));
-					modified = true;
-					break;
-				}
-			}
-		} while (n--);
-	}
-no_add:;
-	if (modified)
-		InvalidateWindow(WC_SUBSIDIES_LIST, 0);
-}
-
 Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type)
 {
 	const CargoSpec *cs = GetCargo(cargo_type);
@@ -1173,7 +905,6 @@
 	return BigMulS(dist * time_factor * num_pieces, _cargo_payment_rates[cargo_type], 21);
 }
 
-
 struct FindIndustryToDeliverData {
 	const Rect *rect;            ///< Station acceptance rectangle
 	CargoID cargo_type;          ///< Cargo type that was delivered
@@ -1273,73 +1004,6 @@
 	}
 }
 
-static bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type)
-{
-	Subsidy *s;
-	TileIndex xy;
-	Pair pair;
-
-	/* check if there is an already existing subsidy that applies to us */
-	for (s = _subsidies; s != endof(_subsidies); s++) {
-		if (s->cargo_type == cargo_type &&
-				s->age >= 12 &&
-				s->from == from->index &&
-				s->to == to->index) {
-			return true;
-		}
-	}
-
-	/* check if there's a new subsidy that applies.. */
-	for (s = _subsidies; s != endof(_subsidies); s++) {
-		if (s->cargo_type == cargo_type && s->age < 12) {
-			/* Check distance from source */
-			const CargoSpec *cs = GetCargo(cargo_type);
-			if (cs->town_effect == TE_PASSENGERS || cs->town_effect == TE_MAIL) {
-				xy = Town::Get(s->from)->xy;
-			} else {
-				xy = Industry::Get(s->from)->xy;
-			}
-			if (DistanceMax(xy, from->xy) > 9) continue;
-
-			/* Check distance from dest */
-			switch (cs->town_effect) {
-				case TE_PASSENGERS:
-				case TE_MAIL:
-				case TE_GOODS:
-				case TE_FOOD:
-					xy = Town::Get(s->to)->xy;
-					break;
-
-				default:
-					xy = Industry::Get(s->to)->xy;
-					break;
-			}
-			if (DistanceMax(xy, to->xy) > 9) continue;
-
-			/* Found a subsidy, change the values to indicate that it's in use */
-			s->age = 12;
-			s->from = from->index;
-			s->to = to->index;
-
-			/* Add a news item */
-			pair = SetupSubsidyDecodeParam(s, 0);
-			InjectDParam(1);
-
-			SetDParam(0, _current_company);
-			AddNewsItem(
-				STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier,
-				NS_SUBSIDIES,
-				pair.a, pair.b
-			);
-			AI::BroadcastNewEvent(new AIEventSubsidyAwarded(s - _subsidies));
-
-			InvalidateWindow(WC_SUBSIDIES_LIST, 0);
-			return true;
-		}
-	}
-	return false;
-}
-
 /**
  * Delivers goods to industries/towns and calculates the payment
  * @param num_pieces amount of cargo delivered
@@ -1826,7 +1490,6 @@
 	/* Reset the _current_company flag */
 	_current_company = OWNER_NONE;
 	HandleEconomyFluctuations();
-	SubsidyMonthlyHandler();
 }
 
 static void DoAcquireCompany(Company *c)
--- a/src/economy_func.h
+++ b/src/economy_func.h
@@ -22,7 +22,6 @@
 extern const ScoreInfo _score_info[];
 extern int _score_part[MAX_COMPANIES][SCORE_END];
 extern Economy _economy;
-extern Subsidy _subsidies[MAX_COMPANIES];
 /* Prices and also the fractional part. */
 extern Prices _price;
 extern uint16 _price_frac[NUM_PRICES];
@@ -30,10 +29,6 @@
 extern uint16 _cargo_payment_rates_frac[NUM_CARGO];
 
 int UpdateCompanyRatingAndValue(Company *c, bool update);
-Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode);
-void DeleteSubsidyWithTown(TownID index);
-void DeleteSubsidyWithIndustry(IndustryID index);
-void DeleteSubsidyWithStation(StationID index);
 void StartupIndustryDailyChanges(bool init_counter);
 
 Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type);
--- a/src/economy_type.h
+++ b/src/economy_type.h
@@ -23,14 +23,6 @@
 	uint32 industry_daily_increment;      ///< The value which will increment industry_daily_change_counter. Computed value. NOSAVE
 };
 
-struct Subsidy {
-	CargoID cargo_type;
-	byte age;
-	/* from and to can either be TownID, StationID or IndustryID */
-	uint16 from;
-	uint16 to;
-};
-
 enum ScoreID {
 	SCORE_BEGIN      = 0,
 	SCORE_VEHICLES   = 0,
--- a/src/industry_cmd.cpp
+++ b/src/industry_cmd.cpp
@@ -34,6 +34,7 @@
 #include "effectvehicle_func.h"
 #include "ai/ai.hpp"
 #include "core/pool_func.hpp"
+#include "subsidy_func.h"
 
 #include "table/strings.h"
 #include "table/industry_land.h"
--- a/src/saveload/oldloader_sl.cpp
+++ b/src/saveload/oldloader_sl.cpp
@@ -11,6 +11,7 @@
 #include "../ship.h"
 #include "../train.h"
 #include "../signs_base.h"
+#include "../subsidy_type.h"
 #include "../debug.h"
 #include "../depot_base.h"
 #include "../newgrf_config.h"
--- a/src/saveload/subsidy_sl.cpp
+++ b/src/saveload/subsidy_sl.cpp
@@ -3,7 +3,7 @@
 /** @file subsidy_sl.cpp Code handling saving and loading of subsidies */
 
 #include "../stdafx.h"
-#include "../economy_func.h"
+#include "../subsidy_type.h"
 
 #include "saveload.h"
 
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -20,6 +20,7 @@
 #include "aircraft.h"
 #include "vehicle_gui.h"
 #include "settings_type.h"
+#include "subsidy_func.h"
 #include "core/pool_func.hpp"
 
 #include "table/strings.h"
new file mode 100644
--- /dev/null
+++ b/src/subsidy.cpp
@@ -0,0 +1,354 @@
+/* $Id$ */
+
+/** @file subsidy.cpp Handling of subsidies. */
+
+#include "stdafx.h"
+#include "company_func.h"
+#include "industry.h"
+#include "map_func.h"
+#include "town.h"
+#include "news_func.h"
+#include "ai/ai.hpp"
+#include "station_base.h"
+#include "cargotype.h"
+#include "strings_func.h"
+#include "window_func.h"
+#include "subsidy_type.h"
+
+#include "table/strings.h"
+
+Subsidy _subsidies[MAX_COMPANIES];
+
+Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
+{
+	TileIndex tile;
+	TileIndex tile2;
+	Pair tp;
+
+	/* if mode is false, use the singular form */
+	const CargoSpec *cs = GetCargo(s->cargo_type);
+	SetDParam(0, mode ? cs->name : cs->name_single);
+
+	if (s->age < 12) {
+		if (cs->town_effect != TE_PASSENGERS && cs->town_effect != TE_MAIL) {
+			SetDParam(1, STR_INDUSTRY);
+			SetDParam(2, s->from);
+			tile = Industry::Get(s->from)->xy;
+
+			if (cs->town_effect != TE_GOODS && cs->town_effect != TE_FOOD) {
+				SetDParam(4, STR_INDUSTRY);
+				SetDParam(5, s->to);
+				tile2 = Industry::Get(s->to)->xy;
+			} else {
+				SetDParam(4, STR_TOWN);
+				SetDParam(5, s->to);
+				tile2 = Town::Get(s->to)->xy;
+			}
+		} else {
+			SetDParam(1, STR_TOWN);
+			SetDParam(2, s->from);
+			tile = Town::Get(s->from)->xy;
+
+			SetDParam(4, STR_TOWN);
+			SetDParam(5, s->to);
+			tile2 = Town::Get(s->to)->xy;
+		}
+	} else {
+		SetDParam(1, s->from);
+		tile = Station::Get(s->from)->xy;
+
+		SetDParam(2, s->to);
+		tile2 = Station::Get(s->to)->xy;
+	}
+
+	tp.a = tile;
+	tp.b = tile2;
+
+	return tp;
+}
+
+void DeleteSubsidyWithTown(TownID index)
+{
+	Subsidy *s;
+
+	for (s = _subsidies; s != endof(_subsidies); s++) {
+		if (s->cargo_type != CT_INVALID && s->age < 12) {
+			const CargoSpec *cs = GetCargo(s->cargo_type);
+			if (((cs->town_effect == TE_PASSENGERS || cs->town_effect == TE_MAIL) && (index == s->from || index == s->to)) ||
+				((cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) && index == s->to)) {
+				s->cargo_type = CT_INVALID;
+			}
+		}
+	}
+}
+
+void DeleteSubsidyWithIndustry(IndustryID index)
+{
+	Subsidy *s;
+
+	for (s = _subsidies; s != endof(_subsidies); s++) {
+		if (s->cargo_type != CT_INVALID && s->age < 12) {
+			const CargoSpec *cs = GetCargo(s->cargo_type);
+			if (cs->town_effect != TE_PASSENGERS && cs->town_effect != TE_MAIL &&
+				(index == s->from || (cs->town_effect != TE_GOODS && cs->town_effect != TE_FOOD && index == s->to))) {
+				s->cargo_type = CT_INVALID;
+			}
+		}
+	}
+}
+
+void DeleteSubsidyWithStation(StationID index)
+{
+	Subsidy *s;
+	bool dirty = false;
+
+	for (s = _subsidies; s != endof(_subsidies); s++) {
+		if (s->cargo_type != CT_INVALID && s->age >= 12 &&
+				(s->from == index || s->to == index)) {
+			s->cargo_type = CT_INVALID;
+			dirty = true;
+		}
+	}
+
+	if (dirty)
+		InvalidateWindow(WC_SUBSIDIES_LIST, 0);
+}
+
+struct FoundRoute {
+	uint distance;
+	CargoID cargo;
+	void *from;
+	void *to;
+};
+
+static void FindSubsidyPassengerRoute(FoundRoute *fr)
+{
+	Town *from, *to;
+
+	fr->distance = UINT_MAX;
+
+	fr->from = from = GetRandomTown();
+	if (from == NULL || from->population < 400) return;
+
+	fr->to = to = GetRandomTown();
+	if (from == to || to == NULL || to->population < 400 || to->pct_pass_transported > 42)
+		return;
+
+	fr->distance = DistanceManhattan(from->xy, to->xy);
+}
+
+static void FindSubsidyCargoRoute(FoundRoute *fr)
+{
+	Industry *i;
+	int trans, total;
+	CargoID cargo;
+
+	fr->distance = UINT_MAX;
+
+	fr->from = i = GetRandomIndustry();
+	if (i == NULL) return;
+
+	/* Randomize cargo type */
+	if (HasBit(Random(), 0) && i->produced_cargo[1] != CT_INVALID) {
+		cargo = i->produced_cargo[1];
+		trans = i->last_month_pct_transported[1];
+		total = i->last_month_production[1];
+	} else {
+		cargo = i->produced_cargo[0];
+		trans = i->last_month_pct_transported[0];
+		total = i->last_month_production[0];
+	}
+
+	/* Quit if no production in this industry
+	 * or if the cargo type is passengers
+	 * or if the pct transported is already large enough */
+	if (total == 0 || trans > 42 || cargo == CT_INVALID) return;
+
+	const CargoSpec *cs = GetCargo(cargo);
+	if (cs->town_effect == TE_PASSENGERS) return;
+
+	fr->cargo = cargo;
+
+	if (cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) {
+		/*  The destination is a town */
+		Town *t = GetRandomTown();
+
+		/* Only want big towns */
+		if (t == NULL || t->population < 900) return;
+
+		fr->distance = DistanceManhattan(i->xy, t->xy);
+		fr->to = t;
+	} else {
+		/* The destination is an industry */
+		Industry *i2 = GetRandomIndustry();
+
+		/* The industry must accept the cargo */
+		if (i2 == NULL || i == i2 ||
+				(cargo != i2->accepts_cargo[0] &&
+				cargo != i2->accepts_cargo[1] &&
+				cargo != i2->accepts_cargo[2])) {
+			return;
+		}
+		fr->distance = DistanceManhattan(i->xy, i2->xy);
+		fr->to = i2;
+	}
+}
+
+static bool CheckSubsidyDuplicate(Subsidy *s)
+{
+	const Subsidy *ss;
+
+	for (ss = _subsidies; ss != endof(_subsidies); ss++) {
+		if (s != ss &&
+				ss->from == s->from &&
+				ss->to == s->to &&
+				ss->cargo_type == s->cargo_type) {
+			s->cargo_type = CT_INVALID;
+			return true;
+		}
+	}
+	return false;
+}
+
+
+void SubsidyMonthlyLoop()
+{
+	Subsidy *s;
+	Pair pair;
+	Station *st;
+	uint n;
+	FoundRoute fr;
+	bool modified = false;
+
+	for (s = _subsidies; s != endof(_subsidies); s++) {
+		if (s->cargo_type == CT_INVALID) continue;
+
+		if (s->age == 12 - 1) {
+			pair = SetupSubsidyDecodeParam(s, 1);
+			AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, pair.a, pair.b);
+			s->cargo_type = CT_INVALID;
+			modified = true;
+			AI::BroadcastNewEvent(new AIEventSubsidyOfferExpired(s - _subsidies));
+		} else if (s->age == 2 * 12 - 1) {
+			st = Station::Get(s->to);
+			if (st->owner == _local_company) {
+				pair = SetupSubsidyDecodeParam(s, 1);
+				AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, pair.a, pair.b);
+			}
+			s->cargo_type = CT_INVALID;
+			modified = true;
+			AI::BroadcastNewEvent(new AIEventSubsidyExpired(s - _subsidies));
+		} else {
+			s->age++;
+		}
+	}
+
+	/* 25% chance to go on */
+	if (Chance16(1, 4)) {
+		/*  Find a free slot*/
+		s = _subsidies;
+		while (s->cargo_type != CT_INVALID) {
+			if (++s == endof(_subsidies))
+				goto no_add;
+		}
+
+		n = 1000;
+		do {
+			FindSubsidyPassengerRoute(&fr);
+			if (fr.distance <= 70) {
+				s->cargo_type = CT_PASSENGERS;
+				s->from = ((Town*)fr.from)->index;
+				s->to = ((Town*)fr.to)->index;
+				goto add_subsidy;
+			}
+			FindSubsidyCargoRoute(&fr);
+			if (fr.distance <= 70) {
+				s->cargo_type = fr.cargo;
+				s->from = ((Industry*)fr.from)->index;
+				{
+					const CargoSpec *cs = GetCargo(fr.cargo);
+					s->to = (cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) ? ((Town*)fr.to)->index : ((Industry*)fr.to)->index;
+				}
+	add_subsidy:
+				if (!CheckSubsidyDuplicate(s)) {
+					s->age = 0;
+					pair = SetupSubsidyDecodeParam(s, 0);
+					AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, pair.a, pair.b);
+					AI::BroadcastNewEvent(new AIEventSubsidyOffer(s - _subsidies));
+					modified = true;
+					break;
+				}
+			}
+		} while (n--);
+	}
+no_add:;
+	if (modified)
+		InvalidateWindow(WC_SUBSIDIES_LIST, 0);
+}
+
+bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type)
+{
+	Subsidy *s;
+	TileIndex xy;
+	Pair pair;
+
+	/* check if there is an already existing subsidy that applies to us */
+	for (s = _subsidies; s != endof(_subsidies); s++) {
+		if (s->cargo_type == cargo_type &&
+				s->age >= 12 &&
+				s->from == from->index &&
+				s->to == to->index) {
+			return true;
+		}
+	}
+
+	/* check if there's a new subsidy that applies.. */
+	for (s = _subsidies; s != endof(_subsidies); s++) {
+		if (s->cargo_type == cargo_type && s->age < 12) {
+			/* Check distance from source */
+			const CargoSpec *cs = GetCargo(cargo_type);
+			if (cs->town_effect == TE_PASSENGERS || cs->town_effect == TE_MAIL) {
+				xy = Town::Get(s->from)->xy;
+			} else {
+				xy = Industry::Get(s->from)->xy;
+			}
+			if (DistanceMax(xy, from->xy) > 9) continue;
+
+			/* Check distance from dest */
+			switch (cs->town_effect) {
+				case TE_PASSENGERS:
+				case TE_MAIL:
+				case TE_GOODS:
+				case TE_FOOD:
+					xy = Town::Get(s->to)->xy;
+					break;
+
+				default:
+					xy = Industry::Get(s->to)->xy;
+					break;
+			}
+			if (DistanceMax(xy, to->xy) > 9) continue;
+
+			/* Found a subsidy, change the values to indicate that it's in use */
+			s->age = 12;
+			s->from = from->index;
+			s->to = to->index;
+
+			/* Add a news item */
+			pair = SetupSubsidyDecodeParam(s, 0);
+			InjectDParam(1);
+
+			SetDParam(0, _current_company);
+			AddNewsItem(
+				STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier,
+				NS_SUBSIDIES,
+				pair.a, pair.b
+			);
+			AI::BroadcastNewEvent(new AIEventSubsidyAwarded(s - _subsidies));
+
+			InvalidateWindow(WC_SUBSIDIES_LIST, 0);
+			return true;
+		}
+	}
+	return false;
+}
new file mode 100644
--- /dev/null
+++ b/src/subsidy_func.h
@@ -0,0 +1,22 @@
+/* $Id$ */
+
+/** @file subsidy_func.h Functions related to subsidies. */
+
+#ifndef SUBSIDY_FUNC_H
+#define SUBSIDY_FUNC_H
+
+#include "core/geometry_type.hpp"
+#include "station_type.h"
+#include "town_type.h"
+#include "industry_type.h"
+#include "company_type.h"
+#include "subsidy_type.h"
+
+Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode);
+void DeleteSubsidyWithTown(TownID index);
+void DeleteSubsidyWithIndustry(IndustryID index);
+void DeleteSubsidyWithStation(StationID index);
+bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type);
+void SubsidyMonthlyHandler();
+
+#endif /* SUBSIDY_FUNC_H */
--- a/src/subsidy_gui.cpp
+++ b/src/subsidy_gui.cpp
@@ -14,6 +14,7 @@
 #include "viewport_func.h"
 #include "gfx_func.h"
 #include "gui.h"
+#include "subsidy_func.h"
 
 #include "table/strings.h"
 
new file mode 100644
--- /dev/null
+++ b/src/subsidy_type.h
@@ -0,0 +1,21 @@
+/* $Id$ */
+
+/** @file subsidy_type.h Types related to subsidies. */
+
+#ifndef SUBSIDY_TYPE_H
+#define SUBSIDY_TYPE_H
+
+#include "cargo_type.h"
+#include "company_type.h"
+
+struct Subsidy {
+	CargoID cargo_type;
+	byte age;
+	/* from and to can either be TownID, StationID or IndustryID */
+	uint16 from;
+	uint16 to;
+};
+
+extern Subsidy _subsidies[MAX_COMPANIES];
+
+#endif /* SUBSIDY_TYPE_H */
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -40,6 +40,7 @@
 #include "functions.h"
 #include "animated_tile_func.h"
 #include "date_func.h"
+#include "subsidy_func.h"
 #include "core/smallmap_type.hpp"
 #include "core/pool_func.hpp"