(svn r11442) -Feature: make news messages related to the industry (production) changes better configurable; you can now disable news messages popping up for industries you are not servicing. Patch by Alberth.
authorrubidium
Thu, 15 Nov 2007 22:20:33 +0000
changeset 7891 aef0c16244d7
parent 7890 4209769a14d8
child 7892 3e3e1c093a56
(svn r11442) -Feature: make news messages related to the industry (production) changes better configurable; you can now disable news messages popping up for industries you are not servicing. Patch by Alberth.
src/industry_cmd.cpp
src/lang/english.txt
src/news.h
src/news_gui.cpp
src/station.h
src/station_cmd.cpp
--- a/src/industry_cmd.cpp	Thu Nov 15 19:18:52 2007 +0000
+++ b/src/industry_cmd.cpp	Thu Nov 15 22:20:33 2007 +0000
@@ -13,6 +13,7 @@
 #include "table/sprites.h"
 #include "map.h"
 #include "tile.h"
+#include "train.h"
 #include "landscape.h"
 #include "viewport.h"
 #include "command.h"
@@ -1856,6 +1857,134 @@
 	return (indspec->behaviour & INDUSTRYBEH_CANCLOSE_LASTINSTANCE) == 0 && GetIndustryTypeCount(type) <= 1;
 }
 
+/**
+* Can given cargo type be accepted or produced by the industry?
+* @param cargo: Cargo type
+* @param ind: Industry
+* @param *c_accepts: Pointer to boolean for acceptance of cargo
+* @param *c_produces: Pointer to boolean for production of cargo
+* @return: \c *c_accepts is set when industry accepts the cargo type,
+*          \c *c_produces is set when the industry produces the cargo type
+*/
+static void CanCargoServiceIndustry(CargoID cargo, Industry *ind, bool *c_accepts, bool *c_produces)
+{
+	const IndustrySpec *indspec = GetIndustrySpec(ind->type);
+
+	/* Check for acceptance of cargo */
+	for (uint j = 0; j < lengthof(ind->accepts_cargo) && ind->accepts_cargo[j] != CT_INVALID; j++) {
+		if (cargo == ind->accepts_cargo[j]) {
+			if (HASBIT(indspec->callback_flags, CBM_IND_REFUSE_CARGO)) {
+				uint16 res = GetIndustryCallback(CBID_INDUSTRY_REFUSE_CARGO,
+						0, GetReverseCargoTranslation(cargo, indspec->grf_prop.grffile),
+						ind, ind->type, ind->xy);
+				if (res == 0) continue;
+			}
+			*c_accepts = true;
+			break;
+		}
+	}
+
+	/* Check for produced cargo */
+	for (uint j = 0; j < lengthof(ind->produced_cargo) && ind->produced_cargo[j] != CT_INVALID; j++) {
+		if (cargo == ind->produced_cargo[j]) {
+			*c_produces = true;
+			break;
+		}
+	}
+}
+
+/**
+* Compute who can service the industry.
+*
+* Here, 'can service' means that he/she has trains and stations close enough
+* to the industry with the right cargo type and the right orders (ie has the
+* technical means).
+*
+* @param ind: Industry being investigated.
+*
+* @return: 0 if nobody can service the industry, 2 if the local player can
+* service the industry, and 1 otherwise (only competitors can service the
+* industry)
+*/
+int WhoCanServiceIndustry(Industry* ind)
+{
+	/* Find all stations within reach of the industry */
+	StationSet stations = FindStationsAroundIndustryTile(ind->xy, ind->width, ind->height);
+
+	if (stations.size() == 0) return 0; // No stations found at all => nobody services
+
+	const Vehicle *v;
+	int result = 0;
+	FOR_ALL_VEHICLES(v) {
+		/* Is it worthwhile to try this vehicle? */
+		if (v->owner != _local_player && result != 0) continue;
+
+		/* Check whether it accepts the right kind of cargo */
+		bool c_accepts = false;
+		bool c_produces = false;
+		if (v->type == VEH_TRAIN && IsFrontEngine(v)) {
+			const Vehicle *u = v;
+			BEGIN_ENUM_WAGONS(u)
+				CanCargoServiceIndustry(u->cargo_type, ind, &c_accepts, &c_produces);
+			END_ENUM_WAGONS(u)
+		} else if (v->type == VEH_ROAD || v->type == VEH_SHIP || v->type == VEH_AIRCRAFT) {
+			CanCargoServiceIndustry(v->cargo_type, ind, &c_accepts, &c_produces);
+		} else {
+			continue;
+		}
+		if (!c_accepts && !c_produces) continue; // Wrong cargo
+
+		/* Check orders of the vehicle.
+		 * We cannot check the first of shared orders only, since the first vehicle in such a chain
+		 * may have a different cargo type.
+		 */
+		const Order *o;
+		FOR_VEHICLE_ORDERS(v, o) {
+			if (o->type == OT_GOTO_STATION && !HASBIT(o->flags, OFB_TRANSFER)) {
+				/* Vehicle visits a station to load or unload */
+				Station *st = GetStation(o->dest);
+				if (!st->IsValid()) continue;
+
+				/* Same cargo produced by industry is dropped here => not serviced by vehicle v */
+				if (HASBIT(o->flags, OFB_UNLOAD) && !c_accepts) break;
+
+				if (stations.find(st) != stations.end()) {
+					if (v->owner == _local_player) return 2; // Player services industry
+					result = 1; // Competitor services industry
+				}
+			}
+		}
+	}
+	return result;
+}
+
+/**
+* Report news that industry production has changed significantly
+*
+* @param ind: Industry with changed production
+* @param type: Cargo type that has changed
+* @param percent: Percentage of change (>0 means increase, <0 means decrease)
+*/
+static void ReportNewsProductionChangeIndustry(Industry *ind, CargoID type, int percent)
+{
+	NewsType nt;
+
+	switch (WhoCanServiceIndustry(ind)) {
+		case 0: nt = NT_INDUSTRY_NOBODY; break;
+		case 1: nt = NT_INDUSTRY_OTHER;  break;
+		case 2: nt = NT_INDUSTRY_PLAYER; break;
+		default: NOT_REACHED(); break;
+	}
+	SetDParam(2, abs(percent));
+	SetDParam(0, GetCargo(type)->name);
+	SetDParam(1, ind->index);
+	AddNewsItem(
+		percent >= 0 ? STR_INDUSTRY_PROD_GOUP : STR_INDUSTRY_PROD_GODOWN,
+		NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, nt, 0),
+		ind->xy + TileDiffXY(1, 1), 0
+	);
+}
+
 /** Change industry production or do closure
  * @param i Industry for which changes are performed
  * @param monthly true if it's the monthly call, false if it's the random call
@@ -1909,7 +2038,6 @@
 			for (byte j = 0; j < 2 && i->produced_cargo[j] != CT_INVALID; j++){
 				uint32 r = Random();
 				int old_prod, new_prod, percent;
-				int mag;
 
 				new_prod = old_prod = i->production_rate[j];
 
@@ -1932,16 +2060,8 @@
 				/* Close the industry when it has the lowest possible production rate */
 				if (new_prod > 1) closeit = false;
 
