settings_gui.cpp

Go to the documentation of this file.
00001 /* $Id: settings_gui.cpp 13199 2008-05-20 20:03:45Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "currency.h"
00008 #include "gui.h"
00009 #include "window_gui.h"
00010 #include "textbuf_gui.h"
00011 #include "command_func.h"
00012 #include "engine.h"
00013 #include "screenshot.h"
00014 #include "newgrf.h"
00015 #include "network/network.h"
00016 #include "town.h"
00017 #include "variables.h"
00018 #include "settings_internal.h"
00019 #include "newgrf_townname.h"
00020 #include "strings_func.h"
00021 #include "functions.h"
00022 #include "window_func.h"
00023 #include "vehicle_base.h"
00024 #include "core/alloc_func.hpp"
00025 #include "string_func.h"
00026 #include "gfx_func.h"
00027 #include "waypoint.h"
00028 #include "widgets/dropdown_type.h"
00029 #include "widgets/dropdown_func.h"
00030 
00031 #include "table/sprites.h"
00032 #include "table/strings.h"
00033 
00034 static const StringID _units_dropdown[] = {
00035   STR_UNITS_IMPERIAL,
00036   STR_UNITS_METRIC,
00037   STR_UNITS_SI,
00038   INVALID_STRING_ID
00039 };
00040 
00041 static const StringID _driveside_dropdown[] = {
00042   STR_02E9_DRIVE_ON_LEFT,
00043   STR_02EA_DRIVE_ON_RIGHT,
00044   INVALID_STRING_ID
00045 };
00046 
00047 static const StringID _autosave_dropdown[] = {
00048   STR_02F7_OFF,
00049   STR_AUTOSAVE_1_MONTH,
00050   STR_02F8_EVERY_3_MONTHS,
00051   STR_02F9_EVERY_6_MONTHS,
00052   STR_02FA_EVERY_12_MONTHS,
00053   INVALID_STRING_ID,
00054 };
00055 
00056 static const StringID _designnames_dropdown[] = {
00057   STR_02BE_DEFAULT,
00058   STR_02BF_CUSTOM,
00059   INVALID_STRING_ID
00060 };
00061 
00062 static StringID *BuildDynamicDropdown(StringID base, int num)
00063 {
00064   static StringID buf[32 + 1];
00065   StringID *p = buf;
00066   while (--num>=0) *p++ = base++;
00067   *p = INVALID_STRING_ID;
00068   return buf;
00069 }
00070 
00071 int _nb_orig_names = SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1;
00072 static StringID *_grf_names = NULL;
00073 static int _nb_grf_names = 0;
00074 
00075 void InitGRFTownGeneratorNames()
00076 {
00077   free(_grf_names);
00078   _grf_names = GetGRFTownNameList();
00079   _nb_grf_names = 0;
00080   for (StringID *s = _grf_names; *s != INVALID_STRING_ID; s++) _nb_grf_names++;
00081 }
00082 
00083 static inline StringID TownName(int town_name)
00084 {
00085   if (town_name < _nb_orig_names) return STR_TOWNNAME_ORIGINAL_ENGLISH + town_name;
00086   town_name -= _nb_orig_names;
00087   if (town_name < _nb_grf_names) return _grf_names[town_name];
00088   return STR_UNDEFINED;
00089 }
00090 
00091 static int GetCurRes()
00092 {
00093   int i;
00094 
00095   for (i = 0; i != _num_resolutions; i++) {
00096     if (_resolutions[i][0] == _screen.width &&
00097         _resolutions[i][1] == _screen.height) {
00098       break;
00099     }
00100   }
00101   return i;
00102 }
00103 
00104 static inline bool RoadVehiclesAreBuilt()
00105 {
00106   const Vehicle* v;
00107 
00108   FOR_ALL_VEHICLES(v) {
00109     if (v->type == VEH_ROAD) return true;
00110   }
00111   return false;
00112 }
00113 
00114 
00115 enum GameOptionsWidgets {
00116   GAMEOPT_CURRENCY_BTN    =  4,
00117   GAMEOPT_DISTANCE_BTN    =  6,
00118   GAMEOPT_ROADSIDE_BTN    =  8,
00119   GAMEOPT_TOWNNAME_BTN    = 10,
00120   GAMEOPT_AUTOSAVE_BTN    = 12,
00121   GAMEOPT_VEHICLENAME_BTN = 14,
00122   GAMEOPT_VEHICLENAME_SAVE,
00123   GAMEOPT_LANG_BTN        = 17,
00124   GAMEOPT_RESOLUTION_BTN  = 19,
00125   GAMEOPT_FULLSCREEN,
00126   GAMEOPT_SCREENSHOT_BTN  = 22,
00127 };
00128 
00134 static void ShowTownnameDropdown(Window *w, int sel)
00135 {
00136   typedef std::map<StringID, int, StringIDCompare> TownList;
00137   TownList townnames;
00138 
00139   /* Add and sort original townnames generators */
00140   for (int i = 0; i < _nb_orig_names; i++) townnames[STR_TOWNNAME_ORIGINAL_ENGLISH + i] = i;
00141 
00142   /* Add and sort newgrf townnames generators */
00143   for (int i = 0; i < _nb_grf_names; i++) townnames[_grf_names[i]] = _nb_orig_names + i;
00144 
00145   DropDownList *list = new DropDownList();
00146   for (TownList::iterator it = townnames.begin(); it != townnames.end(); it++) {
00147     list->push_back(new DropDownListStringItem((*it).first, (*it).second, !(_game_mode == GM_MENU || (*it).second == sel)));
00148   }
00149 
00150   ShowDropDownList(w, list, sel, GAMEOPT_TOWNNAME_BTN);
00151 }
00152 
00157 static void ShowLangDropdown(Window *w)
00158 {
00159   typedef std::map<StringID, int, StringIDCompare> LangList;
00160 
00161   /* Sort language names */
00162   LangList langs;
00163   for (int i = 0; i < _dynlang.num; i++) langs[SPECSTR_LANGUAGE_START + i] = i;
00164 
00165   DropDownList *list = new DropDownList();
00166   for (LangList::iterator it = langs.begin(); it != langs.end(); it++) {
00167     list->push_back(new DropDownListStringItem((*it).first, (*it).second, false));
00168   }
00169 
00170   ShowDropDownList(w, list, _dynlang.curr, GAMEOPT_LANG_BTN);
00171 }
00172 
00173 static void ShowCustCurrency();
00174 
00175 static void GameOptionsWndProc(Window *w, WindowEvent *e)
00176 {
00177   switch (e->event) {
00178     case WE_PAINT: {
00179       int i;
00180       StringID str = STR_02BE_DEFAULT;
00181 
00182       w->SetWidgetDisabledState(GAMEOPT_VEHICLENAME_SAVE, !(_vehicle_design_names & 1));
00183       if (!w->IsWidgetDisabled(GAMEOPT_VEHICLENAME_SAVE)) str = STR_02BF_CUSTOM;
00184       SetDParam(0, str);
00185       SetDParam(1, _currency_specs[_opt_ptr->currency].name);
00186       SetDParam(2, STR_UNITS_IMPERIAL + _opt_ptr->units);
00187       SetDParam(3, STR_02E9_DRIVE_ON_LEFT + _opt_ptr->road_side);
00188       SetDParam(4, TownName(_opt_ptr->town_name));
00189       SetDParam(5, _autosave_dropdown[_opt_ptr->autosave]);
00190       SetDParam(6, SPECSTR_LANGUAGE_START + _dynlang.curr);
00191       i = GetCurRes();
00192       SetDParam(7, i == _num_resolutions ? STR_RES_OTHER : SPECSTR_RESOLUTION_START + i);
00193       SetDParam(8, SPECSTR_SCREENSHOT_START + _cur_screenshot_format);
00194       w->SetWidgetLoweredState(GAMEOPT_FULLSCREEN, _fullscreen);
00195 
00196       DrawWindowWidgets(w);
00197       DrawString(20, 175, STR_OPTIONS_FULLSCREEN, TC_FROMSTRING); // fullscreen
00198     } break;
00199 
00200     case WE_CLICK:
00201       switch (e->we.click.widget) {
00202         case GAMEOPT_CURRENCY_BTN: /* Setup currencies dropdown */
00203           ShowDropDownMenu(w, BuildCurrencyDropdown(), _opt_ptr->currency, GAMEOPT_CURRENCY_BTN, _game_mode == GM_MENU ? 0 : ~GetMaskOfAllowedCurrencies(), 0);
00204           break;
00205 
00206         case GAMEOPT_DISTANCE_BTN: /* Setup distance unit dropdown */
00207           ShowDropDownMenu(w, _units_dropdown, _opt_ptr->units, GAMEOPT_DISTANCE_BTN, 0, 0);
00208           break;
00209 
00210         case GAMEOPT_ROADSIDE_BTN: { /* Setup road-side dropdown */
00211           int i = 0;
00212 
00213           /* You can only change the drive side if you are in the menu or ingame with
00214            * no vehicles present. In a networking game only the server can change it */
00215           if ((_game_mode != GM_MENU && RoadVehiclesAreBuilt()) || (_networking && !_network_server))
00216             i = (-1) ^ (1 << _opt_ptr->road_side); // disable the other value
00217 
00218           ShowDropDownMenu(w, _driveside_dropdown, _opt_ptr->road_side, GAMEOPT_ROADSIDE_BTN, i, 0);
00219         } break;
00220 
00221         case GAMEOPT_TOWNNAME_BTN: /* Setup townname dropdown */
00222           ShowTownnameDropdown(w, _opt_ptr->town_name);
00223           break;
00224 
00225         case GAMEOPT_AUTOSAVE_BTN: /* Setup autosave dropdown */
00226           ShowDropDownMenu(w, _autosave_dropdown, _opt_ptr->autosave, GAMEOPT_AUTOSAVE_BTN, 0, 0);
00227           break;
00228 
00229         case GAMEOPT_VEHICLENAME_BTN: /* Setup customized vehicle-names dropdown */
00230           ShowDropDownMenu(w, _designnames_dropdown, (_vehicle_design_names & 1) ? 1 : 0, GAMEOPT_VEHICLENAME_BTN, (_vehicle_design_names & 2) ? 0 : 2, 0);
00231           break;
00232 
00233         case GAMEOPT_VEHICLENAME_SAVE: /* Save customized vehicle-names to disk */
00234           break;  // not implemented
00235 
00236         case GAMEOPT_LANG_BTN: /* Setup interface language dropdown */
00237           ShowLangDropdown(w);
00238           break;
00239 
00240         case GAMEOPT_RESOLUTION_BTN: /* Setup resolution dropdown */
00241           ShowDropDownMenu(w, BuildDynamicDropdown(SPECSTR_RESOLUTION_START, _num_resolutions), GetCurRes(), GAMEOPT_RESOLUTION_BTN, 0, 0);
00242           break;
00243 
00244         case GAMEOPT_FULLSCREEN: /* Click fullscreen on/off */
00245           /* try to toggle full-screen on/off */
00246           if (!ToggleFullScreen(!_fullscreen)) {
00247             ShowErrorMessage(INVALID_STRING_ID, STR_FULLSCREEN_FAILED, 0, 0);
00248           }
00249           w->SetWidgetLoweredState(GAMEOPT_FULLSCREEN, _fullscreen);
00250           SetWindowDirty(w);
00251           break;
00252 
00253         case GAMEOPT_SCREENSHOT_BTN: /* Setup screenshot format dropdown */
00254           ShowDropDownMenu(w, BuildDynamicDropdown(SPECSTR_SCREENSHOT_START, _num_screenshot_formats), _cur_screenshot_format, GAMEOPT_SCREENSHOT_BTN, 0, 0);
00255           break;
00256       }
00257       break;
00258 
00259     case WE_DROPDOWN_SELECT:
00260       switch (e->we.dropdown.button) {
00261         case GAMEOPT_VEHICLENAME_BTN: /* Vehicle design names */
00262           if (e->we.dropdown.index == 0) {
00263             DeleteCustomEngineNames();
00264             MarkWholeScreenDirty();
00265           } else if (!(_vehicle_design_names & 1)) {
00266             LoadCustomEngineNames();
00267             MarkWholeScreenDirty();
00268           }
00269           break;
00270 
00271         case GAMEOPT_CURRENCY_BTN: /* Currency */
00272           if (e->we.dropdown.index == CUSTOM_CURRENCY_ID) ShowCustCurrency();
00273           _opt_ptr->currency = e->we.dropdown.index;
00274           MarkWholeScreenDirty();
00275           break;
00276 
00277         case GAMEOPT_DISTANCE_BTN: /* Measuring units */
00278           _opt_ptr->units = e->we.dropdown.index;
00279           MarkWholeScreenDirty();
00280           break;
00281 
00282         case GAMEOPT_ROADSIDE_BTN: /* Road side */
00283           if (_opt_ptr->road_side != e->we.dropdown.index) { // only change if setting changed
00284             DoCommandP(0, e->we.dropdown.index, 0, NULL, CMD_SET_ROAD_DRIVE_SIDE | CMD_MSG(STR_00B4_CAN_T_DO_THIS));
00285             MarkWholeScreenDirty();
00286           }
00287           break;
00288 
00289         case GAMEOPT_TOWNNAME_BTN: /* Town names */
00290           if (_game_mode == GM_MENU) {
00291             _opt_ptr->town_name = e->we.dropdown.index;
00292             InvalidateWindow(WC_GAME_OPTIONS, 0);
00293           }
00294           break;
00295 
00296         case GAMEOPT_AUTOSAVE_BTN: /* Autosave options */
00297           _opt.autosave = _opt_newgame.autosave = e->we.dropdown.index;
00298           SetWindowDirty(w);
00299           break;
00300 
00301         case GAMEOPT_LANG_BTN: /* Change interface language */
00302           ReadLanguagePack(e->we.dropdown.index);
00303           CheckForMissingGlyphsInLoadedLanguagePack();
00304           UpdateAllStationVirtCoord();
00305           UpdateAllWaypointSigns();
00306           MarkWholeScreenDirty();
00307           break;
00308 
00309         case GAMEOPT_RESOLUTION_BTN: /* Change resolution */
00310           if (e->we.dropdown.index < _num_resolutions && ChangeResInGame(_resolutions[e->we.dropdown.index][0],_resolutions[e->we.dropdown.index][1]))
00311             SetWindowDirty(w);
00312           break;
00313 
00314         case GAMEOPT_SCREENSHOT_BTN: /* Change screenshot format */
00315           SetScreenshotFormat(e->we.dropdown.index);
00316           SetWindowDirty(w);
00317           break;
00318       }
00319       break;
00320 
00321     case WE_DESTROY:
00322       DeleteWindowById(WC_CUSTOM_CURRENCY, 0);
00323       break;
00324   }
00325 
00326 }
00327 
00334 CommandCost CmdSetRoadDriveSide(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00335 {
00336   /* Check boundaries and you can only change this if NO vehicles have been built yet,
00337    * except in the intro-menu where of course it's always possible to do so. */
00338   if (p1 > 1 || (_game_mode != GM_MENU && RoadVehiclesAreBuilt())) return CMD_ERROR;
00339 
00340   if (flags & DC_EXEC) {
00341     _opt_ptr->road_side = p1;
00342     InvalidateWindow(WC_GAME_OPTIONS,0);
00343   }
00344   return CommandCost();
00345 }
00346 
00347 static const Widget _game_options_widgets[] = {
00348 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                          STR_018B_CLOSE_WINDOW},
00349 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   369,     0,    13, STR_00B1_GAME_OPTIONS,             STR_018C_WINDOW_TITLE_DRAG_THIS},
00350 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   369,    14,   238, 0x0,                               STR_NULL},
00351 {      WWT_FRAME,   RESIZE_NONE,    14,    10,   179,    20,    55, STR_02E0_CURRENCY_UNITS,           STR_NULL},
00352 { WWT_DROPDOWNIN,   RESIZE_NONE,    14,    20,   169,    34,    45, STR_02E1,                          STR_02E2_CURRENCY_UNITS_SELECTION},
00353 {      WWT_FRAME,   RESIZE_NONE,    14,   190,   359,    20,    55, STR_MEASURING_UNITS,               STR_NULL},
00354 { WWT_DROPDOWNIN,   RESIZE_NONE,    14,   200,   349,    34,    45, STR_02E4,                          STR_MEASURING_UNITS_SELECTION},
00355 {      WWT_FRAME,   RESIZE_NONE,    14,    10,   179,    62,    97, STR_02E6_ROAD_VEHICLES,            STR_NULL},
00356 { WWT_DROPDOWNIN,   RESIZE_NONE,    14,    20,   169,    76,    87, STR_02E7,                          STR_02E8_SELECT_SIDE_OF_ROAD_FOR},
00357 {      WWT_FRAME,   RESIZE_NONE,    14,   190,   359,    62,    97, STR_02EB_TOWN_NAMES,               STR_NULL},
00358 { WWT_DROPDOWNIN,   RESIZE_NONE,    14,   200,   349,    76,    87, STR_02EC,                          STR_02ED_SELECT_STYLE_OF_TOWN_NAMES},
00359 {      WWT_FRAME,   RESIZE_NONE,    14,    10,   179,   104,   139, STR_02F4_AUTOSAVE,                 STR_NULL},
00360 { WWT_DROPDOWNIN,   RESIZE_NONE,    14,    20,   169,   118,   129, STR_02F5,                          STR_02F6_SELECT_INTERVAL_BETWEEN},
00361 
00362 {      WWT_FRAME,   RESIZE_NONE,    14,    10,   359,   194,   228, STR_02BC_VEHICLE_DESIGN_NAMES,     STR_NULL},
00363 { WWT_DROPDOWNIN,   RESIZE_NONE,    14,    20,   119,   207,   218, STR_02BD,                          STR_02C1_VEHICLE_DESIGN_NAMES_SELECTION},
00364 {    WWT_TEXTBTN,   RESIZE_NONE,    14,   130,   349,   207,   218, STR_02C0_SAVE_CUSTOM_NAMES,        STR_02C2_SAVE_CUSTOMIZED_VEHICLE},
00365 
00366 {      WWT_FRAME,   RESIZE_NONE,    14,   190,   359,   104,   139, STR_OPTIONS_LANG,                  STR_NULL},
00367 { WWT_DROPDOWNIN,   RESIZE_NONE,    14,   200,   349,   118,   129, STR_OPTIONS_LANG_CBO,              STR_OPTIONS_LANG_TIP},
00368 
00369 {      WWT_FRAME,   RESIZE_NONE,    14,    10,   179,   146,   190, STR_OPTIONS_RES,                   STR_NULL},
00370 { WWT_DROPDOWNIN,   RESIZE_NONE,    14,    20,   169,   160,   171, STR_OPTIONS_RES_CBO,               STR_OPTIONS_RES_TIP},
00371 {    WWT_TEXTBTN,   RESIZE_NONE,    14,   149,   169,   176,   184, STR_EMPTY,                         STR_OPTIONS_FULLSCREEN_TIP},
00372 
00373 {      WWT_FRAME,   RESIZE_NONE,    14,   190,   359,   146,   190, STR_OPTIONS_SCREENSHOT_FORMAT,     STR_NULL},
00374 { WWT_DROPDOWNIN,   RESIZE_NONE,    14,   200,   349,   160,   171, STR_OPTIONS_SCREENSHOT_FORMAT_CBO, STR_OPTIONS_SCREENSHOT_FORMAT_TIP},
00375 
00376 {   WIDGETS_END},
00377 };
00378 
00379 static const WindowDesc _game_options_desc = {
00380   WDP_CENTER, WDP_CENTER, 370, 239, 370, 239,
00381   WC_GAME_OPTIONS, WC_NONE,
00382   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
00383   _game_options_widgets,
00384   GameOptionsWndProc
00385 };
00386 
00387 
00388 void ShowGameOptions()
00389 {
00390   DeleteWindowById(WC_GAME_OPTIONS, 0);
00391   AllocateWindowDesc(&_game_options_desc);
00392 }
00393 
00394 struct GameSettingData {
00395   int16 min;
00396   int16 max;
00397   int16 step;
00398   StringID str;
00399 };
00400 
00401 static const GameSettingData _game_setting_info[] = {
00402   {  0,   7,  1, STR_NULL},
00403   {  0,   3,  1, STR_6830_IMMEDIATE},
00404   {  0,   3,  1, STR_NUM_VERY_LOW},
00405   {  0,   4,  1, STR_NONE},
00406   {100, 500, 50, STR_NULL},
00407   {  2,   4,  1, STR_NULL},
00408   {  0,   2,  1, STR_6820_LOW},
00409   {  0,   4,  1, STR_681B_VERY_SLOW},
00410   {  0,   2,  1, STR_6820_LOW},
00411   {  0,   2,  1, STR_6823_NONE},
00412   {  0,   3,  1, STR_6826_X1_5},
00413   {  0,   2,  1, STR_6820_LOW},
00414   {  0,   3,  1, STR_682A_VERY_FLAT},
00415   {  0,   3,  1, STR_VERY_LOW},
00416   {  0,   1,  1, STR_682E_STEADY},
00417   {  0,   1,  1, STR_6834_AT_END_OF_LINE_AND_AT_STATIONS},
00418   {  0,   1,  1, STR_6836_OFF},
00419   {  0,   2,  1, STR_PERMISSIVE},
00420 };
00421 
00422 /*
00423  * A: competitors
00424  * B: start time in months / 3
00425  * C: town count (3 = high, 0 = very low)
00426  * D: industry count (4 = high, 0 = none)
00427  * E: inital loan / 1000 (in GBP)
00428  * F: interest rate
00429  * G: running costs (0 = low, 2 = high)
00430  * H: construction speed of competitors (0 = very slow, 4 = very fast)
00431  * I: intelligence (0-2)
00432  * J: breakdowns (0 = off, 2 = normal)
00433  * K: subsidy multiplier (0 = 1.5, 3 = 4.0)
00434  * L: construction cost (0-2)
00435  * M: terrain type (0 = very flat, 3 = mountainous)
00436  * N: amount of water (0 = very low, 3 = high)
00437  * O: economy (0 = steady, 1 = fluctuating)
00438  * P: Train reversing (0 = end of line + stations, 1 = end of line)
00439  * Q: disasters
00440  * R: area restructuring (0 = permissive, 2 = hostile)
00441  */
00442 static const GDType _default_game_diff[3][GAME_DIFFICULTY_NUM] = { /*
00443    A, B, C, D,   E, F, G, H, I, J, K, L, M, N, O, P, Q, R*/
00444   {2, 2, 2, 4, 300, 2, 0, 2, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0}, 
00445   {4, 1, 2, 3, 150, 3, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1}, 
00446   {7, 0, 3, 3, 100, 4, 1, 3, 2, 2, 0, 2, 3, 2, 1, 1, 1, 2}, 
00447 };
00448 
00449 void SetDifficultyLevel(int mode, GameOptions *gm_opt)
00450 {
00451   int i;
00452   assert(mode <= 3);
00453 
00454   gm_opt->diff_level = mode;
00455   if (mode != 3) { // not custom
00456     for (i = 0; i != GAME_DIFFICULTY_NUM; i++)
00457       ((GDType*)&gm_opt->diff)[i] = _default_game_diff[mode][i];
00458   }
00459 }
00460 
00465 void CheckDifficultyLevels()
00466 {
00467   if (_opt_newgame.diff_level != 3) {
00468     SetDifficultyLevel(_opt_newgame.diff_level, &_opt_newgame);
00469   } else {
00470     for (uint i = 0; i < GAME_DIFFICULTY_NUM; i++) {
00471       GDType *diff = ((GDType*)&_opt_newgame.diff) + i;
00472       *diff = Clamp(*diff, _game_setting_info[i].min, _game_setting_info[i].max);
00473       *diff -= *diff % _game_setting_info[i].step;
00474     }
00475   }
00476 }
00477 
00478 extern void StartupEconomy();
00479 
00480 enum {
00481   GAMEDIFF_WND_TOP_OFFSET = 45,
00482   GAMEDIFF_WND_ROWSIZE    = 9
00483 };
00484 
00485 /* Temporary holding place of values in the difficulty window until 'Save' is clicked */
00486 static GameOptions _opt_mod_temp;
00487 // 0x383E = (1 << 13) | (1 << 12) | (1 << 11) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1)
00488 #define DIFF_INGAME_DISABLED_BUTTONS 0x383E
00489 
00490 #define NO_SETTINGS_BUTTON 0xFF
00491 
00493 struct difficulty_d {
00494   bool clicked_increase;
00495   uint8 clicked_button;
00496   uint8 timeout;
00497 };
00498 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(difficulty_d));
00499 
00500 /* Names of the game difficulty settings window */
00501 enum GameDifficultyWidgets {
00502   GDW_CLOSEBOX = 0,
00503   GDW_CAPTION,
00504   GDW_UPPER_BG,
00505   GDW_LVL_EASY,
00506   GDW_LVL_MEDIUM,
00507   GDW_LVL_HARD,
00508   GDW_LVL_CUSTOM,
00509   GDW_HIGHSCORE,
00510   GDW_SETTING_BG,
00511   GDW_LOWER_BG,
00512   GDW_ACCEPT,
00513   GDW_CANCEL,
00514 };
00515 
00516 static void GameDifficultyWndProc(Window *w, WindowEvent *e)
00517 {
00518   difficulty_d *diffic_d = &WP(w, difficulty_d);
00519   switch (e->event) {
00520     case WE_CREATE:
00521       diffic_d->clicked_increase = false;
00522       diffic_d->clicked_button = NO_SETTINGS_BUTTON;
00523       diffic_d->timeout = 0;
00524       /* Hide the closebox to make sure that the user aborts or confirms his changes */
00525       w->HideWidget(GDW_CLOSEBOX);
00526       w->widget[GDW_CAPTION].left = 0;
00527       /* Setup disabled buttons when creating window
00528        * disable all other difficulty buttons during gameplay except for 'custom' */
00529       w->SetWidgetsDisabledState(_game_mode == GM_NORMAL,
00530         GDW_LVL_EASY,
00531         GDW_LVL_MEDIUM,
00532         GDW_LVL_HARD,
00533         GDW_LVL_CUSTOM,
00534         WIDGET_LIST_END);
00535       w->SetWidgetDisabledState(GDW_HIGHSCORE, _game_mode == GM_EDITOR || _networking); // highscore chart in multiplayer
00536       w->SetWidgetDisabledState(GDW_ACCEPT, _networking && !_network_server); // Save-button in multiplayer (and if client)
00537       w->LowerWidget(GDW_LVL_EASY + _opt_mod_temp.diff_level);
00538       break;
00539 
00540     case WE_PAINT: {
00541       DrawWindowWidgets(w);
00542 
00543       /* XXX - Disabled buttons in normal gameplay or during muliplayer as non server.
00544        *       Bitshifted for each button to see if that bit is set. If it is set, the
00545        *       button is disabled */
00546       uint32 disabled = 0;
00547       if (_networking && !_network_server) {
00548         disabled = MAX_UVALUE(uint32); // Disable all
00549       } else if (_game_mode == GM_NORMAL) {
00550         disabled = DIFF_INGAME_DISABLED_BUTTONS;
00551       }
00552 
00553       int value;
00554       int y = GAMEDIFF_WND_TOP_OFFSET;
00555       for (uint i = 0; i != GAME_DIFFICULTY_NUM; i++) {
00556         const GameSettingData *gsd = &_game_setting_info[i];
00557         value = ((GDType*)&_opt_mod_temp.diff)[i];
00558 
00559         DrawArrowButtons(5, y, 3,
00560             (diffic_d->clicked_button == i) ? 1 + !!diffic_d->clicked_increase : 0,
00561             !(HasBit(disabled, i) || gsd->min == value),
00562             !(HasBit(disabled, i) || gsd->max == value));
00563 
00564         value += _game_setting_info[i].str;
00565         if (i == 4) value *= 1000; // XXX - handle currency option
00566         SetDParam(0, value);
00567         DrawString(30, y, STR_6805_MAXIMUM_NO_COMPETITORS + i, TC_FROMSTRING);
00568 
00569         y += GAMEDIFF_WND_ROWSIZE + 2; // space items apart a bit
00570       }
00571     } break;
00572 
00573     case WE_CLICK:
00574       switch (e->we.click.widget) {
00575         case GDW_SETTING_BG: { /* Difficulty settings widget, decode click */
00576           /* Don't allow clients to make any changes */
00577           if (_networking && !_network_server) return;
00578 
00579           const int x = e->we.click.pt.x - 5;
00580           if (!IsInsideMM(x, 0, 21)) // Button area
00581             return;
00582 
00583           const int y = e->we.click.pt.y - GAMEDIFF_WND_TOP_OFFSET;
00584           if (y < 0) return;
00585 
00586           /* Get button from Y coord. */
00587           const uint8 btn = y / (GAMEDIFF_WND_ROWSIZE + 2);
00588           if (btn >= GAME_DIFFICULTY_NUM || y % (GAMEDIFF_WND_ROWSIZE + 2) >= 9)
00589             return;
00590 
00591           /* Clicked disabled button? */
00592           if (_game_mode == GM_NORMAL && HasBit(DIFF_INGAME_DISABLED_BUTTONS, btn))
00593             return;
00594 
00595           diffic_d->timeout = 5;
00596 
00597           int16 val = ((GDType*)&_opt_mod_temp.diff)[btn];
00598 
00599           const GameSettingData *info = &_game_setting_info[btn]; // get information about the difficulty setting
00600           if (x >= 10) {
00601             /* Increase button clicked */
00602             val = min(val + info->step, info->max);
00603             diffic_d->clicked_increase = true;
00604           } else {
00605             /* Decrease button clicked */
00606             val -= info->step;
00607             val = max(val,  info->min);
00608             diffic_d->clicked_increase = false;
00609           }
00610           diffic_d->clicked_button = btn;
00611 
00612           /* save value in temporary variable */
00613           ((GDType*)&_opt_mod_temp.diff)[btn] = val;
00614           w->RaiseWidget(GDW_LVL_EASY + _opt_mod_temp.diff_level);
00615           SetDifficultyLevel(3, &_opt_mod_temp); // set difficulty level to custom
00616           w->LowerWidget(GDW_LVL_CUSTOM);
00617           SetWindowDirty(w);
00618         } break;
00619 
00620         case GDW_LVL_EASY:
00621         case GDW_LVL_MEDIUM:
00622         case GDW_LVL_HARD:
00623         case GDW_LVL_CUSTOM:
00624           /* temporarily change difficulty level */
00625           w->RaiseWidget(GDW_LVL_EASY + _opt_mod_temp.diff_level);
00626           SetDifficultyLevel(e->we.click.widget - GDW_LVL_EASY, &_opt_mod_temp);
00627           w->LowerWidget(GDW_LVL_EASY + _opt_mod_temp.diff_level);
00628           SetWindowDirty(w);
00629           break;
00630 
00631         case GDW_HIGHSCORE: // Highscore Table
00632           ShowHighscoreTable(_opt_mod_temp.diff_level, -1);
00633           break;
00634 
00635         case GDW_ACCEPT: { // Save button - save changes
00636           GDType btn, val;
00637           for (btn = 0; btn != GAME_DIFFICULTY_NUM; btn++) {
00638             val = ((GDType*)&_opt_mod_temp.diff)[btn];
00639             /* if setting has changed, change it */
00640             if (val != ((GDType*)&_opt_ptr->diff)[btn])
00641               DoCommandP(0, btn, val, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
00642           }
00643           DoCommandP(0, UINT_MAX, _opt_mod_temp.diff_level, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
00644           DeleteWindow(w);
00645           /* If we are in the editor, we should reload the economy.
00646            * This way when you load a game, the max loan and interest rate
00647            * are loaded correctly. */
00648           if (_game_mode == GM_EDITOR) StartupEconomy();
00649           break;
00650         }
00651 
00652         case GDW_CANCEL: // Cancel button - close window, abandon changes
00653           DeleteWindow(w);
00654           break;
00655       } break;
00656 
00657     case WE_MOUSELOOP: /* Handle the visual 'clicking' of the buttons */
00658       if (diffic_d->timeout != 0) {
00659         diffic_d->timeout--;
00660         if (diffic_d->timeout == 0) diffic_d->clicked_button = NO_SETTINGS_BUTTON;
00661         SetWindowDirty(w);
00662       }
00663       break;
00664   }
00665 }
00666 #undef DIFF_INGAME_DISABLED_BUTTONS
00667 
00668 /* Widget definition for the game difficulty settings window */
00669 static const Widget _game_difficulty_widgets[] = {
00670 {   WWT_CLOSEBOX,   RESIZE_NONE,    10,     0,    10,     0,    13, STR_00C5,                     STR_018B_CLOSE_WINDOW},           // GDW_CLOSEBOX
00671 {    WWT_CAPTION,   RESIZE_NONE,    10,    11,   369,     0,    13, STR_6800_DIFFICULTY_LEVEL,    STR_018C_WINDOW_TITLE_DRAG_THIS}, // GDW_CAPTION
00672 {      WWT_PANEL,   RESIZE_NONE,    10,     0,   369,    14,    41, 0x0,                          STR_NULL},                        // GDW_UPPER_BG
00673 { WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    10,    96,    16,    27, STR_6801_EASY,                STR_NULL},                        // GDW_LVL_EASY
00674 { WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    97,   183,    16,    27, STR_6802_MEDIUM,              STR_NULL},                        // GDW_LVL_MEDIUM
00675 { WWT_PUSHTXTBTN,   RESIZE_NONE,     3,   184,   270,    16,    27, STR_6803_HARD,                STR_NULL},                        // GDW_LVL_HARD
00676 { WWT_PUSHTXTBTN,   RESIZE_NONE,     3,   271,   357,    16,    27, STR_6804_CUSTOM,              STR_NULL},                        // GDW_LVL_CUSTOM
00677 {    WWT_TEXTBTN,   RESIZE_NONE,     6,    10,   357,    28,    39, STR_6838_SHOW_HI_SCORE_CHART, STR_NULL},                        // GDW_HIGHSCORE
00678 {      WWT_PANEL,   RESIZE_NONE,    10,     0,   369,    42,   262, 0x0,                          STR_NULL},                        // GDW_SETTING_BG
00679 {      WWT_PANEL,   RESIZE_NONE,    10,     0,   369,   263,   278, 0x0,                          STR_NULL},                        // GDW_LOWER_BG
00680 { WWT_PUSHTXTBTN,   RESIZE_NONE,     3,   105,   185,   265,   276, STR_OPTIONS_SAVE_CHANGES,     STR_NULL},                        // GDW_ACCEPT
00681 { WWT_PUSHTXTBTN,   RESIZE_NONE,     3,   186,   266,   265,   276, STR_012E_CANCEL,              STR_NULL},                        // GDW_CANCEL
00682 {   WIDGETS_END},
00683 };
00684 
00685 /* Window definition for the game difficulty settings window */
00686 static const WindowDesc _game_difficulty_desc = {
00687   WDP_CENTER, WDP_CENTER, 370, 279, 370, 279,
00688   WC_GAME_OPTIONS, WC_NONE,
00689   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
00690   _game_difficulty_widgets,
00691   GameDifficultyWndProc
00692 };
00693 
00694 void ShowGameDifficulty()
00695 {
00696   DeleteWindowById(WC_GAME_OPTIONS, 0);
00697   /* Copy current settings (ingame or in intro) to temporary holding place
00698    * change that when setting stuff, copy back on clicking 'OK' */
00699   _opt_mod_temp = *_opt_ptr;
00700   AllocateWindowDesc(&_game_difficulty_desc);
00701 }
00702 
00703 static const char *_patches_ui[] = {
00704   "vehicle_speed",
00705   "status_long_date",
00706   "show_finances",
00707   "autoscroll",
00708   "reverse_scroll",
00709   "smooth_scroll",
00710   "errmsg_duration",
00711   "toolbar_pos",
00712   "measure_tooltip",
00713   "window_snap_radius",
00714   "invisible_trees",
00715   "population_in_label",
00716   "link_terraform_toolbar",
00717   "liveries",
00718   "prefer_teamchat",
00719   /* While the horizontal scrollwheel scrolling is written as general code, only
00720    *  the cocoa (OSX) driver generates input for it.
00721    *  Since it's also able to completely disable the scrollwheel will we display it on all platforms anyway */
00722   "scrollwheel_scrolling",
00723   "scrollwheel_multiplier",
00724 #ifdef __APPLE__
00725   /* We might need to emulate a right mouse button on mac */
00726   "right_mouse_btn_emulation",
00727 #endif
00728   "pause_on_newgame",
00729   "advanced_vehicle_list",
00730   "loading_indicators",
00731   "timetable_in_ticks",
00732   "default_rail_type",
00733   "always_build_infrastructure",
00734 };
00735 
00736 static const char *_patches_construction[] = {
00737   "build_on_slopes",
00738   "autoslope",
00739   "extra_dynamite",
00740   "longbridges",
00741   "signal_side",
00742   "always_small_airport",
00743   "enable_signal_gui",
00744   "drag_signals_density",
00745   "oil_refinery_limit",
00746   "semaphore_build_before",
00747 };
00748 
00749 static const char *_patches_stations[] = {
00750   "join_stations",
00751   "full_load_any",
00752   "improved_load",
00753   "selectgoods",
00754   "new_nonstop",
00755   "nonuniform_stations",
00756   "station_spread",
00757   "serviceathelipad",
00758   "modified_catchment",
00759   "gradual_loading",
00760   "road_stop_on_town_road",
00761   "adjacent_stations",
00762 };
00763 
00764 static const char *_patches_economy[] = {
00765   "inflation",
00766   "raw_industry_construction",
00767   "multiple_industry_per_town",
00768   "same_industry_close",
00769   "bribe",
00770   "exclusive_rights",
00771   "give_money",
00772   "colored_news_year",
00773   "ending_year",
00774   "smooth_economy",
00775   "allow_shares",
00776   "town_layout",
00777   "mod_road_rebuild",
00778   "town_growth_rate",
00779   "larger_towns",
00780   "initial_city_size",
00781 };
00782 
00783 static const char *_patches_ai[] = {
00784   "ainew_active",
00785   "ai_in_multiplayer",
00786   "ai_disable_veh_train",
00787   "ai_disable_veh_roadveh",
00788   "ai_disable_veh_aircraft",
00789   "ai_disable_veh_ship",
00790 };
00791 
00792 static const char *_patches_vehicles[] = {
00793   "realistic_acceleration",
00794   "forbid_90_deg",
00795   "mammoth_trains",
00796   "gotodepot",
00797   "roadveh_queue",
00798   "pathfinder_for_trains",
00799   "pathfinder_for_roadvehs",
00800   "pathfinder_for_ships",
00801   "train_income_warn",
00802   "order_review_system",
00803   "never_expire_vehicles",
00804   "lost_train_warn",
00805   "autorenew",
00806   "autorenew_months",
00807   "autorenew_money",
00808   "max_trains",
00809   "max_roadveh",
00810   "max_aircraft",
00811   "max_ships",
00812   "servint_ispercent",
00813   "servint_trains",
00814   "servint_roadveh",
00815   "servint_ships",
00816   "servint_aircraft",
00817   "no_servicing_if_no_breakdowns",
00818   "wagon_speed_limits",
00819   "disable_elrails",
00820   "freight_trains",
00821   "plane_speed",
00822   "timetabling",
00823 };
00824 
00825 struct PatchEntry {
00826   const SettingDesc *setting;
00827   uint index;
00828 };
00829 
00830 struct PatchPage {
00831   const char **names;
00832   PatchEntry *entries;
00833   byte num;
00834 };
00835 
00836 /* PatchPage holds the categories, the number of elements in each category
00837  * and (in NULL) a dynamic array of settings based on the string-representations
00838  * of the settings. This way there is no worry about indeces, and such */
00839 static PatchPage _patches_page[] = {
00840   {_patches_ui,           NULL, lengthof(_patches_ui)},
00841   {_patches_construction, NULL, lengthof(_patches_construction)},
00842   {_patches_vehicles,     NULL, lengthof(_patches_vehicles)},
00843   {_patches_stations,     NULL, lengthof(_patches_stations)},
00844   {_patches_economy,      NULL, lengthof(_patches_economy)},
00845   {_patches_ai,           NULL, lengthof(_patches_ai)},
00846 };
00847 
00848 enum PatchesSelectionWidgets {
00849   PATCHSEL_OPTIONSPANEL = 3,
00850   PATCHSEL_INTERFACE,
00851   PATCHSEL_CONSTRUCTION,
00852   PATCHSEL_VEHICLES,
00853   PATCHSEL_STATIONS,
00854   PATCHSEL_ECONOMY,
00855   PATCHSEL_COMPETITORS
00856 };
00857 
00861 static void PatchesSelectionWndProc(Window *w, WindowEvent *e)
00862 {
00863   static Patches *patches_ptr;
00864   static int patches_max = 0;
00865 
00866   switch (e->event) {
00867     case WE_CREATE: {
00868       static bool first_time = true;
00869 
00870       patches_ptr = (_game_mode == GM_MENU) ? &_patches_newgame : &_patches;
00871 
00872       /* Build up the dynamic settings-array only once per OpenTTD session */
00873       if (first_time) {
00874         PatchPage *page;
00875         for (page = &_patches_page[0]; page != endof(_patches_page); page++) {
00876           uint i;
00877 
00878           if (patches_max < page->num) patches_max = page->num;
00879 
00880           page->entries = MallocT<PatchEntry>(page->num);
00881           for (i = 0; i != page->num; i++) {
00882             uint index;
00883             const SettingDesc *sd = GetPatchFromName(page->names[i], &index);
00884             assert(sd != NULL);
00885 
00886             page->entries[i].setting = sd;
00887             page->entries[i].index = index;
00888           }
00889         }
00890         first_time = false;
00891       }
00892 
00893       /* Resize the window to fit the largest patch tab */
00894       ResizeWindowForWidget(w, PATCHSEL_OPTIONSPANEL, 0, patches_max * 11);
00895 
00896       /* Recentre the window for the new size */
00897       w->top = w->top - (patches_max * 11) / 2;
00898 
00899       w->LowerWidget(4);
00900     } break;
00901 
00902     case WE_PAINT: {
00903       int x, y;
00904       const PatchPage *page = &_patches_page[WP(w, def_d).data_1];
00905       uint i;
00906 
00907       /* Set up selected category */
00908       DrawWindowWidgets(w);
00909 
00910       x = 5;
00911       y = 47;
00912       for (i = 0; i != page->num; i++) {
00913         const SettingDesc *sd = page->entries[i].setting;
00914         const SettingDescBase *sdb = &sd->desc;
00915         const void *var = GetVariableAddress(patches_ptr, &sd->save);
00916         bool editable = true;
00917         bool disabled = false;
00918 
00919         // We do not allow changes of some items when we are a client in a networkgame
00920         if (!(sd->save.conv & SLF_NETWORK_NO) && _networking && !_network_server) editable = false;
00921         if ((sdb->flags & SGF_NETWORK_ONLY) && !_networking) editable = false;
00922         if ((sdb->flags & SGF_NO_NETWORK) && _networking) editable = false;
00923 
00924         if (sdb->cmd == SDT_BOOLX) {
00925           static const int _bool_ctabs[2][2] = {{9, 4}, {7, 6}};
00926           /* Draw checkbox for boolean-value either on/off */
00927           bool on = (*(bool*)var);
00928 
00929           DrawFrameRect(x, y, x + 19, y + 8, _bool_ctabs[!!on][!!editable], on ? FR_LOWERED : FR_NONE);
00930           SetDParam(0, on ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF);
00931         } else {
00932           int32 value;
00933 
00934           value = (int32)ReadValue(var, sd->save.conv);
00935 
00936           /* Draw [<][>] boxes for settings of an integer-type */
00937           DrawArrowButtons(x, y, 3, WP(w, def_d).data_2 - (i * 2), (editable && value != sdb->min), (editable && value != sdb->max));
00938 
00939           disabled = (value == 0) && (sdb->flags & SGF_0ISDISABLED);
00940           if (disabled) {
00941             SetDParam(0, STR_CONFIG_PATCHES_DISABLED);
00942           } else {
00943             if (sdb->flags & SGF_CURRENCY) {
00944               SetDParam(0, STR_CONFIG_PATCHES_CURRENCY);
00945             } else if (sdb->flags & SGF_MULTISTRING) {
00946               SetDParam(0, sdb->str + value + 1);
00947             } else {
00948               SetDParam(0, (sdb->flags & SGF_NOCOMMA) ? STR_CONFIG_PATCHES_INT32 : STR_7024);
00949             }
00950             SetDParam(1, value);
00951           }
00952         }
00953         DrawString(30, y, (sdb->str) + disabled, TC_FROMSTRING);
00954         y += 11;
00955       }
00956     } break;
00957 
00958     case WE_CLICK:
00959       switch (e->we.click.widget) {
00960         case PATCHSEL_OPTIONSPANEL: {
00961           const PatchPage *page = &_patches_page[WP(w, def_d).data_1];
00962           const SettingDesc *sd;
00963           void *var;
00964           int32 value;
00965           int x, y;
00966           byte btn;
00967 
00968           y = e->we.click.pt.y - 46 - 1;
00969           if (y < 0) return;
00970 
00971           x = e->we.click.pt.x - 5;
00972           if (x < 0) return;
00973 
00974           btn = y / 11;
00975           if (y % 11 > 9) return;
00976           if (btn >= page->num) return;
00977 
00978           sd = page->entries[btn].setting;
00979 
00980           /* return if action is only active in network, or only settable by server */
00981           if (!(sd->save.conv & SLF_NETWORK_NO) && _networking && !_network_server) return;
00982           if ((sd->desc.flags & SGF_NETWORK_ONLY) && !_networking) return;
00983           if ((sd->desc.flags & SGF_NO_NETWORK) && _networking) return;
00984 
00985           var = GetVariableAddress(patches_ptr, &sd->save);
00986           value = (int32)ReadValue(var, sd->save.conv);
00987 
00988           /* clicked on the icon on the left side. Either scroller or bool on/off */
00989           if (x < 21) {
00990             const SettingDescBase *sdb = &sd->desc;
00991             int32 oldvalue = value;
00992 
00993             switch (sdb->cmd) {
00994             case SDT_BOOLX: value ^= 1; break;
00995             case SDT_NUMX: {
00996               /* Add a dynamic step-size to the scroller. In a maximum of
00997                * 50-steps you should be able to get from min to max,
00998                * unless specified otherwise in the 'interval' variable
00999                * of the current patch. */
01000               uint32 step = (sdb->interval == 0) ? ((sdb->max - sdb->min) / 50) : sdb->interval;
01001               if (step == 0) step = 1;
01002 
01003               // don't allow too fast scrolling
01004               if ((w->flags4 & WF_TIMEOUT_MASK) > 2 << WF_TIMEOUT_SHL) {
01005                 _left_button_clicked = false;
01006                 return;
01007               }
01008 
01009               /* Increase or decrease the value and clamp it to extremes */
01010               if (x >= 10) {
01011                 value += step;
01012                 if (value > sdb->max) value = sdb->max;
01013               } else {
01014                 value -= step;
01015                 if (value < sdb->min) value = (sdb->flags & SGF_0ISDISABLED) ? 0 : sdb->min;
01016               }
01017 
01018               /* Set up scroller timeout for numeric values */
01019               if (value != oldvalue && !(sd->desc.flags & SGF_MULTISTRING)) {
01020                 WP(w, def_d).data_2 = btn * 2 + 1 + ((x >= 10) ? 1 : 0);
01021                 w->flags4 |= 5 << WF_TIMEOUT_SHL;
01022                 _left_button_clicked = false;
01023               }
01024             } break;
01025             default: NOT_REACHED();
01026             }
01027 
01028             if (value != oldvalue) {
01029               SetPatchValue(page->entries[btn].index, patches_ptr, value);
01030               SetWindowDirty(w);
01031             }
01032           } else {
01033             /* only open editbox for types that its sensible for */
01034             if (sd->desc.cmd != SDT_BOOLX && !(sd->desc.flags & SGF_MULTISTRING)) {
01035               /* Show the correct currency-translated value */
01036               if (sd->desc.flags & SGF_CURRENCY) value *= _currency->rate;
01037 
01038               WP(w, def_d).data_3 = btn;
01039               SetDParam(0, value);
01040               ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_PATCHES_QUERY_CAPT, 10, 100, w, CS_NUMERAL);
01041             }
01042           }
01043         } break;
01044 
01045         case PATCHSEL_INTERFACE: case PATCHSEL_CONSTRUCTION: case PATCHSEL_VEHICLES:
01046         case PATCHSEL_STATIONS:  case PATCHSEL_ECONOMY:      case PATCHSEL_COMPETITORS:
01047           w->RaiseWidget(WP(w, def_d).data_1 + PATCHSEL_INTERFACE);
01048           WP(w, def_d).data_1 = e->we.click.widget - PATCHSEL_INTERFACE;
01049           w->LowerWidget(WP(w, def_d).data_1 + PATCHSEL_INTERFACE);
01050           DeleteWindowById(WC_QUERY_STRING, 0);
01051           SetWindowDirty(w);
01052           break;
01053       }
01054       break;
01055 
01056     case WE_TIMEOUT:
01057       WP(w, def_d).data_2 = 0;
01058       SetWindowDirty(w);
01059       break;
01060 
01061     case WE_ON_EDIT_TEXT:
01062       if (e->we.edittext.str != NULL) {
01063         const PatchEntry *pe = &_patches_page[WP(w, def_d).data_1].entries[WP(w,def_d).data_3];
01064         const SettingDesc *sd = pe->setting;
01065         int32 value = atoi(e->we.edittext.str);
01066 
01067         /* Save the correct currency-translated value */
01068         if (sd->desc.flags & SGF_CURRENCY) value /= _currency->rate;
01069 
01070         SetPatchValue(pe->index, patches_ptr, value);
01071         SetWindowDirty(w);
01072       }
01073       break;
01074 
01075     case WE_DESTROY:
01076       DeleteWindowById(WC_QUERY_STRING, 0);
01077       break;
01078   }
01079 }
01080 
01081 static const Widget _patches_selection_widgets[] = {
01082 {   WWT_CLOSEBOX,   RESIZE_NONE,    10,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
01083 {    WWT_CAPTION,   RESIZE_NONE,    10,    11,   369,     0,    13, STR_CONFIG_PATCHES_CAPTION,      STR_018C_WINDOW_TITLE_DRAG_THIS},
01084 {      WWT_PANEL,   RESIZE_NONE,    10,     0,   369,    14,    41, 0x0,                             STR_NULL},
01085 {      WWT_PANEL,   RESIZE_NONE,    10,     0,   369,    42,    50, 0x0,                             STR_NULL},
01086 
01087 {    WWT_TEXTBTN,   RESIZE_NONE,     3,    10,    96,    16,    27, STR_CONFIG_PATCHES_GUI,          STR_NULL},
01088 {    WWT_TEXTBTN,   RESIZE_NONE,     3,    97,   183,    16,    27, STR_CONFIG_PATCHES_CONSTRUCTION, STR_NULL},
01089 {    WWT_TEXTBTN,   RESIZE_NONE,     3,   184,   270,    16,    27, STR_CONFIG_PATCHES_VEHICLES,     STR_NULL},
01090 {    WWT_TEXTBTN,   RESIZE_NONE,     3,   271,   357,    16,    27, STR_CONFIG_PATCHES_STATIONS,     STR_NULL},
01091 {    WWT_TEXTBTN,   RESIZE_NONE,     3,    10,    96,    28,    39, STR_CONFIG_PATCHES_ECONOMY,      STR_NULL},
01092 {    WWT_TEXTBTN,   RESIZE_NONE,     3,    97,   183,    28,    39, STR_CONFIG_PATCHES_AI,           STR_NULL},
01093 {   WIDGETS_END},
01094 };
01095 
01096 static const WindowDesc _patches_selection_desc = {
01097   WDP_CENTER, WDP_CENTER, 370, 51, 370, 51,
01098   WC_GAME_OPTIONS, WC_NONE,
01099   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
01100   _patches_selection_widgets,
01101   PatchesSelectionWndProc,
01102 };
01103 
01104 void ShowPatchesSelection()
01105 {
01106   DeleteWindowById(WC_GAME_OPTIONS, 0);
01107   AllocateWindowDesc(&_patches_selection_desc);
01108 }
01109 
01110 
01120 void DrawArrowButtons(int x, int y, int ctab, byte state, bool clickable_left, bool clickable_right)
01121 {
01122   int color = (1 << PALETTE_MODIFIER_GREYOUT) | _colour_gradient[COLOUR_YELLOW][2];
01123 
01124   DrawFrameRect(x,      y + 1, x +  9, y + 9, ctab, (state == 1) ? FR_LOWERED : FR_NONE);
01125   DrawFrameRect(x + 10, y + 1, x + 19, y + 9, ctab, (state == 2) ? FR_LOWERED : FR_NONE);
01126   DrawStringCentered(x +  5, y + 1, STR_6819, TC_FROMSTRING); // [<]
01127   DrawStringCentered(x + 15, y + 1, STR_681A, TC_FROMSTRING); // [>]
01128 
01129   /* Grey out the buttons that aren't clickable */
01130   if (!clickable_left)
01131     GfxFillRect(x +  1, y + 1, x +  1 + 8, y + 8, color);
01132   if (!clickable_right)
01133     GfxFillRect(x + 11, y + 1, x + 11 + 8, y + 8, color);
01134 }
01135 
01139 enum CustomCurrenciesWidgets {
01140   CUSTCURR_EXCHANGERATE = 0,
01141   CUSTCURR_SEPARATOR,
01142   CUSTCURR_PREFIX,
01143   CUSTCURR_SUFFIX,
01144   CUSTCURR_TO_EURO,
01145 };
01146 
01147 static char _str_separator[2];
01148 
01149 static void CustCurrencyWndProc(Window *w, WindowEvent *e)
01150 {
01151   switch (e->event) {
01152     case WE_PAINT: {
01153       int x;
01154       int y = 20;
01155       int clk = WP(w, def_d).data_1;
01156       DrawWindowWidgets(w);
01157 
01158       /* exchange rate */
01159       DrawArrowButtons(10, y, 3, GB(clk, 0, 2), true, true);
01160       SetDParam(0, 1);
01161       SetDParam(1, 1);
01162       DrawString(35, y + 1, STR_CURRENCY_EXCHANGE_RATE, TC_FROMSTRING);
01163       y += 12;
01164 
01165       /* separator */
01166       DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(clk, 2, 2) ? FR_LOWERED : FR_NONE);
01167       x = DrawString(35, y + 1, STR_CURRENCY_SEPARATOR, TC_FROMSTRING);
01168       DoDrawString(_str_separator, x + 4, y + 1, TC_ORANGE);
01169       y += 12;
01170 
01171       /* prefix */
01172       DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(clk, 4, 2) ? FR_LOWERED : FR_NONE);
01173       x = DrawString(35, y + 1, STR_CURRENCY_PREFIX, TC_FROMSTRING);
01174       DoDrawString(_custom_currency.prefix, x + 4, y + 1, TC_ORANGE);
01175       y += 12;
01176 
01177       /* suffix */
01178       DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(clk, 6, 2) ? FR_LOWERED : FR_NONE);
01179       x = DrawString(35, y + 1, STR_CURRENCY_SUFFIX, TC_FROMSTRING);
01180       DoDrawString(_custom_currency.suffix, x + 4, y + 1, TC_ORANGE);
01181       y += 12;
01182 
01183       /* switch to euro */
01184       DrawArrowButtons(10, y, 3, GB(clk, 8, 2), true, true);
01185       SetDParam(0, _custom_currency.to_euro);
01186       DrawString(35, y + 1, (_custom_currency.to_euro != CF_NOEURO) ? STR_CURRENCY_SWITCH_TO_EURO : STR_CURRENCY_SWITCH_TO_EURO_NEVER, TC_FROMSTRING);
01187       y += 12;
01188 
01189       /* Preview */
01190       y += 12;
01191       SetDParam(0, 10000);
01192       DrawString(35, y + 1, STR_CURRENCY_PREVIEW, TC_FROMSTRING);
01193     } break;
01194 
01195     case WE_CLICK: {
01196       int line = (e->we.click.pt.y - 20) / 12;
01197       int len = 0;
01198       int x = e->we.click.pt.x;
01199       StringID str = 0;
01200       CharSetFilter afilter = CS_ALPHANUMERAL;
01201 
01202       switch (line) {
01203         case CUSTCURR_EXCHANGERATE:
01204           if (IsInsideMM(x, 10, 30)) { // clicked buttons
01205             if (x < 20) {
01206               if (_custom_currency.rate > 1) _custom_currency.rate--;
01207               WP(w, def_d).data_1 = 1 << (line * 2 + 0);
01208             } else {
01209               if (_custom_currency.rate < 5000) _custom_currency.rate++;
01210               WP(w, def_d).data_1 = 1 << (line * 2 + 1);
01211             }
01212           } else { // enter text
01213             SetDParam(0, _custom_currency.rate);
01214             str = STR_CONFIG_PATCHES_INT32;
01215             len = 4;
01216             afilter = CS_NUMERAL;
01217           }
01218           break;
01219 
01220         case CUSTCURR_SEPARATOR:
01221           if (IsInsideMM(x, 10, 30)) { // clicked button
01222             WP(w, def_d).data_1 = 1 << (line * 2 + 1);
01223           }
01224           str = BindCString(_str_separator);
01225           len = 1;
01226           break;
01227 
01228         case CUSTCURR_PREFIX:
01229           if (IsInsideMM(x, 10, 30)) { // clicked button
01230             WP(w, def_d).data_1 = 1 << (line * 2 + 1);
01231           }
01232           str = BindCString(_custom_currency.prefix);
01233           len = 12;
01234           break;
01235 
01236         case CUSTCURR_SUFFIX:
01237           if (IsInsideMM(x, 10, 30)) { // clicked button
01238             WP(w, def_d).data_1 = 1 << (line * 2 + 1);
01239           }
01240           str = BindCString(_custom_currency.suffix);
01241           len = 12;
01242           break;
01243 
01244         case CUSTCURR_TO_EURO:
01245           if (IsInsideMM(x, 10, 30)) { // clicked buttons
01246             if (x < 20) {
01247               _custom_currency.to_euro = (_custom_currency.to_euro <= 2000) ?
01248                 CF_NOEURO : _custom_currency.to_euro - 1;
01249               WP(w, def_d).data_1 = 1 << (line * 2 + 0);
01250             } else {
01251               _custom_currency.to_euro =
01252                 Clamp(_custom_currency.to_euro + 1, 2000, MAX_YEAR);
01253               WP(w, def_d).data_1 = 1 << (line * 2 + 1);
01254             }
01255           } else { // enter text
01256             SetDParam(0, _custom_currency.to_euro);
01257             str = STR_CONFIG_PATCHES_INT32;
01258             len = 4;
01259             afilter = CS_NUMERAL;
01260           }
01261           break;
01262       }
01263 
01264       if (len != 0) {
01265         WP(w, def_d).data_2 = line;
01266         ShowQueryString(str, STR_CURRENCY_CHANGE_PARAMETER, len + 1, 250, w, afilter);
01267       }
01268 
01269       w->flags4 |= 5 << WF_TIMEOUT_SHL;
01270       SetWindowDirty(w);
01271     } break;
01272 
01273     case WE_ON_EDIT_TEXT: {
01274       const char *b = e->we.edittext.str;
01275 
01276       switch (WP(w, def_d).data_2) {
01277         case CUSTCURR_EXCHANGERATE:
01278           _custom_currency.rate = Clamp(atoi(b), 1, 5000);
01279           break;
01280 
01281         case CUSTCURR_SEPARATOR: /* Thousands seperator */
01282           _custom_currency.separator = (b[0] == '\0') ? ' ' : b[0];
01283           ttd_strlcpy(_str_separator, b, lengthof(_str_separator));
01284           break;
01285 
01286         case CUSTCURR_PREFIX:
01287           ttd_strlcpy(_custom_currency.prefix, b, lengthof(_custom_currency.prefix));
01288           break;
01289 
01290         case CUSTCURR_SUFFIX:
01291           ttd_strlcpy(_custom_currency.suffix, b, lengthof(_custom_currency.suffix));
01292           break;
01293 
01294         case CUSTCURR_TO_EURO: { /* Year to switch to euro */
01295           int val = atoi(b);
01296 
01297           _custom_currency.to_euro = (val < 2000 ? CF_NOEURO : min(val, MAX_YEAR));
01298           break;
01299         }
01300       }
01301       MarkWholeScreenDirty();
01302     } break;
01303 
01304     case WE_TIMEOUT:
01305       WP(w, def_d).data_1 = 0;
01306       SetWindowDirty(w);
01307       break;
01308 
01309     case WE_DESTROY:
01310       DeleteWindowById(WC_QUERY_STRING, 0);
01311       MarkWholeScreenDirty();
01312       break;
01313   }
01314 }
01315 
01316 static const Widget _cust_currency_widgets[] = {
01317 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW},
01318 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   229,     0,    13, STR_CURRENCY_WINDOW, STR_018C_WINDOW_TITLE_DRAG_THIS},
01319 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   229,    14,   119, 0x0,                 STR_NULL},
01320 {   WIDGETS_END},
01321 };
01322 
01323 static const WindowDesc _cust_currency_desc = {
01324   WDP_CENTER, WDP_CENTER, 230, 120, 230, 120,
01325   WC_CUSTOM_CURRENCY, WC_NONE,
01326   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
01327   _cust_currency_widgets,
01328   CustCurrencyWndProc,
01329 };
01330 
01331 static void ShowCustCurrency()
01332 {
01333   _str_separator[0] = _custom_currency.separator;
01334   _str_separator[1] = '\0';
01335 
01336   DeleteWindowById(WC_CUSTOM_CURRENCY, 0);
01337   AllocateWindowDesc(&_cust_currency_desc);
01338 }

Generated on Wed Oct 1 17:03:23 2008 for openttd by  doxygen 1.5.6