# town_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: town_gui.cpp 15425 2009-02-09 02:09:47Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "town.h"
    00008 #include "viewport_func.h"
    00009 #include "gfx_func.h"
    00010 #include "gui.h"
    00011 #include "window_gui.h"
    00012 #include "textbuf_gui.h"
    00013 #include "command_func.h"
    00014 #include "company_func.h"
    00015 #include "company_base.h"
    00016 #include "company_gui.h"
    00017 #include "network/network.h"
    00018 #include "variables.h"
    00019 #include "strings_func.h"
    00020 #include "sound_func.h"
    00021 #include "economy_func.h"
    00022 #include "tilehighlight_func.h"
    00023 #include "sortlist_type.h"
    00024 #include "road_cmd.h"
    00025
    00026 #include "table/sprites.h"
    00027 #include "table/strings.h"
    00028
    00029 typedef GUIList<const Town*> GUITownList;
    00030
    00031 static const Widget _town_authority_widgets[] = {
    00032 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_BROWN,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},              // TWA_CLOSEBOX
    00033 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_BROWN,    11,   316,     0,    13, STR_2022_LOCAL_AUTHORITY, STR_018C_WINDOW_TITLE_DRAG_THIS},    // TWA_CAPTION
    00034 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_BROWN,     0,   316,    14,   105, 0x0,                      STR_NULL},                           // TWA_RATING_INFO
    00035 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_BROWN,     0,   304,   106,   157, 0x0,                      STR_2043_LIST_OF_THINGS_TO_DO_AT},   // TWA_COMMAND_LIST
    00036 {  WWT_SCROLLBAR,   RESIZE_NONE,  COLOUR_BROWN,   305,   316,   106,   157, 0x0,                      STR_0190_SCROLL_BAR_SCROLLS_LIST},   // TWA_SCROLLBAR
    00037 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_BROWN,     0,   316,   158,   209, 0x0,                      STR_NULL},                           // TWA_ACTION_INFO
    00038 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_BROWN,     0,   316,   210,   221, STR_2042_DO_IT,           STR_2044_CARRY_OUT_THE_HIGHLIGHTED}, // TWA_EXECUTE
    00039 {   WIDGETS_END},
    00040 };
    00041
    00042 extern const byte _town_action_costs[8];
    00043
    00044 struct TownAuthorityWindow : Window {
    00045 private:
    00046   Town *town;
    00047   int sel_index;
    00048
    00049   enum TownAuthorityWidget {
    00050     TWA_CLOSEBOX = 0,
    00051     TWA_CAPTION,
    00052     TWA_RATING_INFO,
    00053     TWA_COMMAND_LIST,
    00054     TWA_SCROLLBAR,
    00055     TWA_ACTION_INFO,
    00056     TWA_EXECUTE,
    00057   };
    00058
    00068   static int GetNthSetBit(uint32 bits, int n)
    00069   {
    00070     if (n >= 0) {
    00071       uint i;
    00072       FOR_EACH_SET_BIT(i, bits) {
    00073         n--;
    00074         if (n < 0) return i;
    00075       }
    00076     }
    00077     return -1;
    00078   }
    00079
    00080 public:
    00081   TownAuthorityWindow(const WindowDesc *desc, WindowNumber window_number) :
    00082       Window(desc, window_number), sel_index(-1)
    00083   {
    00084     this->town = GetTown(this->window_number);
    00085     this->vscroll.cap = 5;
    00086
    00087     this->FindWindowPlacementAndResize(desc);
    00088   }
    00089
    00090   virtual void OnPaint()
    00091   {
    00092     int numact;
    00093     uint buttons = GetMaskOfTownActions(&numact, _local_company, this->town);
    00094
    00095     SetVScrollCount(this, numact + 1);
    00096
    00097     if (this->sel_index != -1 && !HasBit(buttons, this->sel_index)) {
    00098       this->sel_index = -1;
    00099     }
    00100
    00101     this->SetWidgetDisabledState(6, this->sel_index == -1);
    00102
    00103     SetDParam(0, this->window_number);
    00104     this->DrawWidgets();
    00105
    00106     int y = this->widget[TWA_RATING_INFO].top + 1;
    00107
    00108     DrawString(2, y, STR_2023_TRANSPORT_COMPANY_RATINGS, TC_FROMSTRING);
    00109     y += 10;
    00110
    00111     /* Draw list of companies */
    00112     const Company *c;
    00113     FOR_ALL_COMPANIES(c) {
    00114       if ((HasBit(this->town->have_ratings, c->index) || this->town->exclusivity == c->index)) {
    00115         DrawCompanyIcon(c->index, 2, y);
    00116
    00117         SetDParam(0, c->index);
    00118         SetDParam(1, c->index);
    00119
    00120         int r = this->town->ratings[c->index];
    00121         StringID str;
    00122         (str = STR_3035_APPALLING, r <= RATING_APPALLING) || // Apalling
    00123         (str++,                    r <= RATING_VERYPOOR)  || // Very Poor
    00124         (str++,                    r <= RATING_POOR)      || // Poor
    00125         (str++,                    r <= RATING_MEDIOCRE)  || // Mediocore
    00126         (str++,                    r <= RATING_GOOD)      || // Good
    00127         (str++,                    r <= RATING_VERYGOOD)  || // Very Good
    00128         (str++,                    r <= RATING_EXCELLENT) || // Excellent
    00129         (str++,                    true);                    // Outstanding
    00130
    00131         SetDParam(2, str);
    00132         if (this->town->exclusivity == c->index) { // red icon for company with exclusive rights
    00133           DrawSprite(SPR_BLOT, PALETTE_TO_RED, 18, y);
    00134         }
    00135
    00136         DrawString(28, y, STR_2024, TC_FROMSTRING);
    00137         y += 10;
    00138       }
    00139     }
    00140
    00141     if (y > this->widget[TWA_RATING_INFO].bottom) {
    00142       /* If the company list is too big to fit, mark ourself dirty and draw again. */
    00143       ResizeWindowForWidget(this, TWA_RATING_INFO, 0, y - this->widget[TWA_RATING_INFO].bottom);
    00144       this->SetDirty();
    00145       return;
    00146     }
    00147
    00148     y = this->widget[TWA_COMMAND_LIST].top + 1;
    00149     int pos = this->vscroll.pos;
    00150
    00151     if (--pos < 0) {
    00152       DrawString(2, y, STR_2045_ACTIONS_AVAILABLE, TC_FROMSTRING);
    00153       y += 10;
    00154     }
    00155
    00156     for (int i = 0; buttons; i++, buttons >>= 1) {
    00157       if (pos <= -5) break;
    00158
    00159       if ((buttons & 1) && --pos < 0) {
    00160         DrawString(3, y, STR_2046_SMALL_ADVERTISING_CAMPAIGN + i, TC_ORANGE);
    00161         y += 10;
    00162       }
    00163     }
    00164
    00165     if (this->sel_index != -1) {
    00166       SetDParam(1, (_price.build_industry >> 8) * _town_action_costs[this->sel_index]);
    00167       SetDParam(0, STR_2046_SMALL_ADVERTISING_CAMPAIGN + this->sel_index);
    00168       DrawStringMultiLine(2, this->widget[TWA_ACTION_INFO].top + 1, STR_204D_INITIATE_A_SMALL_LOCAL + this->sel_index, 313);
    00169     }
    00170   }
    00171
    00172   virtual void OnDoubleClick(Point pt, int widget) { HandleClick(pt, widget, true); }
    00173   virtual void OnClick(Point pt, int widget) { HandleClick(pt, widget, false); }
    00174
    00175   void HandleClick(Point pt, int widget, bool double_click)
    00176   {
    00177     switch (widget) {
    00178       case TWA_COMMAND_LIST: {
    00179         int y = (pt.y - this->widget[TWA_COMMAND_LIST].top - 1) / 10;
    00180
    00181         if (!IsInsideMM(y, 0, 5)) return;
    00182
    00183         y = GetNthSetBit(GetMaskOfTownActions(NULL, _local_company, this->town), y + this->vscroll.pos - 1);
    00184         if (y >= 0) {
    00185           this->sel_index = y;
    00186           this->SetDirty();
    00187         }
    00188         /* Fall through to clicking in case we are double-clicked */
    00189         if (!double_click || y < 0) break;
    00190       }
    00191
    00192       case TWA_EXECUTE:
    00193         DoCommandP(this->town->xy, this->window_number, this->sel_index, CMD_DO_TOWN_ACTION | CMD_MSG(STR_00B4_CAN_T_DO_THIS));
    00194         break;
    00195     }
    00196   }
    00197
    00198   virtual void OnHundredthTick()
    00199   {
    00200     this->SetDirty();
    00201   }
    00202 };
    00203
    00204 static const WindowDesc _town_authority_desc = {
    00205   WDP_AUTO, WDP_AUTO, 317, 222, 317, 222,
    00206   WC_TOWN_AUTHORITY, WC_NONE,
    00207   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
    00208   _town_authority_widgets,
    00209 };
    00210
    00211 static void ShowTownAuthorityWindow(uint town)
    00212 {
    00213   AllocateWindowDescFront<TownAuthorityWindow>(&_town_authority_desc, town);
    00214 }
    00215
    00216 struct TownViewWindow : Window {
    00217 private:
    00218   Town *town;
    00219
    00220   enum TownViewWidget {
    00221     TVW_CAPTION = 1,
    00222     TVW_STICKY,
    00223     TVW_VIEWPORTPANEL,
    00224     TVW_INFOPANEL = 5,
    00225     TVW_CENTERVIEW,
    00226     TVW_SHOWAUTORITY,
    00227     TVW_CHANGENAME,
    00228     TVW_EXPAND,
    00229     TVW_DELETE,
    00230   };
    00231
    00232 public:
    00233   TownViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
    00234   {
    00235     this->town = GetTown(this->window_number);
    00236     bool ingame = _game_mode != GM_EDITOR;
    00237
    00238     this->flags4 |= WF_DISABLE_VP_SCROLL;
    00239     InitializeWindowViewport(this, 3, 17, 254, 86, this->town->xy, ZOOM_LVL_TOWN);
    00240
    00241     if (this->town->larger_town) this->widget[TVW_CAPTION].data = STR_CITY;
    00242     this->SetWidgetHiddenState(TVW_DELETE, ingame);  // hide delete button on game mode
    00243     this->SetWidgetHiddenState(TVW_EXPAND, ingame);  // hide expand button on game mode
    00244     this->SetWidgetHiddenState(TVW_SHOWAUTORITY, !ingame); // hide autority button on editor mode
    00245
    00246     if (ingame) {
    00247       /* resize caption bar */
    00248       this->widget[TVW_CAPTION].right = this->widget[TVW_STICKY].left -1;
    00249       /* move the rename from top on scenario to bottom in game */
    00250       this->widget[TVW_CHANGENAME].top = this->widget[TVW_EXPAND].top;
    00251       this->widget[TVW_CHANGENAME].bottom = this->widget[TVW_EXPAND].bottom;
    00252       this->widget[TVW_CHANGENAME].right = this->widget[TVW_STICKY].right;
    00253     }
    00254
    00255     /* Space required for showing noise level information */
    00256     if (_settings_game.economy.station_noise_level) {
    00257       ResizeWindowForWidget(this, TVW_INFOPANEL, 0, 10);
    00258     }
    00259
    00260     this->FindWindowPlacementAndResize(desc);
    00261   }
    00262
    00263   virtual void OnPaint()
    00264   {
    00265     /* disable renaming town in network games if you are not the server */
    00266     this->SetWidgetDisabledState(TVW_CHANGENAME, _networking && !_network_server);
    00267
    00268     SetDParam(0, this->town->index);
    00269     this->DrawWidgets();
    00270
    00271     SetDParam(0, this->town->population);
    00272     SetDParam(1, this->town->num_houses);
    00273     DrawString(2, 107, STR_2006_POPULATION, TC_FROMSTRING);
    00274
    00275     SetDParam(0, this->town->act_pass);
    00276     SetDParam(1, this->town->max_pass);
    00277     DrawString(2, 117, STR_200D_PASSENGERS_LAST_MONTH_MAX, TC_FROMSTRING);
    00278
    00279     SetDParam(0, this->town->act_mail);
    00280     SetDParam(1, this->town->max_mail);
    00281     DrawString(2, 127, STR_200E_MAIL_LAST_MONTH_MAX, TC_FROMSTRING);
    00282
    00283     this->DrawViewport();
    00284
    00285     /* only show the town noise, if the noise option is activated. */
    00286     if (_settings_game.economy.station_noise_level) {
    00287       SetDParam(0, this->town->noise_reached);
    00288       SetDParam(1, this->town->MaxTownNoise());
    00289       DrawString(2, 137, STR_NOISE_IN_TOWN, TC_FROMSTRING);
    00290     }
    00291   }
    00292
    00293   virtual void OnClick(Point pt, int widget)
    00294   {
    00295     switch (widget) {
    00296       case TVW_CENTERVIEW: /* scroll to location */
    00297         if (_ctrl_pressed) {
    00298           ShowExtraViewPortWindow(this->town->xy);
    00299         } else {
    00300           ScrollMainWindowToTile(this->town->xy);
    00301         }
    00302         break;
    00303
    00304       case TVW_SHOWAUTORITY: /* town authority */
    00305         ShowTownAuthorityWindow(this->window_number);
    00306         break;
    00307
    00308       case TVW_CHANGENAME: /* rename */
    00309         SetDParam(0, this->window_number);
    00310         ShowQueryString(STR_TOWN, STR_2007_RENAME_TOWN, MAX_LENGTH_TOWN_NAME_BYTES, MAX_LENGTH_TOWN_NAME_PIXELS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT);
    00311         break;
    00312
    00313       case TVW_EXPAND: /* expand town - only available on Scenario editor */
    00314         ExpandTown(this->town);
    00315         break;
    00316
    00317       case TVW_DELETE: /* delete town - only available on Scenario editor */
    00318         delete this->town;
    00319         break;
    00320     }
    00321   }
    00322
    00323   virtual void OnInvalidateData(int data = 0)
    00324   {
    00325     /* Called when setting station noise have changed, in order to resize the window */
    00326     this->SetDirty(); // refresh display for current size. This will allow to avoid glitches when downgrading
    00327
    00328     if (_settings_game.economy.station_noise_level) { // adjust depending
    00329       if (this->height == 150) { // window is smaller, needs to be bigger
    00330         ResizeWindowForWidget(this, TVW_INFOPANEL, 0, 10);
    00331       }
    00332     } else {
    00333       if (this->height != 150) { // window is bigger, needs to be smaller
    00334         ResizeWindowForWidget(this, TVW_INFOPANEL, 0, -10);
    00335       }
    00336     }
    00337   }
    00338
    00339   virtual void OnQueryTextFinished(char *str)
    00340   {
    00341     if (str == NULL) return;
    00342
    00343     DoCommandP(0, this->window_number, 0, CMD_RENAME_TOWN | CMD_MSG(STR_2008_CAN_T_RENAME_TOWN), NULL, str);
    00344   }
    00345 };
    00346
    00347
    00348 static const Widget _town_view_widgets[] = {
    00349 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_BROWN,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
    00350 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_BROWN,    11,   172,     0,    13, STR_2005,                 STR_018C_WINDOW_TITLE_DRAG_THIS},
    00351 {  WWT_STICKYBOX,   RESIZE_NONE,  COLOUR_BROWN,   248,   259,     0,    13, 0x0,                      STR_STICKY_BUTTON},
    00352 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_BROWN,     0,   259,    14,   105, 0x0,                      STR_NULL},
    00353 {      WWT_INSET,   RESIZE_NONE,  COLOUR_BROWN,     2,   257,    16,   103, 0x0,                      STR_NULL},
    00354 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_BROWN,     0,   259,   106,   137, 0x0,                      STR_NULL},
    00355 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_BROWN,     0,    85,   138,   149, STR_00E4_LOCATION,        STR_200B_CENTER_THE_MAIN_VIEW_ON},
    00356 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_BROWN,    86,   171,   138,   149, STR_2020_LOCAL_AUTHORITY, STR_2021_SHOW_INFORMATION_ON_LOCAL},
    00357 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_BROWN,   172,   247,     0,    13, STR_0130_RENAME,          STR_200C_CHANGE_TOWN_NAME},
    00358 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_BROWN,    86,   171,   138,   149, STR_023C_EXPAND,          STR_023B_INCREASE_SIZE_OF_TOWN},
    00359 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_BROWN,   172,   259,   138,   149, STR_0290_DELETE,          STR_0291_DELETE_THIS_TOWN_COMPLETELY},
    00360 {   WIDGETS_END},
    00361 };
    00362
    00363 static const WindowDesc _town_view_desc = {
    00364   WDP_AUTO, WDP_AUTO, 260, 150, 260, 150,
    00365   WC_TOWN_VIEW, WC_NONE,
    00366   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
    00367   _town_view_widgets,
    00368 };
    00369
    00370 void ShowTownViewWindow(TownID town)
    00371 {
    00372   AllocateWindowDescFront<TownViewWindow>(&_town_view_desc, town);
    00373 }
    00374
    00375 static const Widget _town_directory_widgets[] = {
    00376 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_BROWN,     0,    10,     0,    13, STR_00C5,               STR_018B_CLOSE_WINDOW},
    00377 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_BROWN,    11,   195,     0,    13, STR_2000_TOWNS,         STR_018C_WINDOW_TITLE_DRAG_THIS},
    00378 {  WWT_STICKYBOX,   RESIZE_NONE,  COLOUR_BROWN,   196,   207,     0,    13, 0x0,                    STR_STICKY_BUTTON},
    00379 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_BROWN,     0,    98,    14,    25, STR_SORT_BY_NAME,       STR_SORT_ORDER_TIP},
    00380 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_BROWN,    99,   195,    14,    25, STR_SORT_BY_POPULATION, STR_SORT_ORDER_TIP},
    00381 {      WWT_PANEL, RESIZE_BOTTOM,  COLOUR_BROWN,     0,   195,    26,   189, 0x0,                    STR_200A_TOWN_NAMES_CLICK_ON_NAME},
    00382 {  WWT_SCROLLBAR, RESIZE_BOTTOM,  COLOUR_BROWN,   196,   207,    14,   189, 0x0,                    STR_0190_SCROLL_BAR_SCROLLS_LIST},
    00383 {      WWT_PANEL,     RESIZE_TB,  COLOUR_BROWN,     0,   195,   190,   201, 0x0,                    STR_NULL},
    00384 {  WWT_RESIZEBOX,     RESIZE_TB,  COLOUR_BROWN,   196,   207,   190,   201, 0x0,                    STR_RESIZE_BUTTON},
    00385 {   WIDGETS_END},
    00386 };
    00387
    00388
    00389 struct TownDirectoryWindow : public Window {
    00390 private:
    00391   enum TownDirectoryWidget {
    00392     TDW_SORTNAME = 3,
    00393     TDW_SORTPOPULATION,
    00394     TDW_CENTERTOWN,
    00395   };
    00396
    00397   /* Runtime saved values */
    00398   static Listing last_sorting;
    00399   static const Town *last_town;
    00400
    00401   /* Constants for sorting towns */
    00402   static GUITownList::SortFunction * const sorter_funcs[];
    00403
    00404   GUITownList towns;
    00405
    00406   void BuildTownList()
    00407   {
    00408     if (!this->towns.NeedRebuild()) return;
    00409
    00410     this->towns.Clear();
    00411
    00412     const Town *t;
    00413     FOR_ALL_TOWNS(t) {
    00414       *this->towns.Append() = t;
    00415     }
    00416
    00417     this->towns.Compact();
    00418     this->towns.RebuildDone();
    00419   }
    00420
    00421   void SortTownList()
    00422   {
    00423     last_town = NULL;
    00424     this->towns.Sort();
    00425   }
    00426
    00428   static int CDECL TownNameSorter(const Town * const *a, const Town * const *b)
    00429   {
    00430     static char buf_cache[64];
    00431     const Town *ta = *a;
    00432     const Town *tb = *b;
    00433     char buf[64];
    00434
    00435     SetDParam(0, ta->index);
    00436     GetString(buf, STR_TOWN, lastof(buf));
    00437
    00438     /* If 'b' is the same town as in the last round, use the cached value
    00439      * We do this to speed stuff up ('b' is called with the same value a lot of
    00440      * times after eachother) */
    00441     if (tb != last_town) {
    00442       last_town = tb;
    00443       SetDParam(0, tb->index);
    00444       GetString(buf_cache, STR_TOWN, lastof(buf_cache));
    00445     }
    00446
    00447     return strcmp(buf, buf_cache);
    00448   }
    00449
    00451   static int CDECL TownPopulationSorter(const Town * const *a, const Town * const *b)
    00452   {
    00453     return (*a)->population - (*b)->population;
    00454   }
    00455
    00456 public:
    00457   TownDirectoryWindow(const WindowDesc *desc) : Window(desc, 0)
    00458   {
    00459     this->vscroll.cap = 16;
    00460     this->resize.step_height = 10;
    00461     this->resize.height = this->height - 10 * 6; // minimum of 10 items in the list, each item 10 high
    00462
    00463     this->towns.SetListing(this->last_sorting);
    00464     this->towns.SetSortFuncs(this->sorter_funcs);
    00465     this->towns.ForceRebuild();
    00466
    00467     this->FindWindowPlacementAndResize(desc);
    00468   }
    00469
    00470   ~TownDirectoryWindow()
    00471   {
    00472     this->last_sorting = this->towns.GetListing();
    00473   }
    00474
    00475   virtual void OnPaint()
    00476   {
    00477     this->BuildTownList();
    00478     this->SortTownList();
    00479
    00480     SetVScrollCount(this, this->towns.Length());
    00481
    00482     this->DrawWidgets();
    00483     this->DrawSortButtonState(this->towns.SortType() == 0 ? TDW_SORTNAME : TDW_SORTPOPULATION, this->towns.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
    00484
    00485     {
    00486       int n = 0;
    00487       uint16 i = this->vscroll.pos;
    00488       int y = 28;
    00489
    00490       while (i < this->towns.Length()) {
    00491         const Town *t = this->towns[i];
    00492
    00493         assert(t->xy != INVALID_TILE);
    00494
    00495         SetDParam(0, t->index);
    00496         SetDParam(1, t->population);
    00497         DrawString(2, y, STR_2057, TC_FROMSTRING);
    00498
    00499         y += 10;
    00500         i++;
    00501         if (++n == this->vscroll.cap) break; // max number of towns in 1 window
    00502       }
    00503
    00504       SetDParam(0, GetWorldPopulation());
    00505       DrawString(3, this->height - 12 + 2, STR_TOWN_POPULATION, TC_FROMSTRING);
    00506     }
    00507   }
    00508
    00509   virtual void OnClick(Point pt, int widget)
    00510   {
    00511     switch (widget) {
    00512       case TDW_SORTNAME: /* Sort by Name ascending/descending */
    00513         if (this->towns.SortType() == 0) {
    00514           this->towns.ToggleSortOrder();
    00515         } else {
    00516           this->towns.SetSortType(0);
    00517         }
    00518         this->SetDirty();
    00519         break;
    00520
    00521       case TDW_SORTPOPULATION: /* Sort by Population ascending/descending */
    00522         if (this->towns.SortType() == 1) {
    00523           this->towns.ToggleSortOrder();
    00524         } else {
    00525           this->towns.SetSortType(1);
    00526         }
    00527         this->SetDirty();
    00528         break;
    00529
    00530       case TDW_CENTERTOWN: { /* Click on Town Matrix */
    00531         uint16 id_v = (pt.y - 28) / 10;
    00532
    00533         if (id_v >= this->vscroll.cap) return; // click out of bounds
    00534
    00535         id_v += this->vscroll.pos;
    00536
    00537         if (id_v >= this->towns.Length()) return; // click out of town bounds
    00538
    00539         const Town *t = this->towns[id_v];
    00540         assert(t->xy != INVALID_TILE);
    00541         if (_ctrl_pressed) {
    00542           ShowExtraViewPortWindow(t->xy);
    00543         } else {
    00544           ScrollMainWindowToTile(t->xy);
    00545         }
    00546         break;
    00547       }
    00548     }
    00549   }
    00550
    00551   virtual void OnHundredthTick()
    00552   {
    00553     this->SetDirty();
    00554   }
    00555
    00556   virtual void OnResize(Point new_size, Point delta)
    00557   {
    00558     this->vscroll.cap += delta.y / 10;
    00559   }
    00560
    00561   virtual void OnInvalidateData(int data)
    00562   {
    00563     if (data == 0) {
    00564       this->towns.ForceRebuild();
    00565     } else {
    00566       this->towns.ForceResort();
    00567     }
    00568   }
    00569 };
    00570
    00571 Listing TownDirectoryWindow::last_sorting = {false, 0};
    00572 const Town *TownDirectoryWindow::last_town = NULL;
    00573
    00574 /* Available town directory sorting functions */
    00575 GUITownList::SortFunction * const TownDirectoryWindow::sorter_funcs[] = {
    00576   &TownNameSorter,
    00577   &TownPopulationSorter,
    00578 };
    00579
    00580 static const WindowDesc _town_directory_desc = {
    00581   WDP_AUTO, WDP_AUTO, 208, 202, 208, 202,
    00582   WC_TOWN_DIRECTORY, WC_NONE,
    00583   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    00584   _town_directory_widgets,
    00585 };
    00586
    00587 void ShowTownDirectory()
    00588 {
    00589   if (BringWindowToFrontById(WC_TOWN_DIRECTORY, 0)) return;
    00590   new TownDirectoryWindow(&_town_directory_desc);
    00591 }
    00592
    00593 void CcBuildTown(bool success, TileIndex tile, uint32 p1, uint32 p2)
    00594 {
    00595   if (success) {
    00596     SndPlayTileFx(SND_1F_SPLAT, tile);
    00597     if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
    00598   }
    00599 }
    00600
    00601 static const Widget _scen_edit_town_gen_widgets[] = {
    00602 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_DARK_GREEN,    0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
    00603 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_DARK_GREEN,   11,   147,     0,    13, STR_0233_TOWN_GENERATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
    00604 {  WWT_STICKYBOX,   RESIZE_NONE,  COLOUR_DARK_GREEN,  148,   159,     0,    13, 0x0,                      STR_STICKY_BUTTON},
    00605 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_DARK_GREEN,    0,   159,    14,   161, 0x0,                      STR_NULL},
    00606
    00607 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,          2,   157,    16,    27, STR_0234_NEW_TOWN,        STR_0235_CONSTRUCT_NEW_TOWN},
    00608 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,          2,   157,    29,    40, STR_023D_RANDOM_TOWN,     STR_023E_BUILD_TOWN_IN_RANDOM_LOCATION},
    00609 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,          2,   157,    42,    53, STR_MANY_RANDOM_TOWNS,    STR_RANDOM_TOWNS_TIP},
    00610
    00611 {      WWT_LABEL,   RESIZE_NONE,  COLOUR_DARK_GREEN,    0,   147,    54,    67, STR_02A5_TOWN_SIZE,          STR_NULL},
    00612 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,          2,    79,    68,    79, STR_02A1_SMALL,              STR_02A4_SELECT_TOWN_SIZE},
    00613 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,         80,   157,    68,    79, STR_02A2_MEDIUM,             STR_02A4_SELECT_TOWN_SIZE},
    00614 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,          2,    79,    81,    92, STR_02A3_LARGE,              STR_02A4_SELECT_TOWN_SIZE},
    00615 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,         80,   157,    81,    92, STR_SELECT_TOWN_SIZE_RANDOM, STR_02A4_SELECT_TOWN_SIZE},
    00616 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,          2,   157,    96,   107, STR_SCENARIO_EDITOR_CITY,    STR_SCENARIO_EDITOR_CITY_TOOLTIP},
    00617
    00618 {      WWT_LABEL,   RESIZE_NONE,  COLOUR_DARK_GREEN,    0,   147,   108,   121, STR_TOWN_ROAD_LAYOUT,           STR_NULL},
    00619 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,          2,    79,   122,   133, STR_SELECT_LAYOUT_ORIGINAL,     STR_SELECT_TOWN_ROAD_LAYOUT},
    00620 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,         80,   157,   122,   133, STR_SELECT_LAYOUT_BETTER_ROADS, STR_SELECT_TOWN_ROAD_LAYOUT},
    00621 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,          2,    79,   135,   146, STR_SELECT_LAYOUT_2X2_GRID,     STR_SELECT_TOWN_ROAD_LAYOUT},
    00622 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,         80,   157,   135,   146, STR_SELECT_LAYOUT_3X3_GRID,     STR_SELECT_TOWN_ROAD_LAYOUT},
    00623 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,          2,   157,   148,   159, STR_SELECT_LAYOUT_RANDOM,       STR_SELECT_TOWN_ROAD_LAYOUT},
    00624
    00625 {   WIDGETS_END},
    00626 };
    00627
    00628 struct ScenarioEditorTownGenerationWindow : Window
    00629 {
    00630 private:
    00631   enum TownScenarioEditorWidget {
    00632     TSEW_NEWTOWN = 4,
    00633     TSEW_RANDOMTOWN,
    00634     TSEW_MANYRANDOMTOWNS,
    00635     TSEW_TOWNSIZE,
    00636     TSEW_SIZE_SMALL,
    00637     TSEW_SIZE_MEDIUM,
    00638     TSEW_SIZE_LARGE,
    00639     TSEW_SIZE_RANDOM,
    00640     TSEW_CITY,
    00641     TSEW_TOWNLAYOUT,
    00642     TSEW_LAYOUT_ORIGINAL,
    00643     TSEW_LAYOUT_BETTER,
    00644     TSEW_LAYOUT_GRID2,
    00645     TSEW_LAYOUT_GRID3,
    00646     TSEW_LAYOUT_RANDOM,
    00647   };
    00648
    00649   static TownSize town_size;
    00650   static bool city;
    00651   static TownLayout town_layout;
    00652
    00653 public:
    00654   ScenarioEditorTownGenerationWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
    00655   {
    00656     this->FindWindowPlacementAndResize(desc);
    00657     town_layout = _settings_game.economy.town_layout;
    00658     city = false;
    00659     this->UpdateButtons();
    00660   }
    00661
    00662   void UpdateButtons()
    00663   {
    00664     for (int i = TSEW_SIZE_SMALL; i <= TSEW_SIZE_RANDOM; i++) {
    00665       this->SetWidgetLoweredState(i, i == TSEW_SIZE_SMALL + town_size);
    00666     }
    00667
    00668     this->SetWidgetLoweredState(TSEW_CITY, city);
    00669
    00670     for (int i = TSEW_LAYOUT_ORIGINAL; i <= TSEW_LAYOUT_RANDOM; i++) {
    00671       this->SetWidgetLoweredState(i, i == TSEW_LAYOUT_ORIGINAL + town_layout);
    00672     }
    00673
    00674     this->SetDirty();
    00675   }
    00676
    00677   virtual void OnPaint()
    00678   {
    00679     this->DrawWidgets();
    00680   }
    00681
    00682   virtual void OnClick(Point pt, int widget)
    00683   {
    00684     switch (widget) {
    00685       case TSEW_NEWTOWN:
    00686         HandlePlacePushButton(this, TSEW_NEWTOWN, SPR_CURSOR_TOWN, VHM_RECT, PlaceProc_Town);
    00687         break;
    00688
    00689       case TSEW_RANDOMTOWN: {
    00690         this->HandleButtonClick(TSEW_RANDOMTOWN);
    00691         _generating_world = true;
    00692         UpdateNearestTownForRoadTiles(true);
    00693         const Town *t = CreateRandomTown(20, town_size, city, town_layout);
    00694         UpdateNearestTownForRoadTiles(false);
    00695         _generating_world = false;
    00696
    00697         if (t == NULL) {
    00698           ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0);
    00699         } else {
    00700           ScrollMainWindowToTile(t->xy);
    00701         }
    00702       } break;
    00703
    00704       case TSEW_MANYRANDOMTOWNS:
    00705         this->HandleButtonClick(TSEW_MANYRANDOMTOWNS);
    00706
    00707         _generating_world = true;
    00708         UpdateNearestTownForRoadTiles(true);
    00709         if (!GenerateTowns(town_layout)) {
    00710           ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0);
    00711         }
    00712         UpdateNearestTownForRoadTiles(false);
    00713         _generating_world = false;
    00714         break;
    00715
    00716       case TSEW_SIZE_SMALL: case TSEW_SIZE_MEDIUM: case TSEW_SIZE_LARGE: case TSEW_SIZE_RANDOM:
    00717         town_size = (TownSize)(widget - TSEW_SIZE_SMALL);
    00718         this->UpdateButtons();
    00719         break;
    00720
    00721       case TSEW_CITY:
    00722         city ^= true;
    00723         this->SetWidgetLoweredState(TSEW_CITY, city);
    00724         this->SetDirty();
    00725         break;
    00726
    00727       case TSEW_LAYOUT_ORIGINAL: case TSEW_LAYOUT_BETTER: case TSEW_LAYOUT_GRID2:
    00728       case TSEW_LAYOUT_GRID3: case TSEW_LAYOUT_RANDOM:
    00729         town_layout = (TownLayout)(widget - TSEW_LAYOUT_ORIGINAL);
    00730         this->UpdateButtons();
    00731         break;
    00732     }
    00733   }
    00734
    00735   virtual void OnTimeout()
    00736   {
    00737     this->RaiseWidget(TSEW_RANDOMTOWN);
    00738     this->RaiseWidget(TSEW_MANYRANDOMTOWNS);
    00739     this->SetDirty();
    00740   }
    00741
    00742   virtual void OnPlaceObject(Point pt, TileIndex tile)
    00743   {
    00744     _place_proc(tile);
    00745   }
    00746
    00747   virtual void OnPlaceObjectAbort()
    00748   {
    00749     this->RaiseButtons();
    00750     this->UpdateButtons();
    00751   }
    00752
    00753   static void PlaceProc_Town(TileIndex tile)
    00754   {
    00755     DoCommandP(tile, town_size | city << 2 | town_layout << 3, 0, CMD_BUILD_TOWN | CMD_MSG(STR_0236_CAN_T_BUILD_TOWN_HERE), CcBuildTown);
    00756   }
    00757 };
    00758
    00759 TownSize ScenarioEditorTownGenerationWindow::town_size = TS_MEDIUM; // select medium-sized towns per default
    00760 bool ScenarioEditorTownGenerationWindow::city;
    00761 TownLayout ScenarioEditorTownGenerationWindow::town_layout;
    00762
    00763 static const WindowDesc _scen_edit_town_gen_desc = {
    00764   WDP_AUTO, WDP_AUTO, 160, 162, 160, 162,
    00765   WC_SCEN_TOWN_GEN, WC_NONE,
    00766   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
    00767   _scen_edit_town_gen_widgets,
    00768 };
    00769
    00770 void ShowBuildTownWindow()
    00771 {
    00772   if (_game_mode != GM_EDITOR && !IsValidCompanyID(_local_company)) return;
    00773   AllocateWindowDescFront<ScenarioEditorTownGenerationWindow>(&_scen_edit_town_gen_desc, 0);
    00774 }