-				mag = abs(percent);
-				if (mag >= 10) {
-					SetDParam(2, mag);
-					SetDParam(0, GetCargo(i->produced_cargo[j])->name);
-					SetDParam(1, i->index);
-					AddNewsItem(
-						percent >= 0 ? STR_INDUSTRY_PROD_GOUP : STR_INDUSTRY_PROD_GODOWN,
-						NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_ECONOMY, 0),
-						i->xy + TileDiffXY(1, 1), 0
-					);
+				if (abs(percent) >= 10) {
+					ReportNewsProductionChangeIndustry(i, i->produced_cargo[j], percent);
 				}
 			}
 		} else {
@@ -1989,6 +2109,19 @@
 	}
 
 	if (!suppress_message && str != STR_NULL) {
+		NewsType nt;
+		/* Compute news category */
+		if (closeit) {
+			nt = NT_OPENCLOSE;
+		} else {
+			switch (WhoCanServiceIndustry(i)) {
+				case 0: nt = NT_INDUSTRY_NOBODY; break;
+				case 1: nt = NT_INDUSTRY_OTHER;  break;
+				case 2: nt = NT_INDUSTRY_PLAYER; break;
+				default: NOT_REACHED(); break;
+			}
+		}
+		/* Set parameters of news string */
 		if (str > STR_LAST_STRINGID) {
 			SetDParam(0, STR_TOWN);
 			SetDParam(1, i->town->index);
@@ -2000,8 +2133,9 @@
 		} else {
 			SetDParam(0, i->index);
 		}
+		/* and report the news to the user */
 		AddNewsItem(str,
-			NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, closeit ? NT_OPENCLOSE : NT_ECONOMY, 0),
+			NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, nt, 0),
 			i->xy + TileDiffXY(1, 1), 0);
 	}
 }
