town_gui.cpp

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

Generated on Mon Mar 9 23:33:52 2009 for openttd by  doxygen 1.5.6