# HG changeset patch # User rubidium # Date 2007-11-15 22:20:33 # Node ID 0df809bba6c9a4c7251e817130a6226529a61c2c # Parent f5f2dd2eafeddc6639ebefc8ec4b6893a6d1b6f6 (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. diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -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 @@ static bool CheckIndustryCloseDownProtec 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 @@ static void ChangeIndustryProduction(Ind 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 @@ static void ChangeIndustryProduction(Ind /* 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 @@ static void ChangeIndustryProduction(Ind } 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 @@ static void ChangeIndustryProduction(Ind } 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 @@ static CommandCost TerraformTile_Industr 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; } } diff --git a/src/lang/english.txt b/src/lang/english.txt --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -594,6 +594,9 @@ STR_0208_ACCIDENTS_DISASTERS 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 diff --git a/src/news.h b/src/news.h --- a/src/news.h +++ b/src/news.h @@ -53,6 +53,9 @@ enum NewsType { 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 diff --git a/src/news_gui.cpp b/src/news_gui.cpp --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -341,6 +341,9 @@ static const byte _news_items_age[NT_END 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 WindowDesc _news_type0_desc = { }; 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 @@ const char *_news_display_name[NT_END] = "company_info", "openclose", "economy", + "production_player", + "production_other", + "production_nobody", "advice", "new_vehicles", "acceptance", @@ -771,6 +780,7 @@ void ShowMessageHistory() } +/** 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 @@ static void MessageOptionsWndProc(Window } } + +/* +* 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}, +{ WWT_LABEL, RESIZE_NONE, COLOUR_BROWN, + 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}, +/* 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}, -/* 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_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, 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_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, 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}, +/* 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, 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}, +{ 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}, -{ 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_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_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}, - -{ 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}, +/* 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, diff --git a/src/station.h b/src/station.h --- a/src/station.h +++ b/src/station.h @@ -14,6 +14,7 @@ #include "newgrf_station.h" #include "cargopacket.h" #include +#include struct Station; struct RoadStop; @@ -241,6 +242,11 @@ enum CatchmentArea { void ModifyStationRatingAround(TileIndex tile, PlayerID owner, int amount, uint radius); +/** A set of stations (\c const \c Station* ) */ +typedef std::set StationSet; + +StationSet FindStationsAroundIndustryTile(TileIndex tile, int w, int h); + void ShowStationViewWindow(StationID station); void UpdateAllStationVirtCoord(); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -433,7 +433,14 @@ static void ShowRejectOrAcceptNews(const 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 @@ void GetProductionAroundTiles(AcceptedCa } } -/** 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 @@ CommandCost CmdRenameStation(TileIndex t 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 @@ uint MoveGoodsToStation(TileIndex tile, 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); }