@@ -2084,7 +2218,7 @@
 				uint16 res = GetIndustryTileCallback(CBID_INDUSTRY_AUTOSLOPE, 0, 0, gfx, GetIndustryByTile(tile), tile);
 				if ((res == 0) || (res == CALLBACK_FAILED)) return _price.terraform;
 			} else {
-				// allow autoslope
+				/* allow autoslope */
 				return _price.terraform;
 			}
 		}
--- a/src/lang/english.txt	Thu Nov 15 19:18:52 2007 +0000
+++ b/src/lang/english.txt	Thu Nov 15 22:20:33 2007 +0000
@@ -594,6 +594,9 @@
 STR_0209_COMPANY_INFORMATION                                    :{YELLOW}Company information
 STR_NEWS_OPEN_CLOSE                                             :{YELLOW}Open / close of industries
 STR_020A_ECONOMY_CHANGES                                        :{YELLOW}Economy changes
+STR_INDUSTRY_CHANGES_SERVED_BY_PLAYER                           :{YELLOW}Production changes of industries served by the player
+STR_INDUSTRY_CHANGES_SERVED_BY_OTHER                            :{YELLOW}Production changes of industries served by competitor(s)
+STR_OTHER_INDUSTRY_PRODUCTION_CHANGES                           :{YELLOW}Other industry production changes
 STR_020B_ADVICE_INFORMATION_ON_PLAYER                           :{YELLOW}Advice / information on player's vehicles
 STR_020C_NEW_VEHICLES                                           :{YELLOW}New vehicles
 STR_020D_CHANGES_OF_CARGO_ACCEPTANCE                            :{YELLOW}Changes to cargo acceptance
--- a/src/news.h	Thu Nov 15 19:18:52 2007 +0000
+++ b/src/news.h	Thu Nov 15 22:20:33 2007 +0000
@@ -53,6 +53,9 @@
 	NT_COMPANY_INFO,    ///< Company info (new companies, bankrupcy messages)
 	NT_OPENCLOSE,       ///< Opening and closing of industries
 	NT_ECONOMY,         ///< Economic changes (recession, industry up/dowm)
+	NT_INDUSTRY_PLAYER, ///< Production changes of industry serviced by local player
+	NT_INDUSTRY_OTHER,  ///< Production changes of industry serviced by competitor(s)
+	NT_INDUSTRY_NOBODY, ///< Other industry production changes
 	NT_ADVICE,          ///< Bits of news about vehicles of the player
 	NT_NEW_VEHICLES,    ///< New vehicle has become available
 	NT_ACCEPTANCE,      ///< A type of cargo is (no longer) accepted
--- a/src/news_gui.cpp	Thu Nov 15 19:18:52 2007 +0000
+++ b/src/news_gui.cpp	Thu Nov 15 22:20:33 2007 +0000
@@ -341,6 +341,9 @@
 	60,  ///< NT_COMPANY_INFO
 	90,  ///< NT_OPENCLOSE
 	30,  ///< NT_ECONOMY
+	30,  ///< NT_INDUSTRY_PLAYER
+	30,  ///< NT_INDUSTRY_OTHER
+	30,  ///< NT_INDUSTRY_NOBODY
 	150, ///< NT_ADVICE
 	30,  ///< NT_NEW_VEHICLES
 	90,  ///< NT_ACCEPTANCE
