tron@2186: /* $Id$ */ tron@2186: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@2291: #include "currency.h" tron@2163: #include "functions.h" tron@1317: #include "string.h" tron@1309: #include "strings.h" // XXX GetCurrentCurrencyRate() tron@1363: #include "table/sprites.h" tron@507: #include "table/strings.h" truelight@0: #include "window.h" truelight@0: #include "gui.h" truelight@0: #include "gfx.h" truelight@0: #include "command.h" truelight@0: #include "engine.h" tron@430: #include "screenshot.h" dominik@478: #include "newgrf.h" truelight@543: #include "network.h" truelight@625: #include "console.h" truelight@835: #include "town.h" tron@2159: #include "variables.h" truelight@0: truelight@0: static uint32 _difficulty_click_a; truelight@0: static uint32 _difficulty_click_b; truelight@0: static byte _difficulty_timeout; truelight@0: truelight@0: static const StringID _distances_dropdown[] = { truelight@0: STR_0139_IMPERIAL_MILES, truelight@0: STR_013A_METRIC_KILOMETERS, truelight@0: INVALID_STRING_ID truelight@0: }; truelight@0: truelight@0: static const StringID _driveside_dropdown[] = { truelight@0: STR_02E9_DRIVE_ON_LEFT, truelight@0: STR_02EA_DRIVE_ON_RIGHT, truelight@0: INVALID_STRING_ID truelight@0: }; truelight@0: truelight@0: static const StringID _autosave_dropdown[] = { truelight@0: STR_02F7_OFF, truelight@0: STR_AUTOSAVE_1_MONTH, truelight@0: STR_02F8_EVERY_3_MONTHS, truelight@0: STR_02F9_EVERY_6_MONTHS, truelight@0: STR_02FA_EVERY_12_MONTHS, truelight@0: INVALID_STRING_ID, truelight@0: }; truelight@0: truelight@0: static const StringID _designnames_dropdown[] = { truelight@0: STR_02BE_DEFAULT, truelight@0: STR_02BF_CUSTOM, truelight@0: INVALID_STRING_ID truelight@0: }; truelight@0: truelight@0: static StringID *BuildDynamicDropdown(StringID base, int num) truelight@0: { truelight@0: static StringID buf[32 + 1]; truelight@0: StringID *p = buf; truelight@0: while (--num>=0) *p++ = base++; truelight@0: *p = INVALID_STRING_ID; truelight@0: return buf; truelight@0: } truelight@0: tron@1093: static int GetCurRes(void) truelight@0: { truelight@0: int i; tron@2639: tron@2639: for (i = 0; i != _num_resolutions; i++) { darkvater@306: if (_resolutions[i][0] == _screen.width && tron@2639: _resolutions[i][1] == _screen.height) { truelight@0: break; tron@2639: } tron@2639: } truelight@0: return i; truelight@0: } truelight@0: darkvater@871: static inline bool RoadVehiclesAreBuilt(void) darkvater@871: { tron@2639: const Vehicle* v; tron@2639: tron@915: FOR_ALL_VEHICLES(v) { darkvater@871: if (v->type == VEH_Road) return true; darkvater@871: } darkvater@871: return false; darkvater@871: } darkvater@871: tron@2817: tron@2817: static void ShowCustCurrency(void); tron@2817: truelight@0: static void GameOptionsWndProc(Window *w, WindowEvent *e) truelight@0: { Darkvater@1500: switch (e->event) { truelight@0: case WE_PAINT: { darkvater@871: int i; truelight@0: StringID str = STR_02BE_DEFAULT; truelight@0: w->disabled_state = (_vehicle_design_names & 1) ? (++str, 0) : (1 << 21); tron@534: SetDParam(0, str); Darkvater@1500: SetDParam(1, _currency_string_list[_opt_ptr->currency]); Darkvater@1500: SetDParam(2, _opt_ptr->kilometers + STR_0139_IMPERIAL_MILES); Darkvater@1500: SetDParam(3, STR_02E9_DRIVE_ON_LEFT + _opt_ptr->road_side); Darkvater@1500: SetDParam(4, STR_TOWNNAME_ORIGINAL_ENGLISH + _opt_ptr->town_name); Darkvater@1500: SetDParam(5, _autosave_dropdown[_opt_ptr->autosave]); tron@534: SetDParam(6, SPECSTR_LANGUAGE_START + _dynlang.curr); truelight@0: i = GetCurRes(); tron@534: SetDParam(7, i == _num_resolutions ? STR_RES_OTHER : SPECSTR_RESOLUTION_START + i); tron@534: SetDParam(8, SPECSTR_SCREENSHOT_START + _cur_screenshot_format); darkvater@298: (_fullscreen) ? SETBIT(w->click_state, 28) : CLRBIT(w->click_state, 28); // fullscreen button darkvater@298: truelight@0: DrawWindowWidgets(w); darkvater@298: DrawString(20, 175, STR_OPTIONS_FULLSCREEN, 0); // fullscreen truelight@0: } break; truelight@0: truelight@0: case WE_CLICK: Darkvater@1500: switch (e->click.widget) { Darkvater@1806: case 4: case 5: /* Setup currencies dropdown */ Darkvater@1806: ShowDropDownMenu(w, _currency_string_list, _opt_ptr->currency, 5, _game_mode == GM_MENU ? 0 : ~GetMaskOfAllowedCurrencies(), 0); truelight@0: return; Darkvater@1806: case 7: case 8: /* Setup distance unit dropdown */ Darkvater@1806: ShowDropDownMenu(w, _distances_dropdown, _opt_ptr->kilometers, 8, 0, 0); truelight@0: return; Darkvater@1806: case 10: case 11: { /* Setup road-side dropdown */ darkvater@871: int i = 0; darkvater@871: darkvater@871: /* You can only change the drive side if you are in the menu or ingame with darkvater@871: * no vehicles present. In a networking game only the server can change it */ darkvater@871: if ((_game_mode != GM_MENU && RoadVehiclesAreBuilt()) || (_networking && !_network_server)) Darkvater@1500: i = (-1) ^ (1 << _opt_ptr->road_side); // disable the other value darkvater@871: Darkvater@1806: ShowDropDownMenu(w, _driveside_dropdown, _opt_ptr->road_side, 11, i, 0); darkvater@871: } return; Darkvater@1806: case 13: case 14: { /* Setup townname dropdown */ Darkvater@1500: int i = _opt_ptr->town_name; Darkvater@1806: ShowDropDownMenu(w, BuildDynamicDropdown(STR_TOWNNAME_ORIGINAL_ENGLISH, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1), i, 14, (_game_mode == GM_MENU) ? 0 : (-1) ^ (1 << i), 0); truelight@0: return; truelight@0: } Darkvater@1806: case 16: case 17: /* Setup autosave dropdown */ Darkvater@1806: ShowDropDownMenu(w, _autosave_dropdown, _opt_ptr->autosave, 17, 0, 0); truelight@0: return; Darkvater@1806: case 19: case 20: /* Setup customized vehicle-names dropdown */ Darkvater@1806: ShowDropDownMenu(w, _designnames_dropdown, (_vehicle_design_names & 1) ? 1 : 0, 20, (_vehicle_design_names & 2) ? 0 : 2, 0); truelight@0: return; darkvater@871: case 21: /* Save customized vehicle-names to disk */ truelight@0: return; Darkvater@1806: case 23: case 24: /* Setup interface language dropdown */ Darkvater@1806: ShowDropDownMenu(w, _dynlang.dropdown, _dynlang.curr, 24, 0, 0); truelight@0: return; Darkvater@1806: case 26: case 27: /* Setup resolution dropdown */ Darkvater@1806: ShowDropDownMenu(w, BuildDynamicDropdown(SPECSTR_RESOLUTION_START, _num_resolutions), GetCurRes(), 27, 0, 0); truelight@0: return; darkvater@298: case 28: /* Click fullscreen on/off */ darkvater@298: (_fullscreen) ? CLRBIT(w->click_state, 28) : SETBIT(w->click_state, 28); darkvater@298: ToggleFullScreen(!_fullscreen); // toggle full-screen on/off darkvater@298: SetWindowDirty(w); darkvater@298: return; Darkvater@1806: case 30: case 31: /* Setup screenshot format dropdown */ Darkvater@1806: ShowDropDownMenu(w, BuildDynamicDropdown(SPECSTR_SCREENSHOT_START, _num_screenshot_formats), _cur_screenshot_format, 31, 0, 0); truelight@0: return; truelight@0: } truelight@0: break; truelight@0: truelight@193: case WE_DROPDOWN_SELECT: Darkvater@1500: switch (e->dropdown.button) { darkvater@871: case 20: /* Vehicle design names */ truelight@0: if (e->dropdown.index == 0) { truelight@0: DeleteCustomEngineNames(); truelight@0: MarkWholeScreenDirty(); Darkvater@1500: } else if (!(_vehicle_design_names & 1)) { truelight@0: LoadCustomEngineNames(); truelight@0: MarkWholeScreenDirty(); truelight@0: } truelight@0: break; darkvater@871: case 5: /* Currency */ dominik@759: if (e->dropdown.index == 23) dominik@759: ShowCustCurrency(); Darkvater@1500: _opt_ptr->currency = e->dropdown.index; truelight@0: MarkWholeScreenDirty(); truelight@0: break; darkvater@871: case 8: /* Distance units */ Darkvater@1500: _opt_ptr->kilometers = e->dropdown.index; truelight@0: MarkWholeScreenDirty(); truelight@0: break; darkvater@871: case 11: /* Road side */ Darkvater@1500: if (_opt_ptr->road_side != e->dropdown.index) { // only change if setting changed Darkvater@1803: DoCommandP(0, e->dropdown.index, 0, NULL, CMD_SET_ROAD_DRIVE_SIDE | CMD_MSG(STR_00B4_CAN_T_DO_THIS)); dominik@1113: MarkWholeScreenDirty(); dominik@1113: } truelight@0: break; darkvater@871: case 14: /* Town names */ Darkvater@1794: if (_game_mode == GM_MENU) { Darkvater@1794: _opt_ptr->town_name = e->dropdown.index; Darkvater@1794: InvalidateWindow(WC_GAME_OPTIONS, 0); Darkvater@1794: } truelight@0: break; darkvater@871: case 17: /* Autosave options */ Darkvater@1500: _opt_ptr->autosave = e->dropdown.index; truelight@0: SetWindowDirty(w); truelight@0: break; darkvater@871: case 24: /* Change interface language */ truelight@0: ReadLanguagePack(e->dropdown.index); truelight@0: MarkWholeScreenDirty(); truelight@0: break; darkvater@871: case 27: /* Change resolution */ truelight@0: if (e->dropdown.index < _num_resolutions && ChangeResInGame(_resolutions[e->dropdown.index][0],_resolutions[e->dropdown.index][1])) truelight@0: SetWindowDirty(w); truelight@0: break; darkvater@871: case 31: /* Change screenshot format */ truelight@0: SetScreenshotFormat(e->dropdown.index); truelight@0: SetWindowDirty(w); truelight@0: break; truelight@0: } truelight@0: break; dominik@759: dominik@759: case WE_DESTROY: dominik@759: DeleteWindowById(WC_CUSTOM_CURRENCY, 0); dominik@759: break; truelight@0: } dominik@759: truelight@0: } truelight@0: Darkvater@1794: /** Change the side of the road vehicles drive on (server only). Darkvater@1794: * @param x,y unused Darkvater@1794: * @param p1 the side of the road; 0 = left side and 1 = right side Darkvater@1794: * @param p2 unused Darkvater@1794: */ truelight@0: int32 CmdSetRoadDriveSide(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { Darkvater@1803: /* Check boundaries and you can only change this if NO vehicles have been built yet, Darkvater@1803: * except in the intro-menu where of course it's always possible to do so. */ Darkvater@1803: if (p1 > 1 || (_game_mode != GM_MENU && RoadVehiclesAreBuilt())) return CMD_ERROR; Darkvater@1794: truelight@0: if (flags & DC_EXEC) { Darkvater@1500: _opt_ptr->road_side = p1; truelight@0: InvalidateWindow(WC_GAME_OPTIONS,0); truelight@0: } truelight@0: return 0; truelight@0: } truelight@0: truelight@0: static const Widget _game_options_widgets[] = { truelight@867: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 14, 11, 369, 0, 13, STR_00B1_GAME_OPTIONS, STR_018C_WINDOW_TITLE_DRAG_THIS}, truelight@867: { WWT_PANEL, RESIZE_NONE, 14, 0, 369, 14, 238, 0x0, STR_NULL}, truelight@867: { WWT_FRAME, RESIZE_NONE, 14, 10, 179, 20, 55, STR_02E0_CURRENCY_UNITS, STR_NULL}, truelight@867: { WWT_6, RESIZE_NONE, 14, 20, 169, 34, 45, STR_02E1, STR_02E2_CURRENCY_UNITS_SELECTION}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 158, 168, 35, 44, STR_0225, STR_02E2_CURRENCY_UNITS_SELECTION}, truelight@867: { WWT_FRAME, RESIZE_NONE, 14, 190, 359, 20, 55, STR_02E3_DISTANCE_UNITS, STR_NULL}, truelight@867: { WWT_6, RESIZE_NONE, 14, 200, 349, 34, 45, STR_02E4, STR_02E5_DISTANCE_UNITS_SELECTION}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 338, 348, 35, 44, STR_0225, STR_02E5_DISTANCE_UNITS_SELECTION}, truelight@867: { WWT_FRAME, RESIZE_NONE, 14, 10, 179, 62, 97, STR_02E6_ROAD_VEHICLES, STR_NULL}, truelight@867: { WWT_6, RESIZE_NONE, 14, 20, 169, 76, 87, STR_02E7, STR_02E8_SELECT_SIDE_OF_ROAD_FOR}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 158, 168, 77, 86, STR_0225, STR_02E8_SELECT_SIDE_OF_ROAD_FOR}, truelight@867: { WWT_FRAME, RESIZE_NONE, 14, 190, 359, 62, 97, STR_02EB_TOWN_NAMES, STR_NULL}, truelight@867: { WWT_6, RESIZE_NONE, 14, 200, 349, 76, 87, STR_02EC, STR_02ED_SELECT_STYLE_OF_TOWN_NAMES}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 338, 348, 77, 86, STR_0225, STR_02ED_SELECT_STYLE_OF_TOWN_NAMES}, truelight@867: { WWT_FRAME, RESIZE_NONE, 14, 10, 179, 104, 139, STR_02F4_AUTOSAVE, STR_NULL}, truelight@867: { WWT_6, RESIZE_NONE, 14, 20, 169, 118, 129, STR_02F5, STR_02F6_SELECT_INTERVAL_BETWEEN}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 158, 168, 119, 128, STR_0225, STR_02F6_SELECT_INTERVAL_BETWEEN}, truelight@0: truelight@867: { WWT_FRAME, RESIZE_NONE, 14, 10, 359, 194, 228, STR_02BC_VEHICLE_DESIGN_NAMES, STR_NULL}, truelight@867: { WWT_6, RESIZE_NONE, 14, 20, 119, 207, 218, STR_02BD, STR_02C1_VEHICLE_DESIGN_NAMES_SELECTION}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 108, 118, 208, 217, STR_0225, STR_02C1_VEHICLE_DESIGN_NAMES_SELECTION}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 130, 349, 207, 218, STR_02C0_SAVE_CUSTOM_NAMES, STR_02C2_SAVE_CUSTOMIZED_VEHICLE}, truelight@0: truelight@867: { WWT_FRAME, RESIZE_NONE, 14, 190, 359, 104, 139, STR_OPTIONS_LANG, STR_NULL}, truelight@867: { WWT_6, RESIZE_NONE, 14, 200, 349, 118, 129, STR_OPTIONS_LANG_CBO, STR_OPTIONS_LANG_TIP}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 338, 348, 119, 128, STR_0225, STR_OPTIONS_LANG_TIP}, darkvater@298: truelight@867: { WWT_FRAME, RESIZE_NONE, 14, 10, 179, 146, 190, STR_OPTIONS_RES, STR_NULL}, truelight@867: { WWT_6, RESIZE_NONE, 14, 20, 169, 160, 171, STR_OPTIONS_RES_CBO, STR_OPTIONS_RES_TIP}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 158, 168, 161, 170, STR_0225, STR_OPTIONS_RES_TIP}, truelight@867: { WWT_TEXTBTN, RESIZE_NONE, 14, 149, 169, 176, 184, STR_EMPTY, STR_OPTIONS_FULLSCREEN_TIP}, truelight@0: truelight@867: { WWT_FRAME, RESIZE_NONE, 14, 190, 359, 146, 190, STR_OPTIONS_SCREENSHOT_FORMAT, STR_NULL}, truelight@867: { WWT_6, RESIZE_NONE, 14, 200, 349, 160, 171, STR_OPTIONS_SCREENSHOT_FORMAT_CBO, STR_OPTIONS_SCREENSHOT_FORMAT_TIP}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 338, 348, 161, 170, STR_0225, STR_OPTIONS_SCREENSHOT_FORMAT_TIP}, truelight@0: darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _game_options_desc = { darkvater@298: WDP_CENTER, WDP_CENTER, 370, 239, truelight@0: WC_GAME_OPTIONS,0, ludde@2064: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, truelight@0: _game_options_widgets, truelight@0: GameOptionsWndProc truelight@0: }; truelight@0: truelight@0: tron@1093: void ShowGameOptions(void) truelight@0: { truelight@0: DeleteWindowById(WC_GAME_OPTIONS, 0); truelight@0: AllocateWindowDesc(&_game_options_desc); truelight@0: } truelight@0: truelight@0: typedef struct { truelight@0: int16 min; truelight@0: int16 max; truelight@0: int16 step; truelight@0: StringID str; truelight@0: } GameSettingData; truelight@0: truelight@0: static const GameSettingData _game_setting_info[] = { Darkvater@1500: { 0, 7, 1, STR_NULL}, Darkvater@1500: { 0, 3, 1, STR_6830_IMMEDIATE}, Darkvater@1500: { 0, 2, 1, STR_6816_LOW}, Darkvater@1500: { 0, 3, 1, STR_26816_NONE}, Darkvater@1500: {100, 500, 50, STR_NULL}, Darkvater@1500: { 2, 4, 1, STR_NULL}, Darkvater@1500: { 0, 2, 1, STR_6820_LOW}, Darkvater@1500: { 0, 4, 1, STR_681B_VERY_SLOW}, Darkvater@1500: { 0, 2, 1, STR_6820_LOW}, Darkvater@1500: { 0, 2, 1, STR_6823_NONE}, Darkvater@1500: { 0, 3, 1, STR_6826_X1_5}, Darkvater@1500: { 0, 2, 1, STR_6820_LOW}, Darkvater@1500: { 0, 3, 1, STR_682A_VERY_FLAT}, Darkvater@1500: { 0, 3, 1, STR_VERY_LOW}, Darkvater@1500: { 0, 1, 1, STR_682E_STEADY}, Darkvater@1500: { 0, 1, 1, STR_6834_AT_END_OF_LINE_AND_AT_STATIONS}, Darkvater@1500: { 0, 1, 1, STR_6836_OFF}, Darkvater@1500: { 0, 2, 1, STR_6839_PERMISSIVE}, truelight@0: }; truelight@0: tron@536: static inline bool GetBitAndShift(uint32 *b) truelight@0: { truelight@0: uint32 x = *b; truelight@0: *b >>= 1; Darkvater@1500: return HASBIT(x, 0); truelight@0: } truelight@0: celestar@1385: /* celestar@1385: A: competitors celestar@1385: B: start time in months / 3 celestar@1385: C: town count (2 = high, 0 = low) celestar@1385: D: industry count (3 = high, 0 = none) celestar@1385: E: inital loan / 1000 (in GBP) celestar@1385: F: interest rate celestar@1385: G: running costs (0 = low, 2 = high) celestar@1385: H: construction speed of competitors (0 = very slow, 4 = very fast) celestar@1385: I: intelligence (0-2) celestar@1385: J: breakdowns(0 = off, 2 = normal) celestar@1385: K: subsidy multiplier (0 = 1.5, 3 = 4.0) celestar@1385: L: construction cost (0-2) celestar@1385: M: terrain type (0 = very flat, 3 = mountainous) celestar@1385: N: amount of water (0 = very low, 3 = high) celestar@1385: O: economy (0 = steady, 1 = fluctuating) celestar@1385: P: Train reversing (0 = end of line + stations, 1 = end of line) celestar@1385: Q: disasters celestar@1385: R: area restructuring (0 = permissive, 2 = hostile) celestar@1385: */ celestar@1385: static const int16 _default_game_diff[3][GAME_DIFFICULTY_NUM] = { /* celestar@1385: A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R*/ celestar@1385: {2, 2, 1, 3, 300, 2, 0, 2, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0}, //easy celestar@1385: {4, 1, 1, 2, 150, 3, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1}, //medium celestar@1385: {7, 0, 2, 2, 100, 4, 1, 3, 2, 2, 0, 2, 3, 2, 1, 1, 1, 2}, //hard truelight@0: }; truelight@0: truelight@0: void SetDifficultyLevel(int mode, GameOptions *gm_opt) truelight@0: { truelight@0: int i; truelight@0: assert(mode <= 3); truelight@0: truelight@0: gm_opt->diff_level = mode; truelight@0: if (mode != 3) { // not custom Darkvater@1500: for (i = 0; i != GAME_DIFFICULTY_NUM; i++) truelight@0: ((int*)&gm_opt->diff)[i] = _default_game_diff[mode][i]; truelight@0: } truelight@0: } truelight@0: tron@1093: extern void StartupEconomy(void); darkvater@46: darkvater@983: enum { darkvater@983: GAMEDIFF_WND_TOP_OFFSET = 45, darkvater@983: GAMEDIFF_WND_ROWSIZE = 9 darkvater@983: }; darkvater@983: Darkvater@1500: // Temporary holding place of values in the difficulty window until 'Save' is clicked Darkvater@1500: static GameOptions _opt_mod_temp; Darkvater@1500: // 0x383E = (1 << 13) | (1 << 12) | (1 << 11) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) Darkvater@1500: #define DIFF_INGAME_DISABLED_BUTTONS 0x383E Darkvater@1500: truelight@0: static void GameDifficultyWndProc(Window *w, WindowEvent *e) truelight@0: { Darkvater@1500: switch (e->event) { Darkvater@1794: case WE_CREATE: /* Setup disabled buttons when creating window */ Darkvater@1794: // disable all other difficulty buttons during gameplay except for 'custom' Darkvater@1794: w->disabled_state = (_game_mode != GM_NORMAL) ? 0 : (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); Darkvater@1794: Darkvater@1794: if (_game_mode == GM_EDITOR) SETBIT(w->disabled_state, 7); Darkvater@1794: Darkvater@1794: if (_networking) { Darkvater@1794: SETBIT(w->disabled_state, 7); // disable highscore chart in multiplayer Darkvater@1794: if (!_network_server) Darkvater@1794: SETBIT(w->disabled_state, 10); // Disable save-button in multiplayer (and if client) Darkvater@1794: } Darkvater@1794: break; truelight@0: case WE_PAINT: { truelight@0: uint32 click_a, click_b, disabled; truelight@0: int i; Darkvater@1500: int y, value; truelight@0: Darkvater@1500: w->click_state = (1 << 3) << _opt_mod_temp.diff_level; // have current difficulty button clicked truelight@0: DrawWindowWidgets(w); truelight@0: truelight@0: click_a = _difficulty_click_a; truelight@0: click_b = _difficulty_click_b; truelight@0: Darkvater@1500: /* XXX - Disabled buttons in normal gameplay. Bitshifted for each button to see if Darkvater@1500: * that bit is set. If it is set, the button is disabled */ Darkvater@1500: disabled = (_game_mode == GM_NORMAL) ? DIFF_INGAME_DISABLED_BUTTONS : 0; truelight@0: darkvater@983: y = GAMEDIFF_WND_TOP_OFFSET; truelight@0: for (i = 0; i != GAME_DIFFICULTY_NUM; i++) { Darkvater@1500: DrawFrameRect( 5, y, 5 + 8, y + 8, 3, GetBitAndShift(&click_a) ? (1 << 5) : 0); Darkvater@1500: DrawFrameRect(15, y, 15 + 8, y + 8, 3, GetBitAndShift(&click_b) ? (1 << 5) : 0); truelight@543: if (GetBitAndShift(&disabled) || (_networking && !_network_server)) { celestar@2231: int color = PALETTE_MODIFIER_GREYOUT | _color_list[3].unk2; Darkvater@1500: GfxFillRect( 6, y + 1, 6 + 8, y + 8, color); Darkvater@1500: GfxFillRect(16, y + 1, 16 + 8, y + 8, color); truelight@0: } truelight@0: Darkvater@1500: DrawStringCentered(10, y, STR_6819, 0); Darkvater@1500: DrawStringCentered(20, y, STR_681A, 0); truelight@0: truelight@0: truelight@0: value = _game_setting_info[i].str + ((int*)&_opt_mod_temp.diff)[i]; Darkvater@1500: if (i == 4) value *= 1000; // XXX - handle currency option tron@534: SetDParam(0, value); Darkvater@1500: DrawString(30, y, STR_6805_MAXIMUM_NO_COMPETITORS + i, 0); truelight@0: darkvater@983: y += GAMEDIFF_WND_ROWSIZE + 2; // space items apart a bit truelight@0: } truelight@0: } break; truelight@0: truelight@0: case WE_CLICK: Darkvater@1500: switch (e->click.widget) { Darkvater@1500: case 8: { /* Difficulty settings widget, decode click */ Darkvater@1500: const GameSettingData *info; Darkvater@1500: int x, y; truelight@0: uint btn, dis; truelight@0: int val; truelight@193: truelight@543: // Don't allow clients to make any changes truelight@543: if (_networking && !_network_server) truelight@543: return; truelight@543: truelight@0: x = e->click.pt.x - 5; Darkvater@1500: if (!IS_INT_INSIDE(x, 0, 21)) // Button area truelight@0: return; truelight@0: darkvater@983: y = e->click.pt.y - GAMEDIFF_WND_TOP_OFFSET; truelight@0: if (y < 0) truelight@0: return; truelight@0: truelight@0: // Get button from Y coord. darkvater@983: btn = y / (GAMEDIFF_WND_ROWSIZE + 2); darkvater@983: if (btn >= GAME_DIFFICULTY_NUM || y % (GAMEDIFF_WND_ROWSIZE + 2) >= 9) truelight@0: return; truelight@0: truelight@0: // Clicked disabled button? Darkvater@1500: dis = (_game_mode == GM_NORMAL) ? DIFF_INGAME_DISABLED_BUTTONS : 0; darkvater@983: truelight@0: if (HASBIT(dis, btn)) truelight@0: return; truelight@0: truelight@0: _difficulty_timeout = 5; truelight@0: truelight@0: val = ((int*)&_opt_mod_temp.diff)[btn]; truelight@0: Darkvater@1500: info = &_game_setting_info[btn]; // get information about the difficulty setting truelight@0: if (x >= 10) { truelight@0: // Increase button clicked truelight@0: val = min(val + info->step, info->max); truelight@0: SETBIT(_difficulty_click_b, btn); truelight@0: } else { truelight@0: // Decrease button clicked truelight@0: val = max(val - info->step, info->min); truelight@0: SETBIT(_difficulty_click_a, btn); truelight@0: } truelight@0: truelight@0: // save value in temporary variable truelight@0: ((int*)&_opt_mod_temp.diff)[btn] = val; truelight@0: SetDifficultyLevel(3, &_opt_mod_temp); // set difficulty level to custom truelight@0: SetWindowDirty(w); Darkvater@1500: } break; darkvater@983: case 3: case 4: case 5: case 6: /* Easy / Medium / Hard / Custom */ truelight@0: // temporarily change difficulty level darkvater@983: SetDifficultyLevel(e->click.widget - 3, &_opt_mod_temp); truelight@0: SetWindowDirty(w); truelight@0: break; darkvater@983: case 7: /* Highscore Table */ darkvater@983: ShowHighscoreTable(_opt_mod_temp.diff_level, -1); truelight@0: break; darkvater@983: case 10: { /* Save button - save changes */ truelight@0: int btn, val; truelight@0: for (btn = 0; btn != GAME_DIFFICULTY_NUM; btn++) { truelight@0: val = ((int*)&_opt_mod_temp.diff)[btn]; truelight@0: // if setting has changed, change it Darkvater@1501: if (val != ((int*)&_opt_ptr->diff)[btn]) truelight@0: DoCommandP(0, btn, val, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); truelight@0: } truelight@0: DoCommandP(0, -1, _opt_mod_temp.diff_level, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); truelight@0: DeleteWindow(w); dominik@42: // If we are in the editor, we should reload the economy. dominik@42: // This way when you load a game, the max loan and interest rate dominik@42: // are loaded correctly. dominik@42: if (_game_mode == GM_EDITOR) dominik@42: StartupEconomy(); truelight@0: break; truelight@0: } Darkvater@1500: case 11: /* Cancel button - close window, abandon changes */ truelight@0: DeleteWindow(w); truelight@0: break; Darkvater@1500: } break; truelight@0: Darkvater@1500: case WE_MOUSELOOP: /* Handle the visual 'clicking' of the buttons */ truelight@0: if (_difficulty_timeout != 0 && !--_difficulty_timeout) { truelight@0: _difficulty_click_a = 0; truelight@0: _difficulty_click_b = 0; truelight@0: SetWindowDirty(w); truelight@0: } truelight@0: break; truelight@0: } truelight@0: } truelight@0: Darkvater@1500: #undef DIFF_INGAME_DISABLED_BUTTONS Darkvater@1500: truelight@0: static const Widget _game_difficulty_widgets[] = { truelight@867: { WWT_CLOSEBOX, RESIZE_NONE, 10, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 10, 11, 369, 0, 13, STR_6800_DIFFICULTY_LEVEL, STR_018C_WINDOW_TITLE_DRAG_THIS}, truelight@867: { WWT_PANEL, RESIZE_NONE, 10, 0, 369, 14, 29, 0x0, STR_NULL}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 10, 96, 16, 27, STR_6801_EASY, STR_NULL}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 97, 183, 16, 27, STR_6802_MEDIUM, STR_NULL}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 184, 270, 16, 27, STR_6803_HARD, STR_NULL}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 271, 357, 16, 27, STR_6804_CUSTOM, STR_NULL}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 10, 0, 369, 30, 41, STR_6838_SHOW_HI_SCORE_CHART,STR_NULL}, darkvater@983: { WWT_PANEL, RESIZE_NONE, 10, 0, 369, 42, 262, 0x0, STR_NULL}, truelight@867: { WWT_PANEL, RESIZE_NONE, 10, 0, 369, 263, 278, 0x0, STR_NULL}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 105, 185, 265, 276, STR_OPTIONS_SAVE_CHANGES, STR_NULL}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 186, 266, 265, 276, STR_012E_CANCEL, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _game_difficulty_desc = { truelight@0: WDP_CENTER, WDP_CENTER, 370, 279, truelight@0: WC_GAME_OPTIONS,0, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, truelight@0: _game_difficulty_widgets, truelight@0: GameDifficultyWndProc truelight@0: }; truelight@0: tron@1093: void ShowGameDifficulty(void) truelight@0: { truelight@0: DeleteWindowById(WC_GAME_OPTIONS, 0); Darkvater@1500: /* Copy current settings (ingame or in intro) to temporary holding place Darkvater@1500: * change that when setting stuff, copy back on clicking 'OK' */ Darkvater@1500: memcpy(&_opt_mod_temp, _opt_ptr, sizeof(GameOptions)); truelight@0: AllocateWindowDesc(&_game_difficulty_desc); truelight@193: } truelight@0: darkvater@68: // virtual PositionMainToolbar function, calls the right one. tron@1095: static int32 v_PositionMainToolbar(int32 p1) darkvater@68: { tron@2639: if (_game_mode != GM_MENU) PositionMainToolbar(NULL); darkvater@68: return 0; darkvater@68: } darkvater@68: tron@1095: static int32 AiNew_PatchActive_Warning(int32 p1) truelight@84: { Darkvater@2749: if (p1 == 1) ShowErrorMessage(INVALID_STRING_ID, TEMP_AI_ACTIVATED, 0, 0); tron@2639: return 0; truelight@84: } truelight@84: truelight@2682: static int32 Ai_In_Multiplayer_Warning(int32 p1) truelight@2682: { Darkvater@2776: if (p1 == 1) { Darkvater@2776: ShowErrorMessage(INVALID_STRING_ID, TEMP_AI_MULTIPLAYER, 0, 0); Darkvater@2776: _patches.ainew_active = true; Darkvater@2776: } truelight@2682: return 0; truelight@2682: } truelight@2682: tron@1095: static int32 PopulationInLabelActive(int32 p1) truelight@835: { tron@2639: Town* t; truelight@835: truelight@835: FOR_ALL_TOWNS(t) { tron@2639: if (t->xy != 0) UpdateTownVirtCoord(t); truelight@835: } truelight@835: return 0; truelight@835: } truelight@835: tron@1095: static int32 InvisibleTreesActive(int32 p1) dominik@91: { dominik@91: MarkWholeScreenDirty(); dominik@91: return 0; dominik@91: } dominik@91: tron@1095: static int32 InValidateDetailsWindow(int32 p1) darkvater@156: { darkvater@156: InvalidateWindowClasses(WC_VEHICLE_DETAILS); darkvater@156: return 0; darkvater@156: } darkvater@156: Darkvater@1781: static int32 InvalidateStationBuildWindow(int32 p1) Darkvater@1781: { Darkvater@1781: InvalidateWindow(WC_BUILD_STATION, 0); Darkvater@1781: return 0; Darkvater@1781: } Darkvater@1781: darkvater@156: /* Check service intervals of vehicles, p1 is value of % or day based servicing */ tron@1095: static int32 CheckInterval(int32 p1) darkvater@156: { darkvater@156: bool warning; darkvater@156: if (p1) { truelight@193: warning = ( (IS_INT_INSIDE(_patches.servint_trains, 5, 90+1) || _patches.servint_trains == 0) && darkvater@156: (IS_INT_INSIDE(_patches.servint_roadveh, 5, 90+1) || _patches.servint_roadveh == 0) && truelight@193: (IS_INT_INSIDE(_patches.servint_aircraft, 5, 90+1) || _patches.servint_aircraft == 0) && darkvater@156: (IS_INT_INSIDE(_patches.servint_ships, 5, 90+1) || _patches.servint_ships == 0) ); darkvater@156: } else { truelight@193: warning = ( (IS_INT_INSIDE(_patches.servint_trains, 30, 800+1) || _patches.servint_trains == 0) && darkvater@156: (IS_INT_INSIDE(_patches.servint_roadveh, 30, 800+1) || _patches.servint_roadveh == 0) && truelight@193: (IS_INT_INSIDE(_patches.servint_aircraft, 30, 800+1) || _patches.servint_aircraft == 0) && darkvater@156: (IS_INT_INSIDE(_patches.servint_ships, 30, 800+1) || _patches.servint_ships == 0) ); darkvater@156: } darkvater@156: darkvater@156: if (!warning) Darkvater@2749: ShowErrorMessage(INVALID_STRING_ID, STR_CONFIG_PATCHES_SERVICE_INTERVAL_INCOMPATIBLE, 0, 0); darkvater@156: darkvater@156: return InValidateDetailsWindow(0); darkvater@156: } darkvater@156: bjarni@2293: static int32 EngineRenewUpdate(int32 p1) bjarni@2293: { bjarni@2293: DoCommandP(0, 0, _patches.autorenew, NULL, CMD_REPLACE_VEHICLE); bjarni@2293: return 0; bjarni@2293: } bjarni@2293: bjarni@2293: static int32 EngineRenewMonthsUpdate(int32 p1) bjarni@2293: { bjarni@2293: DoCommandP(0, 1, _patches.autorenew_months, NULL, CMD_REPLACE_VEHICLE); bjarni@2293: return 0; bjarni@2293: } bjarni@2293: bjarni@2293: static int32 EngineRenewMoneyUpdate(int32 p1) bjarni@2293: { bjarni@2293: DoCommandP(0, 2, _patches.autorenew_money, NULL, CMD_REPLACE_VEHICLE); bjarni@2293: return 0; bjarni@2293: } bjarni@2293: darkvater@68: typedef int32 PatchButtonClick(int32); darkvater@68: truelight@0: typedef struct PatchEntry { tron@2639: byte type; // type of selector tron@2639: byte flags; // selector flags tron@2639: StringID str; // string with descriptive text tron@2639: char console_name[40]; // the name this patch has in console tron@2639: void* variable; // pointer to the variable tron@2639: int32 min, max; // range for spinbox setting tron@2639: uint32 step; // step for spinbox tron@2639: PatchButtonClick* click_proc; // callback procedure truelight@0: } PatchEntry; truelight@0: truelight@0: enum { darkvater@179: PE_BOOL = 0, darkvater@179: PE_UINT8 = 1, darkvater@179: PE_INT16 = 2, darkvater@179: PE_UINT16 = 3, darkvater@179: PE_INT32 = 4, darkvater@179: PE_CURRENCY = 5, Darkvater@1741: // selector flags Darkvater@2454: PF_0ISDIS = 1 << 0, // a value of zero means the feature is disabled Darkvater@2454: PF_NOCOMMA = 1 << 1, // number without any thousand seperators Darkvater@2454: PF_MULTISTRING = 1 << 2, // string but only a limited number of options, so don't open editobx Darkvater@2454: PF_PLAYERBASED = 1 << 3, // This has to match the entries that are in settings.c, patch_player_settings darkvater@998: PF_NETWORK_ONLY = 1 << 4, // this setting only applies to network games truelight@0: }; truelight@0: truelight@0: static const PatchEntry _patches_ui[] = { truelight@625: {PE_BOOL, PF_PLAYERBASED, STR_CONFIG_PATCHES_VEHICLESPEED, "vehicle_speed", &_patches.vehicle_speed, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, PF_PLAYERBASED, STR_CONFIG_PATCHES_LONGDATE, "long_date", &_patches.status_long_date, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, PF_PLAYERBASED, STR_CONFIG_PATCHES_SHOWFINANCES, "show_finances", &_patches.show_finances, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTOSCROLL, "autoscroll", &_patches.autoscroll, 0, 0, 0, NULL}, tron@2680: {PE_BOOL, PF_PLAYERBASED, STR_CONFIG_PATCHES_REVERSE_SCROLLING, "reverse_scroll", &_patches.reverse_scroll, 0, 0, 0, NULL }, truelight@0: truelight@625: {PE_UINT8, PF_PLAYERBASED, STR_CONFIG_PATCHES_ERRMSG_DURATION, "errmsg_duration", &_patches.errmsg_duration, 0, 20, 1, NULL}, truelight@193: truelight@625: {PE_UINT8, PF_MULTISTRING | PF_PLAYERBASED, STR_CONFIG_PATCHES_TOOLBAR_POS, "toolbar_pos", &_patches.toolbar_pos, 0, 2, 1, &v_PositionMainToolbar}, truelight@625: {PE_UINT8, PF_0ISDIS | PF_PLAYERBASED, STR_CONFIG_PATCHES_SNAP_RADIUS, "window_snap_radius", &_patches.window_snap_radius, 1, 32, 1, NULL}, truelight@625: {PE_BOOL, PF_PLAYERBASED, STR_CONFIG_PATCHES_INVISIBLE_TREES, "invisible_trees", &_patches.invisible_trees, 0, 1, 1, &InvisibleTreesActive}, truelight@835: {PE_BOOL, PF_PLAYERBASED, STR_CONFIG_PATCHES_POPULATION_IN_LABEL, "population_in_label", &_patches.population_in_label, 0, 1, 1, &PopulationInLabelActive}, tron@1218: tron@1218: {PE_INT32, 0, STR_CONFIG_PATCHES_MAP_X, "map_x", &_patches.map_x, 6, 11, 1, NULL}, tron@1218: {PE_INT32, 0, STR_CONFIG_PATCHES_MAP_Y, "map_y", &_patches.map_y, 6, 11, 1, NULL}, peter1138@2619: peter1138@2619: {PE_BOOL, PF_PLAYERBASED, STR_CONFIG_PATCHES_LINK_TERRAFORM_TOOLBAR, "link_terraform_toolbar", &_patches.link_terraform_toolbar, 0, 1, 1, NULL}, truelight@0: }; truelight@0: truelight@0: static const PatchEntry _patches_construction[] = { truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_BUILDONSLOPES, "build_on_slopes", &_patches.build_on_slopes, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_EXTRADYNAMITE, "extra_dynamite", &_patches.extra_dynamite, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_LONGBRIDGES, "long_bridges", &_patches.longbridges, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_SIGNALSIDE, "signal_side", &_patches.signal_side, 0, 0, 0, NULL}, truelight@193: truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_SMALL_AIRPORTS, "always_small_airport", &_patches.always_small_airport, 0, 0, 0, NULL}, truelight@625: {PE_UINT8, PF_PLAYERBASED, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY, "drag_signals_density", &_patches.drag_signals_density, 1, 20, 1, NULL}, truelight@0: }; truelight@0: truelight@0: static const PatchEntry _patches_vehicles[] = { truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_REALISTICACCEL, "realistic_acceleration", &_patches.realistic_acceleration, 0, 0, 0, NULL}, matthijs@1247: {PE_BOOL, 0, STR_CONFIG_PATCHES_FORBID_90_DEG, "forbid_90_deg", &_patches.forbid_90_deg, 0, 0, 0, NULL}, matthijs@1247: {PE_BOOL, 0, STR_CONFIG_PATCHES_MAMMOTHTRAINS, "mammoth_trains", &_patches.mammoth_trains, 0, 0, 0, NULL}, matthijs@1247: {PE_BOOL, 0, STR_CONFIG_PATCHES_GOTODEPOT, "goto_depot", &_patches.gotodepot, 0, 0, 0, NULL}, matthijs@1247: {PE_BOOL, 0, STR_CONFIG_PATCHES_ROADVEH_QUEUE, "roadveh_queue", &_patches.roadveh_queue, 0, 0, 0, NULL}, matthijs@1247: {PE_BOOL, 0, STR_CONFIG_PATCHES_NEW_PATHFINDING_ALL, "new_pathfinding_all", &_patches.new_pathfinding_all, 0, 0, 0, NULL}, truelight@0: truelight@625: {PE_BOOL, PF_PLAYERBASED, STR_CONFIG_PATCHES_WARN_INCOME_LESS, "train_income_warn", &_patches.train_income_warn, 0, 0, 0, NULL}, truelight@625: {PE_UINT8, PF_MULTISTRING | PF_PLAYERBASED, STR_CONFIG_PATCHES_ORDER_REVIEW, "order_review_system", &_patches.order_review_system,0,2, 1, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_NEVER_EXPIRE_VEHICLES, "never_expire_vehicles", &_patches.never_expire_vehicles,0,0,0, NULL}, truelight@0: truelight@625: {PE_UINT16, PF_0ISDIS | PF_PLAYERBASED, STR_CONFIG_PATCHES_LOST_TRAIN_DAYS, "lost_train_days", &_patches.lost_train_days, 180,720, 60, NULL}, bjarni@2293: {PE_BOOL, PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTORENEW_VEHICLE, "autorenew", &_patches.autorenew, 0, 0, 0, &EngineRenewUpdate}, bjarni@2293: {PE_INT16, PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTORENEW_MONTHS, "autorenew_months", &_patches.autorenew_months, -12, 12, 1, &EngineRenewMonthsUpdate}, bjarni@2293: {PE_CURRENCY, PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTORENEW_MONEY, "autorenew_money", &_patches.autorenew_money, 0, 2000000, 100000, &EngineRenewMoneyUpdate}, darkvater@179: truelight@1282: {PE_UINT16, 0, STR_CONFIG_PATCHES_MAX_TRAINS, "max_trains", &_patches.max_trains, 0,5000, 50, NULL}, truelight@1282: {PE_UINT16, 0, STR_CONFIG_PATCHES_MAX_ROADVEH, "max_roadveh", &_patches.max_roadveh, 0,5000, 50, NULL}, truelight@1282: {PE_UINT16, 0, STR_CONFIG_PATCHES_MAX_AIRCRAFT, "max_aircraft", &_patches.max_aircraft, 0,5000, 50, NULL}, truelight@1282: {PE_UINT16, 0, STR_CONFIG_PATCHES_MAX_SHIPS, "max_ships", &_patches.max_ships, 0,5000, 50, NULL}, truelight@625: truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_SERVINT_ISPERCENT,"servint_isperfect",&_patches.servint_ispercent, 0, 0, 0, &CheckInterval}, truelight@625: {PE_UINT16, PF_0ISDIS, STR_CONFIG_PATCHES_SERVINT_TRAINS, "servint_trains", &_patches.servint_trains, 5,800, 5, &InValidateDetailsWindow}, truelight@625: {PE_UINT16, PF_0ISDIS, STR_CONFIG_PATCHES_SERVINT_ROADVEH, "servint_roadveh", &_patches.servint_roadveh, 5,800, 5, &InValidateDetailsWindow}, truelight@625: {PE_UINT16, PF_0ISDIS, STR_CONFIG_PATCHES_SERVINT_AIRCRAFT, "servint_aircraft", &_patches.servint_aircraft, 5,800, 5, &InValidateDetailsWindow}, truelight@625: {PE_UINT16, PF_0ISDIS, STR_CONFIG_PATCHES_SERVINT_SHIPS, "servint_ships", &_patches.servint_ships, 5,800, 5, &InValidateDetailsWindow}, darkvater@1037: {PE_BOOL, 0, STR_CONFIG_PATCHES_NOSERVICE, "no_servicing_if_no_breakdowns", &_patches.no_servicing_if_no_breakdowns, 0, 0, 0, NULL}, peter1138@2456: {PE_BOOL, 0, STR_CONFIG_PATCHES_WAGONSPEEDLIMITS, "wagon_speed_limits", &_patches.wagon_speed_limits, 0, 0, 0, NULL}, truelight@0: }; truelight@0: truelight@0: static const PatchEntry _patches_stations[] = { truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_JOINSTATIONS, "join_stations", &_patches.join_stations, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_FULLLOADANY, "full_load_any", &_patches.full_load_any, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_IMPROVEDLOAD, "improved_load", &_patches.improved_load, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_SELECTGOODS, "select_goods", &_patches.selectgoods, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_NEW_NONSTOP, "new_nonstop", &_patches.new_nonstop, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_NONUNIFORM_STATIONS, "nonuniform_stations", &_patches.nonuniform_stations, 0, 0, 0, NULL}, Darkvater@1781: {PE_UINT8, 0, STR_CONFIG_PATCHES_STATION_SPREAD, "station_spread", &_patches.station_spread, 4, 64, 1, &InvalidateStationBuildWindow}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_SERVICEATHELIPAD, "service_at_helipad", &_patches.serviceathelipad, 0, 0, 0, NULL}, celestar@951: {PE_BOOL, 0, STR_CONFIG_PATCHES_CATCHMENT, "modified_catchment", &_patches.modified_catchment, 0, 0, 0, NULL}, truelight@0: }; truelight@0: truelight@0: static const PatchEntry _patches_economy[] = { truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_INFLATION, "inflation", &_patches.inflation, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_BUILDXTRAIND, "build_rawmaterial", &_patches.build_rawmaterial_ind, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_MULTIPINDTOWN, "multiple_industry_per_town", &_patches.multiple_industry_per_town,0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_SAMEINDCLOSE, "same_industry_close", &_patches.same_industry_close, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_BRIBE, "bribe", &_patches.bribe, 0, 0, 0, NULL}, truelight@625: {PE_UINT8, 0, STR_CONFIG_PATCHES_SNOWLINE_HEIGHT, "snow_line_height", &_patches.snow_line_height, 2, 13, 1, NULL}, truelight@193: truelight@625: {PE_INT32, PF_NOCOMMA, STR_CONFIG_PATCHES_COLORED_NEWS_DATE, "colored_new_data", &_patches.colored_news_date, 1900, 2200, 5, NULL}, darkvater@998: {PE_INT32, PF_NOCOMMA, STR_CONFIG_PATCHES_STARTING_DATE, "starting_date", &_patches.starting_date, MAX_YEAR_BEGIN_REAL, MAX_YEAR_END_REAL, 1, NULL}, darkvater@998: {PE_INT32, PF_NOCOMMA | PF_NETWORK_ONLY, STR_CONFIG_PATCHES_ENDING_DATE, "ending_date", &_patches.ending_date, MAX_YEAR_BEGIN_REAL, MAX_YEAR_END_REAL, 1, NULL}, truelight@0: truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_SMOOTH_ECONOMY, "smooth_economy", &_patches.smooth_economy, 0, 0, 0, NULL}, darkvater@930: {PE_BOOL, 0, STR_CONFIG_PATCHES_ALLOW_SHARES, "allow_shares", &_patches.allow_shares, 0, 0, 0, NULL}, truelight@0: }; truelight@0: truelight@21: static const PatchEntry _patches_ai[] = { truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_AINEW_ACTIVE, "ainew_active", &_patches.ainew_active, 0, 1, 1, &AiNew_PatchActive_Warning}, truelight@2682: {PE_BOOL, 0, STR_CONFIG_PATCHES_AI_IN_MULTIPLAYER, "ai_in_multiplayer", &_patches.ai_in_multiplayer, 0, 1, 1, &Ai_In_Multiplayer_Warning}, truelight@84: truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_AI_BUILDS_TRAINS, "ai_disable_veh_train", &_patches.ai_disable_veh_train, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_AI_BUILDS_ROADVEH,"ai_disable_veh_roadveh",&_patches.ai_disable_veh_roadveh, 0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_AI_BUILDS_AIRCRAFT,"ai_disable_veh_aircraft",&_patches.ai_disable_veh_aircraft,0, 0, 0, NULL}, truelight@625: {PE_BOOL, 0, STR_CONFIG_PATCHES_AI_BUILDS_SHIPS,"ai_disable_veh_ship",&_patches.ai_disable_veh_ship, 0, 0, 0, NULL}, truelight@21: }; truelight@21: truelight@0: typedef struct PatchPage { truelight@0: const PatchEntry *entries; truelight@0: uint num; truelight@0: } PatchPage; truelight@0: truelight@0: static const PatchPage _patches_page[] = { darkvater@179: {_patches_ui, lengthof(_patches_ui) }, truelight@0: {_patches_construction, lengthof(_patches_construction) }, darkvater@179: {_patches_vehicles, lengthof(_patches_vehicles) }, darkvater@179: {_patches_stations, lengthof(_patches_stations) }, darkvater@179: {_patches_economy, lengthof(_patches_economy) }, darkvater@179: {_patches_ai, lengthof(_patches_ai) }, truelight@0: }; truelight@0: truelight@26: truelight@0: static int32 ReadPE(const PatchEntry*pe) truelight@0: { Darkvater@1741: switch (pe->type) { truelight@0: case PE_BOOL: return *(bool*)pe->variable; truelight@0: case PE_UINT8: return *(uint8*)pe->variable; truelight@0: case PE_INT16: return *(int16*)pe->variable; truelight@0: case PE_UINT16: return *(uint16*)pe->variable; truelight@0: case PE_INT32: return *(int32*)pe->variable; tron@2468: case PE_CURRENCY: return (*(int32*)pe->variable) * _currency->rate; Darkvater@1741: default: NOT_REACHED(); truelight@0: } truelight@193: truelight@0: /* useless, but avoids compiler warning this way */ truelight@0: return 0; truelight@0: } truelight@0: tron@2631: static void WritePE(const PatchEntry* p, int32 v) truelight@20: { tron@2631: if ((p->flags & PF_0ISDIS) && v <= 0) { tron@2631: switch (p->type) { tron@2631: case PE_BOOL: *(bool* )p->variable = false; break; tron@2631: case PE_UINT8: *(uint8* )p->variable = 0; break; tron@2631: case PE_INT16: *(int16* )p->variable = 0; break; tron@2631: case PE_UINT16: *(uint16*)p->variable = 0; break; tron@2631: case PE_CURRENCY: *(int32* )p->variable = 0; break; tron@2631: case PE_INT32: *(int32* )p->variable = 0; break; darkvater@1031: } darkvater@48: return; darkvater@48: } darkvater@48: Darkvater@1741: // "clamp" 'disabled' value to smallest type tron@2631: switch (p->type) { tron@2631: case PE_BOOL: *(bool* )p->variable = (v != 0); break; tron@2631: case PE_UINT8: *(uint8* )p->variable = clamp(v, p->min, p->max); break; tron@2631: case PE_INT16: *(int16* )p->variable = clamp(v, p->min, p->max); break; tron@2631: case PE_UINT16: *(uint16*)p->variable = clamp(v, p->min, p->max); break; tron@2631: case PE_CURRENCY: *(int32* )p->variable = clamp(v, p->min, p->max); break; tron@2631: case PE_INT32: *(int32* )p->variable = clamp(v, p->min, p->max); break; Darkvater@1741: default: NOT_REACHED(); truelight@20: } truelight@0: } truelight@0: truelight@0: static void PatchesSelectionWndProc(Window *w, WindowEvent *e) truelight@0: { Darkvater@1741: switch (e->event) { truelight@0: case WE_PAINT: { truelight@0: int x,y; truelight@0: const PatchEntry *pe; truelight@0: const PatchPage *page; truelight@0: uint clk; truelight@0: int32 val; tron@2639: uint i; truelight@0: truelight@0: w->click_state = 1 << (WP(w,def_d).data_1 + 4); truelight@0: truelight@0: DrawWindowWidgets(w); truelight@0: truelight@0: x = 0; truelight@0: y = 46; truelight@0: clk = WP(w,def_d).data_2; truelight@0: page = &_patches_page[WP(w,def_d).data_1]; darkvater@998: for (i = 0, pe = page->entries; i != page->num; i++, pe++) { dominik@11: bool disabled = false; truelight@543: bool editable = true; darkvater@998: darkvater@998: if ((pe->flags & PF_NETWORK_ONLY) && !_networking) darkvater@998: editable = false; darkvater@998: truelight@543: // We do not allow changes of some items when we are a client in a networkgame truelight@543: if (!(pe->flags & PF_PLAYERBASED) && _networking && !_network_server) truelight@543: editable = false; truelight@0: if (pe->type == PE_BOOL) { truelight@543: if (editable) hackykid@1938: DrawFrameRect(x+5, y+1, x+15+9, y+9, (*(bool*)pe->variable) ? 6 : 4, (*(bool*)pe->variable) ? FR_LOWERED : 0); truelight@543: else hackykid@1938: DrawFrameRect(x+5, y+1, x+15+9, y+9, (*(bool*)pe->variable) ? 7 : 9, (*(bool*)pe->variable) ? FR_LOWERED : 0); tron@534: SetDParam(0, *(bool*)pe->variable ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF); truelight@0: } else { hackykid@1938: DrawFrameRect(x+5, y+1, x+5+9, y+9, 3, clk == i*2+1 ? FR_LOWERED : 0); hackykid@1938: DrawFrameRect(x+15, y+1, x+15+9, y+9, 3, clk == i*2+2 ? FR_LOWERED : 0); truelight@543: if (!editable) { celestar@2231: int color = PALETTE_MODIFIER_GREYOUT | _color_list[3].unk2; truelight@543: GfxFillRect(x+6, y+2, x+6+8, y+9, color); truelight@543: GfxFillRect(x+16, y+2, x+16+8, y+9, color); truelight@543: } truelight@0: DrawStringCentered(x+10, y+1, STR_6819, 0); truelight@0: DrawStringCentered(x+20, y+1, STR_681A, 0); truelight@0: truelight@0: val = ReadPE(pe); tron@2468: if (pe->type == PE_CURRENCY) val /= _currency->rate; dominik@11: disabled = ((val == 0) && (pe->flags & PF_0ISDIS)); dominik@11: if (disabled) { tron@534: SetDParam(0, STR_CONFIG_PATCHES_DISABLED); truelight@0: } else { tron@534: SetDParam(1, val); truelight@26: if (pe->type == PE_CURRENCY) tron@534: SetDParam(0, STR_CONFIG_PATCHES_CURRENCY); truelight@53: else { truelight@53: if (pe->flags & PF_MULTISTRING) tron@534: SetDParam(0, pe->str + val + 1); truelight@53: else tron@534: SetDParam(0, pe->flags & PF_NOCOMMA ? STR_CONFIG_PATCHES_INT32 : STR_7024); truelight@53: } truelight@0: } truelight@0: } dominik@11: DrawString(30, y+1, (pe->str)+disabled, 0); truelight@0: y += 11; truelight@0: } truelight@0: break; truelight@0: } truelight@0: truelight@0: case WE_CLICK: truelight@0: switch(e->click.widget) { truelight@0: case 3: { truelight@0: int x,y; truelight@0: uint btn; truelight@0: const PatchPage *page; truelight@0: const PatchEntry *pe; truelight@0: truelight@0: y = e->click.pt.y - 46 - 1; truelight@0: if (y < 0) return; truelight@193: truelight@0: btn = y / 11; truelight@0: if (y % 11 > 9) return; truelight@0: truelight@0: page = &_patches_page[WP(w,def_d).data_1]; truelight@0: if (btn >= page->num) return; truelight@0: pe = &page->entries[btn]; truelight@0: truelight@0: x = e->click.pt.x - 5; truelight@0: if (x < 0) return; truelight@0: darkvater@998: if (((pe->flags & PF_NETWORK_ONLY) && !_networking) || // return if action is only active in network darkvater@998: (!(pe->flags & PF_PLAYERBASED) && _networking && !_network_server)) // return if only server can change it truelight@543: return; truelight@543: darkvater@68: if (x < 21) { // clicked on the icon on the left side. Either scroller or bool on/off truelight@0: int32 val = ReadPE(pe), oval = val; truelight@0: truelight@0: switch(pe->type) { truelight@0: case PE_BOOL: truelight@0: val ^= 1; truelight@0: break; truelight@0: case PE_UINT8: truelight@0: case PE_INT16: truelight@0: case PE_UINT16: truelight@0: case PE_INT32: truelight@26: case PE_CURRENCY: truelight@0: // don't allow too fast scrolling truelight@0: if ((w->flags4 & WF_TIMEOUT_MASK) > 2 << WF_TIMEOUT_SHL) { truelight@0: _left_button_clicked = false; truelight@0: return; truelight@0: } truelight@0: truelight@0: if (x >= 10) { truelight@0: //increase truelight@0: if (pe->flags & PF_0ISDIS && val == 0) truelight@0: val = pe->min; truelight@0: else truelight@0: val += pe->step; truelight@0: if (val > pe->max) val = pe->max; truelight@0: } else { dominik@47: // decrease truelight@0: if (val <= pe->min && pe->flags & PF_0ISDIS) { truelight@0: val = 0; truelight@0: } else { truelight@0: val -= pe->step; truelight@0: if (val < pe->min) val = pe->min; truelight@0: } truelight@0: } truelight@193: truelight@0: if (val != oval) { truelight@0: WP(w,def_d).data_2 = btn * 2 + 1 + ((x>=10) ? 1 : 0); truelight@0: w->flags4 |= 5 << WF_TIMEOUT_SHL; truelight@0: _left_button_clicked = false; truelight@0: } truelight@193: break; truelight@0: } truelight@0: if (val != oval) { truelight@543: // To make patch-changes network-safe tron@2468: if (pe->type == PE_CURRENCY) val /= _currency->rate; truelight@543: // If an item is playerbased, we do not send it over the network (if any) truelight@543: if (pe->flags & PF_PLAYERBASED) { truelight@543: WritePE(pe, val); truelight@543: } else { truelight@543: // Else we do truelight@543: DoCommandP(0, (byte)WP(w,def_d).data_1 + ((byte)btn << 8), val, NULL, CMD_CHANGE_PATCH_SETTING); truelight@543: } truelight@0: SetWindowDirty(w); darkvater@68: darkvater@68: if (pe->click_proc != NULL) // call callback function darkvater@68: pe->click_proc(val); truelight@0: } truelight@0: } else { darkvater@68: if (pe->type != PE_BOOL && !(pe->flags & PF_MULTISTRING)) { // do not open editbox truelight@0: WP(w,def_d).data_3 = btn; tron@534: SetDParam(0, ReadPE(pe)); truelight@0: ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_PATCHES_QUERY_CAPT, 10, 100, WC_GAME_OPTIONS, 0); truelight@0: } truelight@0: } truelight@193: truelight@0: break; truelight@0: } truelight@21: case 4: case 5: case 6: case 7: case 8: case 9: truelight@0: WP(w,def_d).data_1 = e->click.widget - 4; truelight@0: DeleteWindowById(WC_QUERY_STRING, 0); truelight@0: SetWindowDirty(w); truelight@0: break; truelight@0: } truelight@0: break; truelight@0: truelight@0: case WE_TIMEOUT: truelight@0: WP(w,def_d).data_2 = 0; truelight@0: SetWindowDirty(w); truelight@0: break; truelight@0: truelight@0: case WE_ON_EDIT_TEXT: { truelight@0: if (*e->edittext.str) { truelight@0: const PatchPage *page = &_patches_page[WP(w,def_d).data_1]; darkvater@68: const PatchEntry *pe = &page->entries[WP(w,def_d).data_3]; truelight@543: int32 val; truelight@543: val = atoi(e->edittext.str); tron@2468: if (pe->type == PE_CURRENCY) val /= _currency->rate; truelight@543: // If an item is playerbased, we do not send it over the network (if any) truelight@543: if (pe->flags & PF_PLAYERBASED) { truelight@543: WritePE(pe, val); truelight@543: } else { truelight@543: // Else we do truelight@543: DoCommandP(0, (byte)WP(w,def_d).data_1 + ((byte)WP(w,def_d).data_3 << 8), val, NULL, CMD_CHANGE_PATCH_SETTING); truelight@543: } truelight@0: SetWindowDirty(w); darkvater@68: darkvater@68: if (pe->click_proc != NULL) // call callback function darkvater@68: pe->click_proc(*(int32*)pe->variable); truelight@0: } truelight@0: break; truelight@0: } truelight@0: truelight@0: case WE_DESTROY: truelight@0: DeleteWindowById(WC_QUERY_STRING, 0); truelight@0: break; truelight@0: } truelight@0: } truelight@0: Darkvater@1796: /** Network-safe changing of patch-settings. Darkvater@1796: * @param p1 various bitstuffed elements tron@2140: * - p1 = (bit 0- 7) - the patches type (page) that is being changed (construction, network, ai) tron@2140: * - p2 = (bit 8-15) - the actual patch (entry) being set inside the category Darkvater@1739: * @param p2 the new value for the patch Darkvater@1796: * @todo check that the new value is a valid one. Awful lot of work, but since only Darkvater@1796: * the server is allowed to do this, we trust it on this one :) Darkvater@1739: */ truelight@543: int32 CmdChangePatchSetting(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@543: { tron@2140: byte pcat = GB(p1, 0, 8); tron@2140: byte pel = GB(p1, 8, 8); Darkvater@1739: Darkvater@1796: if (pcat >= lengthof(_patches_page)) return CMD_ERROR; Darkvater@1796: if (pel >= _patches_page[pcat].num) return CMD_ERROR; truelight@543: truelight@543: if (flags & DC_EXEC) { Darkvater@1739: const PatchEntry *pe = &_patches_page[pcat].entries[pel]; truelight@543: WritePE(pe, (int32)p2); truelight@543: truelight@543: InvalidateWindow(WC_GAME_OPTIONS, 0); truelight@543: } truelight@543: truelight@543: return 0; truelight@543: } truelight@543: Darkvater@1739: static const PatchEntry *IConsoleGetPatch(const char *name, uint *page, uint *entry) Darkvater@1739: { Darkvater@1739: const PatchPage *pp; Darkvater@1739: const PatchEntry *pe; Darkvater@1739: Darkvater@1739: for (*page = 0; *page < lengthof(_patches_page); (*page)++) { Darkvater@1739: pp = &_patches_page[*page]; Darkvater@1739: for (*entry = 0; *entry < pp->num; (*entry)++) { Darkvater@1739: pe = &pp->entries[*entry]; Darkvater@1739: if (strncmp(pe->console_name, name, sizeof(pe->console_name)) == 0) Darkvater@1739: return pe; Darkvater@1739: } Darkvater@1739: } Darkvater@1739: Darkvater@1739: return NULL; Darkvater@1739: } Darkvater@1739: truelight@625: /* Those 2 functions need to be here, else we have to make some stuff non-static truelight@625: and besides, it is also better to keep stuff like this at the same place */ Darkvater@2454: void IConsoleSetPatchSetting(const char *name, const char *value) truelight@625: { Darkvater@1739: const PatchEntry *pe; Darkvater@1739: uint page, entry; truelight@625: int val; truelight@625: Darkvater@1739: pe = IConsoleGetPatch(name, &page, &entry); truelight@625: Darkvater@1739: if (pe == NULL) { Darkvater@1805: IConsolePrintF(_icolour_warn, "'%s' is an unknown patch setting.", name); truelight@625: return; truelight@625: } truelight@625: Darkvater@1739: sscanf(value, "%d", &val); truelight@625: Darkvater@1739: if (pe->type == PE_CURRENCY) // currency can be different on each client tron@2468: val /= _currency->rate; truelight@625: truelight@625: // If an item is playerbased, we do not send it over the network (if any) truelight@625: if (pe->flags & PF_PLAYERBASED) { truelight@625: WritePE(pe, val); Darkvater@1739: } else // Else we do Darkvater@1739: DoCommandP(0, page + (entry << 8), val, NULL, CMD_CHANGE_PATCH_SETTING); truelight@625: Darkvater@1739: { Darkvater@1739: char tval[20]; Darkvater@1739: const char *tval2 = value; Darkvater@1739: if (pe->type == PE_BOOL) { Darkvater@1739: snprintf(tval, sizeof(tval), (val == 1) ? "on" : "off"); Darkvater@1739: tval2 = tval; Darkvater@1739: } Darkvater@1739: Darkvater@1805: IConsolePrintF(_icolour_warn, "'%s' changed to: %s", name, tval2); truelight@625: } truelight@625: } truelight@625: Darkvater@1739: void IConsoleGetPatchSetting(const char *name) truelight@625: { Darkvater@1739: char value[20]; Darkvater@1739: uint page, entry; Darkvater@1739: const PatchEntry *pe = IConsoleGetPatch(name, &page, &entry); truelight@625: Darkvater@1739: /* We did not find the patch setting */ Darkvater@1739: if (pe == NULL) { Darkvater@1805: IConsolePrintF(_icolour_warn, "'%s' is an unknown patch setting.", name); truelight@625: return; truelight@625: } truelight@625: Darkvater@1739: if (pe->type == PE_BOOL) { Darkvater@1739: snprintf(value, sizeof(value), (ReadPE(pe) == 1) ? "on" : "off"); tron@2639: } else { Darkvater@1739: snprintf(value, sizeof(value), "%d", ReadPE(pe)); tron@2639: } truelight@625: Darkvater@1805: IConsolePrintF(_icolour_warn, "Current value for '%s' is: '%s'", name, value); truelight@625: } truelight@625: truelight@0: static const Widget _patches_selection_widgets[] = { truelight@867: { WWT_CLOSEBOX, RESIZE_NONE, 10, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 10, 11, 369, 0, 13, STR_CONFIG_PATCHES_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS}, truelight@867: { WWT_PANEL, RESIZE_NONE, 10, 0, 369, 14, 41, 0x0, STR_NULL}, truelight@867: { WWT_PANEL, RESIZE_NONE, 10, 0, 369, 42, 320, 0x0, STR_NULL}, truelight@0: peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 3, 10, 96, 16, 27, STR_CONFIG_PATCHES_GUI, STR_NULL}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 3, 97, 183, 16, 27, STR_CONFIG_PATCHES_CONSTRUCTION, STR_NULL}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 3, 184, 270, 16, 27, STR_CONFIG_PATCHES_VEHICLES, STR_NULL}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 3, 271, 357, 16, 27, STR_CONFIG_PATCHES_STATIONS, STR_NULL}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 3, 10, 96, 28, 39, STR_CONFIG_PATCHES_ECONOMY, STR_NULL}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 3, 97, 183, 28, 39, STR_CONFIG_PATCHES_AI, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _patches_selection_desc = { truelight@0: WDP_CENTER, WDP_CENTER, 370, 321, truelight@0: WC_GAME_OPTIONS,0, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, truelight@0: _patches_selection_widgets, truelight@0: PatchesSelectionWndProc, truelight@0: }; truelight@0: tron@1093: void ShowPatchesSelection(void) truelight@0: { truelight@0: DeleteWindowById(WC_GAME_OPTIONS, 0); truelight@0: AllocateWindowDesc(&_patches_selection_desc); truelight@0: } dominik@478: darkvater@484: enum { dominik@502: NEWGRF_WND_PROC_OFFSET_TOP_WIDGET = 14, darkvater@484: NEWGRF_WND_PROC_ROWSIZE = 14 darkvater@484: }; dominik@478: dominik@478: static void NewgrfWndProc(Window *w, WindowEvent *e) dominik@478: { Darkvater@1741: static GRFFile *_sel_grffile; darkvater@484: switch (e->event) { dominik@478: case WE_PAINT: { dominik@502: int x, y = NEWGRF_WND_PROC_OFFSET_TOP_WIDGET; darkvater@484: uint16 i = 0; tron@1477: GRFFile *c = _first_grffile; dominik@478: dominik@478: DrawWindowWidgets(w); truelight@543: darkvater@484: if (_first_grffile == NULL) { // no grf sets installed dominik@502: DrawStringMultiCenter(140, 210, STR_NEWGRF_NO_FILES_INSTALLED, 250); dominik@478: break; dominik@478: } dominik@478: dominik@478: // draw list of all grf files darkvater@484: while (c != NULL) { darkvater@484: if (i >= w->vscroll.pos) { // draw files according to scrollbar position tron@2639: bool h = (_sel_grffile == c); dominik@502: // show highlighted item with a different background and highlighted text tron@2639: if (h) GfxFillRect(1, y + 1, 267, y + 12, 156); dominik@502: // XXX - will be grf name later truelight@543: DoDrawString(c->filename, 25, y + 2, h ? 0xC : 0x10); celestar@2187: DrawSprite(SPRITE_PALETTE(SPR_SQUARE | PALETTE_TO_RED), 5, y + 2); darkvater@484: y += NEWGRF_WND_PROC_ROWSIZE; dominik@478: } truelight@543: darkvater@484: c = c->next; darkvater@484: if (++i == w->vscroll.cap + w->vscroll.pos) break; // stop after displaying 12 items dominik@478: } dominik@478: dominik@478: // DoDrawString(_sel_grffile->setname, 120, 200, 0x01); // draw grf name dominik@478: darkvater@484: if (_sel_grffile == NULL) { // no grf file selected yet dominik@502: DrawStringMultiCenter(140, 210, STR_NEWGRF_TIP, 250); dominik@478: } else { dominik@478: // draw filename darkvater@484: x = DrawString(5, 199, STR_NEWGRF_FILENAME, 0); darkvater@484: DoDrawString(_sel_grffile->filename, x + 2, 199, 0x01); truelight@543: dominik@478: // draw grf id darkvater@484: x = DrawString(5, 209, STR_NEWGRF_GRF_ID, 0); tron@2201: snprintf(_userstring, lengthof(_userstring), "%08X", _sel_grffile->grfid); pasky@486: DrawString(x + 2, 209, STR_SPEC_USERSTRING, 0x01); dominik@478: } darkvater@484: } break; dominik@478: dominik@478: case WE_CLICK: dominik@478: switch(e->click.widget) { darkvater@982: case 3: { // select a grf file dominik@502: int y = (e->click.pt.y - NEWGRF_WND_PROC_OFFSET_TOP_WIDGET) / NEWGRF_WND_PROC_ROWSIZE; dominik@478: tron@2549: if (y >= w->vscroll.cap) return; // click out of bounds dominik@478: darkvater@484: y += w->vscroll.pos; darkvater@484: tron@2766: if (y >= w->vscroll.count) return; dominik@478: dominik@478: _sel_grffile = _first_grffile; darkvater@484: // get selected grf-file darkvater@484: while (y-- != 0) _sel_grffile = _sel_grffile->next; darkvater@484: dominik@478: SetWindowDirty(w); darkvater@484: } break; darkvater@484: case 9: /* Cancel button */ dominik@478: DeleteWindowById(WC_GAME_OPTIONS, 0); dominik@478: break; dominik@478: } break; dominik@478: dominik@478: /* Parameter edit box not used yet dominik@478: case WE_TIMEOUT: dominik@478: WP(w,def_d).data_2 = 0; dominik@478: SetWindowDirty(w); dominik@478: break; dominik@478: dominik@478: case WE_ON_EDIT_TEXT: { dominik@478: if (*e->edittext.str) { dominik@478: SetWindowDirty(w); dominik@478: } dominik@478: break; dominik@478: } dominik@478: */ dominik@478: case WE_DESTROY: dominik@502: _sel_grffile = NULL; dominik@478: DeleteWindowById(WC_QUERY_STRING, 0); dominik@478: break; dominik@478: } dominik@478: } dominik@478: dominik@478: static const Widget _newgrf_widgets[] = { truelight@867: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 14, 11, 279, 0, 13, STR_NEWGRF_SETTINGS_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS}, truelight@867: { WWT_PANEL, RESIZE_NONE, 14, 0, 279, 183, 276, 0x0, STR_NULL}, dominik@478: darkvater@982: { WWT_MATRIX, RESIZE_NONE, 14, 0, 267, 14, 182, 0xC01,/*small rows*/ STR_NEWGRF_TIP}, darkvater@893: { WWT_SCROLLBAR, RESIZE_NONE, 14, 268, 279, 14, 182, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, dominik@478: peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 147, 158, 244, 255, STR_0188, STR_NULL}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 159, 170, 244, 255, STR_0189, STR_NULL}, peter1138@2725: { WWT_TEXTBTN, RESIZE_NONE, 14, 175, 274, 244, 255, STR_NEWGRF_SET_PARAMETERS, STR_NULL}, darkvater@484: darkvater@1015: { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 5, 138, 261, 272, STR_NEWGRF_APPLY_CHANGES, STR_NULL}, darkvater@1015: { WWT_PUSHTXTBTN, RESIZE_NONE, 3, 142, 274, 261, 272, STR_012E_CANCEL, STR_NULL}, dominik@478: { WIDGETS_END}, dominik@478: }; dominik@478: dominik@478: static const WindowDesc _newgrf_desc = { dominik@502: WDP_CENTER, WDP_CENTER, 280, 277, dominik@478: WC_GAME_OPTIONS,0, darkvater@1015: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, dominik@478: _newgrf_widgets, dominik@478: NewgrfWndProc, dominik@478: }; dominik@478: tron@1093: void ShowNewgrf(void) dominik@478: { tron@2766: const GRFFile* c; dominik@478: Window *w; tron@2766: uint count; tron@2766: dominik@478: DeleteWindowById(WC_GAME_OPTIONS, 0); dominik@478: w = AllocateWindowDesc(&_newgrf_desc); dominik@478: tron@2766: count = 0; tron@2766: for (c = _first_grffile; c != NULL; c = c->next) count++; dominik@478: dominik@478: w->vscroll.cap = 12; tron@2766: w->vscroll.count = count; dominik@478: w->vscroll.pos = 0; dominik@478: w->disabled_state = (1 << 5) | (1 << 6) | (1 << 7); dominik@478: } dominik@478: dominik@759: /* state: 0 = none clicked, 0x01 = first clicked, 0x02 = second clicked */ dominik@759: void DrawArrowButtons(int x, int y, int state) dominik@759: { hackykid@1938: DrawFrameRect(x, y+1, x+9, y+9, 3, (state & 0x01) ? FR_LOWERED : 0); hackykid@1938: DrawFrameRect(x+10, y+1, x+19, y+9, 3, (state & 0x02) ? FR_LOWERED : 0); dominik@759: DrawStringCentered(x+5, y+1, STR_6819, 0); dominik@759: DrawStringCentered(x+15, y+1, STR_681A, 0); dominik@759: } dominik@759: tron@2307: static char _str_separator[2]; dominik@759: dominik@759: static void CustCurrencyWndProc(Window *w, WindowEvent *e) dominik@759: { dominik@759: switch (e->event) { dominik@759: case WE_PAINT: { dominik@759: int x=35, y=20, i=0; dominik@759: int clk = WP(w,def_d).data_1; dominik@759: DrawWindowWidgets(w); dominik@759: dominik@759: // exchange rate dominik@759: DrawArrowButtons(10, y, (clk >> (i*2)) & 0x03); dominik@759: SetDParam(0, 1); dominik@759: SetDParam(1, 1); dominik@759: DrawString(x, y + 1, STR_CURRENCY_EXCHANGE_RATE, 0); dominik@759: x = 35; dominik@759: y+=12; dominik@759: i++; dominik@759: dominik@759: // separator hackykid@1938: DrawFrameRect(10, y+1, 29, y+9, 0, ((clk >> (i*2)) & 0x03) ? FR_LOWERED : 0); dominik@759: x = DrawString(x, y + 1, STR_CURRENCY_SEPARATOR, 0); Darkvater@1759: DoDrawString(_str_separator, x + 4, y + 1, 6); dominik@759: x = 35; dominik@759: y+=12; dominik@759: i++; dominik@759: dominik@759: // prefix hackykid@1938: DrawFrameRect(10, y+1, 29, y+9, 0, ((clk >> (i*2)) & 0x03) ? FR_LOWERED : 0); dominik@759: x = DrawString(x, y + 1, STR_CURRENCY_PREFIX, 0); tron@2306: DoDrawString(_custom_currency.prefix, x + 4, y + 1, 6); dominik@759: x = 35; dominik@759: y+=12; dominik@759: i++; dominik@759: dominik@788: // suffix hackykid@1938: DrawFrameRect(10, y+1, 29, y+9, 0, ((clk >> (i*2)) & 0x03) ? FR_LOWERED : 0); dominik@788: x = DrawString(x, y + 1, STR_CURRENCY_SUFFIX, 0); tron@2306: DoDrawString(_custom_currency.suffix, x + 4, y + 1, 6); dominik@759: x = 35; dominik@759: y+=12; dominik@759: i++; dominik@759: dominik@759: // switch to euro dominik@759: DrawArrowButtons(10, y, (clk >> (i*2)) & 0x03); tron@2306: SetDParam(0, _custom_currency.to_euro); tron@2306: DrawString(x, y + 1, (_custom_currency.to_euro != CF_NOEURO) ? STR_CURRENCY_SWITCH_TO_EURO : STR_CURRENCY_SWITCH_TO_EURO_NEVER, 0); dominik@759: x = 35; dominik@759: y+=12; dominik@759: i++; dominik@759: dominik@759: // Preview dominik@759: y+=12; dominik@759: SetDParam(0, 10000); dominik@759: DrawString(x, y + 1, STR_CURRENCY_PREVIEW, 0); dominik@759: } break; dominik@759: dominik@759: case WE_CLICK: { dominik@759: bool edittext = false; dominik@759: int line = (e->click.pt.y - 20)/12; darkvater@776: int len = 0; dominik@759: int x = e->click.pt.x; darkvater@776: StringID str = 0; dominik@759: dominik@759: switch ( line ) { dominik@759: case 0: // rate dominik@759: if ( IS_INT_INSIDE(x, 10, 30) ) { // clicked buttons dominik@759: if (x < 20) { tron@2306: if (_custom_currency.rate > 1) _custom_currency.rate--; dominik@759: WP(w,def_d).data_1 = (1 << (line * 2 + 0)); dominik@759: } else { tron@2306: if (_custom_currency.rate < 5000) _custom_currency.rate++; dominik@759: WP(w,def_d).data_1 = (1 << (line * 2 + 1)); dominik@759: } dominik@759: } else { // enter text tron@2306: SetDParam(0, _custom_currency.rate); dominik@759: str = STR_CONFIG_PATCHES_INT32; dominik@759: len = 4; dominik@759: edittext = true; dominik@759: } dominik@759: break; dominik@759: case 1: // separator dominik@759: if ( IS_INT_INSIDE(x, 10, 30) ) // clicked button dominik@759: WP(w,def_d).data_1 = (1 << (line * 2 + 1)); ludde@2055: str = BindCString(_str_separator); dominik@759: len = 1; dominik@759: edittext = true; dominik@759: break; dominik@759: case 2: // prefix dominik@759: if ( IS_INT_INSIDE(x, 10, 30) ) // clicked button dominik@759: WP(w,def_d).data_1 = (1 << (line * 2 + 1)); tron@2306: str = BindCString(_custom_currency.prefix); dominik@759: len = 12; dominik@759: edittext = true; dominik@759: break; dominik@788: case 3: // suffix dominik@759: if ( IS_INT_INSIDE(x, 10, 30) ) // clicked button dominik@759: WP(w,def_d).data_1 = (1 << (line * 2 + 1)); tron@2306: str = BindCString(_custom_currency.suffix); dominik@759: len = 12; dominik@759: edittext = true; dominik@759: break; dominik@759: case 4: // to euro dominik@759: if ( IS_INT_INSIDE(x, 10, 30) ) { // clicked buttons dominik@759: if (x < 20) { tron@2306: _custom_currency.to_euro = (_custom_currency.to_euro <= 2000) ? tron@2306: CF_NOEURO : _custom_currency.to_euro - 1; dominik@759: WP(w,def_d).data_1 = (1 << (line * 2 + 0)); dominik@759: } else { tron@2306: _custom_currency.to_euro = tron@2306: clamp(_custom_currency.to_euro + 1, 2000, MAX_YEAR_END_REAL); dominik@759: WP(w,def_d).data_1 = (1 << (line * 2 + 1)); dominik@759: } dominik@759: } else { // enter text tron@2306: SetDParam(0, _custom_currency.to_euro); dominik@759: str = STR_CONFIG_PATCHES_INT32; dominik@759: len = 4; dominik@759: edittext = true; dominik@759: } dominik@759: break; dominik@759: } truelight@812: Darkvater@1756: if (edittext) { dominik@759: WP(w,def_d).data_2 = line; dominik@759: ShowQueryString( dominik@759: str, dominik@759: STR_CURRENCY_CHANGE_PARAMETER, Darkvater@1756: len + 1, // maximum number of characters OR dominik@759: 250, // characters up to this width pixels, whichever is satisfied first dominik@759: w->window_class, dominik@759: w->window_number); dominik@759: } truelight@812: dominik@759: w->flags4 |= 5 << WF_TIMEOUT_SHL; dominik@759: SetWindowDirty(w); dominik@759: } break; dominik@759: dominik@759: case WE_ON_EDIT_TEXT: { dominik@759: int val; tron@1323: const char *b = e->edittext.str; dominik@759: switch (WP(w,def_d).data_2) { Darkvater@1759: case 0: /* Exchange rate */ dominik@759: val = atoi(b); dominik@759: val = clamp(val, 1, 5000); tron@2306: _custom_currency.rate = val; tron@2639: break; tron@2639: Darkvater@1759: case 1: /* Thousands seperator */ tron@2306: _custom_currency.separator = (b[0] == '\0') ? ' ' : b[0]; tron@2307: ttd_strlcpy(_str_separator, b, lengthof(_str_separator)); tron@2639: break; tron@2639: Darkvater@1759: case 2: /* Currency prefix */ tron@2306: ttd_strlcpy(_custom_currency.prefix, b, lengthof(_custom_currency.prefix)); tron@2639: break; tron@2639: Darkvater@1759: case 3: /* Currency suffix */ tron@2306: ttd_strlcpy(_custom_currency.suffix, b, lengthof(_custom_currency.suffix)); tron@2639: break; tron@2639: Darkvater@1759: case 4: /* Year to switch to euro */ dominik@759: val = atoi(b); darkvater@970: val = clamp(val, 1999, MAX_YEAR_END_REAL); dominik@759: if (val == 1999) val = 0; tron@2306: _custom_currency.to_euro = val; tron@2639: break; dominik@759: } dominik@759: MarkWholeScreenDirty(); truelight@812: truelight@812: dominik@759: } break; dominik@759: dominik@759: case WE_TIMEOUT: dominik@759: WP(w,def_d).data_1 = 0; dominik@759: SetWindowDirty(w); dominik@759: break; dominik@759: dominik@759: case WE_DESTROY: dominik@759: DeleteWindowById(WC_QUERY_STRING, 0); dominik@759: MarkWholeScreenDirty(); dominik@759: break; dominik@759: } dominik@759: } dominik@759: dominik@759: static const Widget _cust_currency_widgets[] = { truelight@867: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 14, 11, 229, 0, 13, STR_CURRENCY_WINDOW, STR_018C_WINDOW_TITLE_DRAG_THIS}, truelight@867: { WWT_PANEL, RESIZE_NONE, 14, 0, 229, 14, 119, 0x0, STR_NULL}, dominik@759: { WIDGETS_END}, dominik@759: }; dominik@759: dominik@759: static const WindowDesc _cust_currency_desc = { dominik@759: WDP_CENTER, WDP_CENTER, 230, 120, dominik@759: WC_CUSTOM_CURRENCY, 0, dominik@759: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, dominik@759: _cust_currency_widgets, dominik@759: CustCurrencyWndProc, dominik@759: }; dominik@759: tron@2817: static void ShowCustCurrency(void) dominik@759: { tron@2306: _str_separator[0] = _custom_currency.separator; Darkvater@1759: _str_separator[1] = '\0'; dominik@759: dominik@759: DeleteWindowById(WC_CUSTOM_CURRENCY, 0); tron@2639: AllocateWindowDesc(&_cust_currency_desc); dominik@759: }