@@ -394,17 +397,20 @@
 };
 
 static const SoundFx _news_sounds[NT_END] = {
-	SND_1D_APPLAUSE,
-	SND_1D_APPLAUSE,
-	SND_BEGIN,
-	SND_BEGIN,
-	SND_BEGIN,
-	SND_BEGIN,
-	SND_BEGIN,
-	SND_1E_OOOOH,
-	SND_BEGIN,
-	SND_BEGIN,
-	SND_BEGIN,
+	SND_1D_APPLAUSE,	///< NT_ARRIVAL_PLAYER
+	SND_1D_APPLAUSE,	///< NT_ARRIVAL_OTHER
+	SND_BEGIN,		///< NT_ACCIDENT
+	SND_BEGIN,		///< NT_COMPANY_INFO
+	SND_BEGIN,		///< NT_OPENCLOSE
+	SND_BEGIN,		///< NT_ECONOMY
+	SND_BEGIN,		///< NT_INDUSTRY_PLAYER
+	SND_BEGIN,		///< NT_INDUSTRY_OTHER
+	SND_BEGIN,		///< NT_INDUSTRY_NOBODY
+	SND_BEGIN,		///< NT_ADVICE
+	SND_1E_OOOOH,		///< NT_NEW_VEHICLES
+	SND_BEGIN,		///< NT_ACCEPTANCE
+	SND_BEGIN,		///< NT_SUBSIDIES
+	SND_BEGIN,		///< NT_GENERAL
 };
 
 const char *_news_display_name[NT_END] = {
@@ -414,6 +420,9 @@
 	"company_info",
 	"openclose",
 	"economy",
+	"production_player",
+	"production_other",
+	"production_nobody",
 	"advice",
 	"new_vehicles",
 	"acceptance",
@@ -771,6 +780,7 @@
 }
 
 
+/** News settings window widget offset constants */
 enum {
 	WIDGET_NEWSOPT_BTN_SUMMARY  = 4,  ///< Button that adjusts at once the level for all settings
 	WIDGET_NEWSOPT_DROP_SUMMARY,      ///< Drop down button for same upper button
@@ -880,83 +890,106 @@
 	}
 }
 
+
+/*
+* The news settings window widgets
+*
+* Main part of the window is a list of news-setting lines, one for each news category.
+* Each line is constructed by an expansion of the \c NEWS_SETTINGS_LINE macro
+*/
+
+/**
+* Macro to construct one news-setting line in the news-settings window.
+* One line consists of four widgets, namely
+* - A [<] button
+* - A [...] label
+* - A [>] button
+* - A text label describing the news category
+* Horizontal positions of the widgets are hard-coded, vertical start position is (\a basey + \a linenum * \c NEWS_SETTING_BASELINE_SKIP).
+* Height of one line is 12, with the text label shifted 1 pixel down.
+*
+* First line should be widget number WIDGET_NEWSOPT_START_OPTION
+*
+* @param basey: Base Y coordinate
+* @param linenum: Count, news-setting is the \a linenum-th line
+* @param text: StringID for the text label to display
+*/
+#define NEWS_SETTINGS_LINE(basey, linenum, text) \
+	{ WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \
+	    4,  12,  basey     + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \
+	  SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, \
+	{ WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_YELLOW, \
+	   13,  89,  basey     + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \
+	  STR_EMPTY, STR_NULL}, \
+	{ WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \
+	   90,  98,  basey     + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \
+	  SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, \
+        { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, \
+	  103, 409,  basey + 1 + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 13 + linenum * NEWS_SETTING_BASELINE_SKIP, \
+	  text, STR_NULL}
+
+static const int NEWS_SETTING_BASELINE_SKIP = 12; ///< Distance between two news-setting lines, should be at least 12
+
+
 static const Widget _message_options_widgets[] = {
-{   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,   10,     0,    13, STR_00C5,                              STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,   RESIZE_NONE,    13,    11,  409,     0,    13, STR_0204_MESSAGE_OPTIONS,              STR_018C_WINDOW_TITLE_DRAG_THIS},
-{      WWT_PANEL,   RESIZE_NONE,    13,     0,  409,    14,   196, 0x0,                                   STR_NULL},
+{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_BROWN,   0,  10,  0, 13,
+	STR_00C5,                 STR_018B_CLOSE_WINDOW},
+{  WWT_CAPTION, RESIZE_NONE, COLOUR_BROWN,  11, 409,  0, 13,
+	STR_0204_MESSAGE_OPTIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
+{    WWT_PANEL, RESIZE_NONE, COLOUR_BROWN,   0, 409, 14, 64 + NT_END * NEWS_SETTING_BASELINE_SKIP,
+	0x0,                      STR_NULL},
 
 /* Text at the top of the main panel, in black */
-{      WWT_LABEL,   RESIZE_NONE,    13,     0,  409,    13,    26, STR_0205_MESSAGE_TYPES,                STR_NULL},
-
-/* General drop down and sound button */
-{      WWT_PANEL,   RESIZE_NONE,     3,     4,   86,   166,   177, 0x0,                                   STR_NULL},
-{    WWT_TEXTBTN,   RESIZE_NONE,     3,    87,   98,   166,   177, STR_0225,                              STR_NULL},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,   167,   179, STR_MESSAGES_ALL,                      STR_NULL},
-
-{  WWT_TEXTBTN_2,   RESIZE_NONE,     3,     4,   98,   178,   189, STR_02DB_OFF,                          STR_NULL},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,   179,   191, STR_MESSAGE_SOUND,                     STR_NULL},
-
-/* Each four group is composed of the buttons [<] [..] [>] and the descriptor of the setting */
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    26,    37, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,    26,    37, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    26,    37, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,    27,    39, STR_0206_ARRIVAL_OF_FIRST_VEHICLE,     STR_NULL},
-
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    38,    49, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,    38,    49, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    38,    49, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,    39,    51, STR_0207_ARRIVAL_OF_FIRST_VEHICLE,     STR_NULL},
-
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    50,    61, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,    50,    61, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    50,    61, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,    51,    63, STR_0208_ACCIDENTS_DISASTERS,          STR_NULL},
-
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    62,    73, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,    62,    73, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    62,    73, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,    63,    75, STR_0209_COMPANY_INFORMATION,          STR_NULL},
+{    WWT_LABEL, RESIZE_NONE, COLOUR_BROWN,
+	  0, 409, 13, 26,
+	STR_0205_MESSAGE_TYPES,   STR_NULL},
 
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    74,    85, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,    74,    85, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    74,    85, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,    75,    87, STR_NEWS_OPEN_CLOSE,                   STR_NULL},
-
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    86,    97, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,    86,    97, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    86,    97, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,    87,    99, STR_020A_ECONOMY_CHANGES,              STR_NULL},
-
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    98,   109, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,    98,   109, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    98,   109, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,    99,   111, STR_020B_ADVICE_INFORMATION_ON_PLAYER, STR_NULL},
+/* General drop down and sound button, widgets WIDGET_NEWSOPT_BTN_SUMMARY and WIDGET_NEWSOPT_DROP_SUMMARY */
+{     WWT_PANEL, RESIZE_NONE, COLOUR_YELLOW,
+	  4,  86,  34 + NT_END * NEWS_SETTING_BASELINE_SKIP,  45 + NT_END * NEWS_SETTING_BASELINE_SKIP,
+	0x0, STR_NULL},
 
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,   110,   121, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,   110,   121, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,   110,   121, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,   111,   123, STR_020C_NEW_VEHICLES,                 STR_NULL},
+{   WWT_TEXTBTN, RESIZE_NONE, COLOUR_YELLOW,
+	 87,  98,  34 + NT_END * NEWS_SETTING_BASELINE_SKIP,  45 + NT_END * NEWS_SETTING_BASELINE_SKIP,
+	STR_0225, STR_NULL},
 
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,   122,   133, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,   122,   133, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,   122,   133, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,   123,   135, STR_020D_CHANGES_OF_CARGO_ACCEPTANCE,  STR_NULL},
+{      WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW,
+	103, 409,  35 + NT_END * NEWS_SETTING_BASELINE_SKIP,  47 + NT_END * NEWS_SETTING_BASELINE_SKIP,
+	STR_MESSAGES_ALL, STR_NULL},
 
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,   134,   145, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,   134,   145, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,   134,   145, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,   135,   147, STR_020E_SUBSIDIES,                    STR_NULL},
+/* Below is widget WIDGET_NEWSOPT_SOUNDTICKER */
+{ WWT_TEXTBTN_2, RESIZE_NONE, COLOUR_YELLOW,
+	  4,  98,  46 + NT_END * NEWS_SETTING_BASELINE_SKIP,  57 + NT_END * NEWS_SETTING_BASELINE_SKIP,
+	STR_02DB_OFF,  STR_NULL},
 
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,   146,   157, SPR_ARROW_LEFT,                        STR_HSCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    13,   89,   146,   157, STR_EMPTY,                             STR_NULL},
-{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,   146,   157, SPR_ARROW_RIGHT,                       STR_HSCROLL_BAR_SCROLLS_LIST},
-{       WWT_TEXT,   RESIZE_NONE,     3,    103, 409,   147,   159, STR_020F_GENERAL_INFORMATION,          STR_NULL},
+{      WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW,
+	103, 409,  47 + NT_END * NEWS_SETTING_BASELINE_SKIP,  59 + NT_END * NEWS_SETTING_BASELINE_SKIP,
+	STR_MESSAGE_SOUND, STR_NULL},
+
+/* List of news-setting lines (4 widgets for each line).
+ * First widget must be number WIDGET_NEWSOPT_START_OPTION
+ */
+NEWS_SETTINGS_LINE(26, NT_ARRIVAL_PLAYER, STR_0206_ARRIVAL_OF_FIRST_VEHICLE),
+NEWS_SETTINGS_LINE(26, NT_ARRIVAL_OTHER,  STR_0207_ARRIVAL_OF_FIRST_VEHICLE),
+NEWS_SETTINGS_LINE(26, NT_ACCIDENT, STR_0208_ACCIDENTS_DISASTERS),
+NEWS_SETTINGS_LINE(26, NT_COMPANY_INFO, STR_0209_COMPANY_INFORMATION),
+NEWS_SETTINGS_LINE(26, NT_OPENCLOSE, STR_NEWS_OPEN_CLOSE),
+NEWS_SETTINGS_LINE(26, NT_ECONOMY, STR_020A_ECONOMY_CHANGES),
+NEWS_SETTINGS_LINE(26, NT_INDUSTRY_PLAYER, STR_INDUSTRY_CHANGES_SERVED_BY_PLAYER),
+NEWS_SETTINGS_LINE(26, NT_INDUSTRY_OTHER, STR_INDUSTRY_CHANGES_SERVED_BY_OTHER),
+NEWS_SETTINGS_LINE(26, NT_INDUSTRY_NOBODY, STR_OTHER_INDUSTRY_PRODUCTION_CHANGES),
+NEWS_SETTINGS_LINE(26, NT_ADVICE, STR_020B_ADVICE_INFORMATION_ON_PLAYER),
+NEWS_SETTINGS_LINE(26, NT_NEW_VEHICLES, STR_020C_NEW_VEHICLES),
+NEWS_SETTINGS_LINE(26, NT_ACCEPTANCE, STR_020D_CHANGES_OF_CARGO_ACCEPTANCE),
+NEWS_SETTINGS_LINE(26, NT_SUBSIDIES, STR_020E_SUBSIDIES),
+NEWS_SETTINGS_LINE(26, NT_GENERAL, STR_020F_GENERAL_INFORMATION),
 
 {   WIDGETS_END},
 };
 
 static const WindowDesc _message_options_desc = {
-	270, 22, 410, 197, 410, 197,
+	270,  22,  410,  65 + NT_END * NEWS_SETTING_BASELINE_SKIP,
+	           410,  65 + NT_END * NEWS_SETTING_BASELINE_SKIP,
 	WC_GAME_OPTIONS, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 	_message_options_widgets,
--- a/src/station.h	Thu Nov 15 19:18:52 2007 +0000
+++ b/src/station.h	Thu Nov 15 22:20:33 2007 +0000
@@ -14,6 +14,7 @@
 #include "newgrf_station.h"
 #include "cargopacket.h"
 #include <list>
+#include <set>
 
 struct Station;
 struct RoadStop;
@@ -241,6 +242,11 @@
 
 void ModifyStationRatingAround(TileIndex tile, PlayerID owner, int amount, uint radius);
 
+/** A set of stations (\c const \c Station* ) */
+typedef std::set<Station*> StationSet;
+
+StationSet FindStationsAroundIndustryTile(TileIndex tile, int w, int h);
+
 void ShowStationViewWindow(StationID station);
 void UpdateAllStationVirtCoord();
 
--- a/src/station_cmd.cpp	Thu Nov 15 19:18:52 2007 +0000
+++ b/src/station_cmd.cpp	Thu Nov 15 22:20:33 2007 +0000
@@ -433,7 +433,14 @@
 	AddNewsItem(msg, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT | NF_TILE, NT_ACCEPTANCE, 0), st->xy, 0);
 }
 
-/** Get a list of the cargo types being produced around the tile.*/
+/**
+* Get a list of the cargo types being produced around the tile (in a rectangle).
+* @param produced: Destination array of produced cargo
+* @param tile: Center of the search area
+* @param w: Width of the center
+* @param h: Height of the center
+* @param rad: Radius of the search area
+*/
 void GetProductionAroundTiles(AcceptedCargo produced, TileIndex tile,
 	int w, int h, int rad)
 {
@@ -477,7 +484,14 @@
 	}
 }
 
-/** Get a list of the cargo types that are accepted around the tile.*/
+/**
+* Get a list of the cargo types that are accepted around the tile.
+* @param accepts: Destination array of accepted cargo
+* @param tile: Center of the search area
+* @param w: Width of the center
+* @param h: Height of the center
+* @param rad: Radius of the rectangular search area
+*/
 void GetAcceptanceAroundTiles(AcceptedCargo accepts, TileIndex tile,
 	int w, int h, int rad)
 {
@@ -2626,14 +2640,20 @@
 	return CommandCost();
 }
 
-
-uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount)
+/**
+* Find all (non-buoy) stations around an industry tile
+*
+* @param tile: Center tile to search from
+* @param w: Width of the center
+* @param h: Height of the center
+*
+* @return: Set of found stations
+*/
+StationSet FindStationsAroundIndustryTile(TileIndex tile, int w, int h)
 {
-	Station* around[8];
-
-	for (uint i = 0; i < lengthof(around); i++) around[i] = NULL;
-
-	int w_prod; //width and height of the "producer" of the cargo
+	StationSet station_set;
+
+	int w_prod; // width and height of the "producer" of the cargo
 	int h_prod;
 	int max_rad;
 	if (_patches.modified_catchment) {
@@ -2656,91 +2676,103 @@
 
 		Station *st = GetStationByTile(cur_tile);
 
-		for (uint i = 0; i != lengthof(around); i++) {
-			if (around[i] == NULL) {
-				if (!st->IsBuoy() &&
-						(st->town->exclusive_counter == 0 || st->town->exclusivity == st->owner) && // check exclusive transport rights
-						st->goods[type].rating != 0 && // when you've got the lowest rating you can get, it's better not to give cargo anymore
-						(!_patches.selectgoods || st->goods[type].last_speed != 0) && // we are servicing the station (or cargo is dumped on all stations)
-						((st->facilities & ~FACIL_BUS_STOP)   != 0 || IsCargoInClass(type, CC_PASSENGERS)) && // if we have other fac. than a bus stop, or the cargo is passengers
-						((st->facilities & ~FACIL_TRUCK_STOP) != 0 || !IsCargoInClass(type, CC_PASSENGERS))) { // if we have other fac. than a cargo bay or the cargo is not passengers
-					if (_patches.modified_catchment) {
-						/* min and max coordinates of the producer relative */
-						const int x_min_prod = max_rad + 1;
-						const int x_max_prod = max_rad + w_prod;
-						const int y_min_prod = max_rad + 1;
-						const int y_max_prod = max_rad + h_prod;
-
-						int rad = FindCatchmentRadius(st);
-
-						int x_dist = min(w_cur - x_min_prod, x_max_prod - w_cur);
-						if (w_cur < x_min_prod) {
-							x_dist = x_min_prod - w_cur;
-						} else if (w_cur > x_max_prod) {
-							x_dist = w_cur - x_max_prod;
-						}
-
-						int y_dist = min(h_cur - y_min_prod, y_max_prod - h_cur);
-						if (h_cur < y_min_prod) {
-							y_dist = y_min_prod - h_cur;
-						} else if (h_cur > y_max_prod) {
-							y_dist = h_cur - y_max_prod;
-						}
-
-						if (x_dist > rad || y_dist > rad) break;
-					}
-
-					around[i] = st;
-				}
-				break;
-			} else if (around[i] == st) {
-				break;
+		if (st->IsBuoy()) continue; // bouys don't accept cargo
+
+
+		if (_patches.modified_catchment) {
+			/* min and max coordinates of the producer relative */
+			const int x_min_prod = max_rad + 1;
+			const int x_max_prod = max_rad + w_prod;
+			const int y_min_prod = max_rad + 1;
+			const int y_max_prod = max_rad + h_prod;
+
+			int rad = FindCatchmentRadius(st);
+
+			int x_dist = min(w_cur - x_min_prod, x_max_prod - w_cur);
+			if (w_cur < x_min_prod) {
+				x_dist = x_min_prod - w_cur;
+			} else if (w_cur > x_max_prod) {
+				x_dist = w_cur - x_max_prod;
 			}
+
+			if (x_dist > rad) continue;
+
+			int y_dist = min(h_cur - y_min_prod, y_max_prod - h_cur);
+			if (h_cur < y_min_prod) {
+				y_dist = y_min_prod - h_cur;
+			} else if (h_cur > y_max_prod) {
+				y_dist = h_cur - y_max_prod;
+			}
+
+			if (y_dist > rad) continue;
 		}
+
+		/* Insert the station in the set. This will fail if it has
+		 * already been added.
+		 */
+		station_set.insert(st);
+
 	END_TILE_LOOP(cur_tile, w, h, tile - TileDiffXY(max_rad, max_rad))
 
+	return station_set;
+}
+
+uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount)
+{
+	Station *st1 = NULL;	// Station with best rating
+	Station *st2 = NULL;	// Second best station
+	uint best_rating1 = 0;	// rating of st1
+	uint best_rating2 = 0;	// rating of st2
+
+	StationSet all_stations = FindStationsAroundIndustryTile(tile, w, h);
+	for (StationSet::iterator st_iter = all_stations.begin(); st_iter != all_stations.end(); ++st_iter) {
+		Station *st = *st_iter;
+
+		/* Is the station reserved exclusively for somebody else? */
+		if (st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) continue;
+
+		if (st->goods[type].rating == 0) continue; // Lowest possible rating, better not to give cargo anymore
+
+		if (_patches.selectgoods && st->goods[type].last_speed == 0) continue; // Selectively servicing stations, and not this one
+
+		if (IsCargoInClass(type, CC_PASSENGERS)) {
+			if (st->facilities == FACIL_TRUCK_STOP) continue; // passengers are never served by just a truck stop
+		} else {
+			if (st->facilities == FACIL_BUS_STOP) continue; // non-passengers are never served by just a bus stop
+		}
+
+		/* This station can be used, add it to st1/st2 */
+		if (st1 == NULL || st->goods[type].rating >= best_rating1) {
+			st2 = st1; best_rating2 = best_rating1; st1 = st; best_rating1 = st->goods[type].rating;
+		} else if (st2 == NULL || st->goods[type].rating >= best_rating2) {
+			st2 = st; best_rating2 = st->goods[type].rating;
+		}
+	}
+
 	/* no stations around at all? */
-	if (around[0] == NULL) return 0;
-
-	if (around[1] == NULL) {
+	if (st1 == NULL) return 0;
+
+	if (st2 == NULL) {
 		/* only one station around */
-		uint moved = (amount * around[0]->goods[type].rating >> 8) + 1;
-		UpdateStationWaiting(around[0], type, moved);
+		uint moved = amount * best_rating1 / 256 + 1;
+		UpdateStationWaiting(st1, type, moved);
 		return moved;
 	}
 
-	/* several stations around, find the two with the highest rating */
-	Station *st1 = NULL;
-	Station *st2 = NULL;
-	uint best_rating  = 0;
-	uint best_rating2 = 0;
-
-	for (uint i = 0; i != lengthof(around) && around[i] != NULL; i++) {
-		if (around[i]->goods[type].rating >= best_rating) {
-			best_rating2 = best_rating;
-			st2 = st1;
-
-			best_rating = around[i]->goods[type].rating;
-			st1 = around[i];
-		} else if (around[i]->goods[type].rating >= best_rating2) {
-			best_rating2 = around[i]->goods[type].rating;
-			st2 = around[i];
-		}
-	}
-
+	/* several stations around, the best two (highest rating) are in st1 and st2 */
 	assert(st1 != NULL);
 	assert(st2 != NULL);
-	assert(best_rating != 0 || best_rating2 != 0);
+	assert(best_rating1 != 0 || best_rating2 != 0);
 
 	/* the 2nd highest one gets a penalty */
 	best_rating2 >>= 1;
 
 	/* amount given to station 1 */
-	uint t = (best_rating * (amount + 1)) / (best_rating + best_rating2);
+	uint t = (best_rating1 * (amount + 1)) / (best_rating1 + best_rating2);
 
 	uint moved = 0;
 	if (t != 0) {
-		moved = t * best_rating / 256 + 1;
+		moved = t * best_rating1 / 256 + 1;
 		amount -= t;
 		UpdateStationWaiting(st1, type, moved);
 	}