00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "heightmap.h"
00008 #include "debug.h"
00009 #include "landscape.h"
00010 #include "newgrf.h"
00011 #include "newgrf_text.h"
00012 #include "saveload.h"
00013 #include "tile_map.h"
00014 #include "gui.h"
00015 #include "window_gui.h"
00016 #include "station_gui.h"
00017 #include "textbuf_gui.h"
00018 #include "viewport_func.h"
00019 #include "gfx_func.h"
00020 #include "station.h"
00021 #include "command_func.h"
00022 #include "player_func.h"
00023 #include "player_base.h"
00024 #include "town.h"
00025 #include "network/network.h"
00026 #include "variables.h"
00027 #include "train.h"
00028 #include "tgp.h"
00029 #include "cargotype.h"
00030 #include "player_face.h"
00031 #include "strings_func.h"
00032 #include "fileio.h"
00033 #include "fios.h"
00034 #include "tile_cmd.h"
00035 #include "zoom_func.h"
00036 #include "functions.h"
00037 #include "window_func.h"
00038 #include "date_func.h"
00039 #include "sound_func.h"
00040 #include "string_func.h"
00041 #include "player_gui.h"
00042 #include "settings_type.h"
00043
00044 #include "table/sprites.h"
00045 #include "table/strings.h"
00046 #include "table/tree_land.h"
00047
00048
00049 FiosItem *_fios_list;
00050 SaveLoadDialogMode _saveload_mode;
00051
00052
00053 static bool _fios_path_changed;
00054 static bool _savegame_sort_dirty;
00055
00056 enum {
00057 LAND_INFO_LINES = 7,
00058 LAND_INFO_LINE_BUFF_SIZE = 512,
00059 };
00060
00061 static char _landinfo_data[LAND_INFO_LINES][LAND_INFO_LINE_BUFF_SIZE];
00062
00063 static void LandInfoWndProc(Window *w, WindowEvent *e)
00064 {
00065 if (e->event == WE_PAINT) {
00066 DrawWindowWidgets(w);
00067
00068 DoDrawStringCentered(140, 16, _landinfo_data[0], TC_LIGHT_BLUE);
00069 DoDrawStringCentered(140, 27, _landinfo_data[1], TC_FROMSTRING);
00070 DoDrawStringCentered(140, 38, _landinfo_data[2], TC_FROMSTRING);
00071 DoDrawStringCentered(140, 49, _landinfo_data[3], TC_FROMSTRING);
00072 DoDrawStringCentered(140, 60, _landinfo_data[4], TC_FROMSTRING);
00073 if (_landinfo_data[5][0] != '\0') DrawStringMultiCenter(140, 76, BindCString(_landinfo_data[5]), w->width - 4);
00074 if (_landinfo_data[6][0] != '\0') DoDrawStringCentered(140, 71, _landinfo_data[6], TC_FROMSTRING);
00075 }
00076 }
00077
00078 static const Widget _land_info_widgets[] = {
00079 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00080 { WWT_CAPTION, RESIZE_NONE, 14, 11, 279, 0, 13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
00081 { WWT_PANEL, RESIZE_NONE, 14, 0, 279, 14, 92, 0x0, STR_NULL},
00082 { WIDGETS_END},
00083 };
00084
00085 static const WindowDesc _land_info_desc = {
00086 WDP_AUTO, WDP_AUTO, 280, 93, 280, 93,
00087 WC_LAND_INFO, WC_NONE,
00088 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
00089 _land_info_widgets,
00090 LandInfoWndProc
00091 };
00092
00093 static void Place_LandInfo(TileIndex tile)
00094 {
00095 Player *p;
00096 Window *w;
00097 Town *t;
00098 Money old_money;
00099 CommandCost costclear;
00100 AcceptedCargo ac;
00101 TileDesc td;
00102 StringID str;
00103
00104 DeleteWindowById(WC_LAND_INFO, 0);
00105
00106 w = AllocateWindowDesc(&_land_info_desc);
00107 WP(w, void_d).data = &_landinfo_data;
00108
00109 p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : PLAYER_FIRST);
00110 t = ClosestTownFromTile(tile, _patches.dist_local_authority);
00111
00112 old_money = p->player_money;
00113 p->player_money = INT64_MAX;
00114 costclear = DoCommand(tile, 0, 0, 0, CMD_LANDSCAPE_CLEAR);
00115 p->player_money = old_money;
00116
00117
00118 td.build_date = 0;
00119 GetAcceptedCargo(tile, ac);
00120 GetTileDesc(tile, &td);
00121
00122 SetDParam(0, td.dparam[0]);
00123 GetString(_landinfo_data[0], td.str, lastof(_landinfo_data[0]));
00124
00125 SetDParam(0, STR_01A6_N_A);
00126 if (td.owner != OWNER_NONE && td.owner != OWNER_WATER) GetNameOfOwner(td.owner, tile);
00127 GetString(_landinfo_data[1], STR_01A7_OWNER, lastof(_landinfo_data[1]));
00128
00129 str = STR_01A4_COST_TO_CLEAR_N_A;
00130 if (CmdSucceeded(costclear)) {
00131 SetDParam(0, costclear.GetCost());
00132 str = STR_01A5_COST_TO_CLEAR;
00133 }
00134 GetString(_landinfo_data[2], str, lastof(_landinfo_data[2]));
00135
00136 snprintf(_userstring, lengthof(_userstring), "0x%.4X", tile);
00137 SetDParam(0, TileX(tile));
00138 SetDParam(1, TileY(tile));
00139 SetDParam(2, TileHeight(tile));
00140 SetDParam(3, STR_SPEC_USERSTRING);
00141 GetString(_landinfo_data[3], STR_LANDINFO_COORDS, lastof(_landinfo_data[3]));
00142
00143 SetDParam(0, STR_01A9_NONE);
00144 if (t != NULL && t->IsValid()) {
00145 SetDParam(0, STR_TOWN);
00146 SetDParam(1, t->index);
00147 }
00148 GetString(_landinfo_data[4], STR_01A8_LOCAL_AUTHORITY, lastof(_landinfo_data[4]));
00149
00150 {
00151 char *p = GetString(_landinfo_data[5], STR_01CE_CARGO_ACCEPTED, lastof(_landinfo_data[5]));
00152 bool found = false;
00153
00154 for (CargoID i = 0; i < NUM_CARGO; ++i) {
00155 if (ac[i] > 0) {
00156
00157 if (found) {
00158 *p++ = ',';
00159 *p++ = ' ';
00160 }
00161 found = true;
00162
00163
00164 if (ac[i] < 8) {
00165 SetDParam(0, ac[i]);
00166 SetDParam(1, GetCargo(i)->name);
00167 p = GetString(p, STR_01D1_8, lastof(_landinfo_data[5]));
00168 } else {
00169 p = GetString(p, GetCargo(i)->name, lastof(_landinfo_data[5]));
00170 }
00171 }
00172 }
00173
00174 if (!found) _landinfo_data[5][0] = '\0';
00175 }
00176
00177 if (td.build_date != 0) {
00178 SetDParam(0, td.build_date);
00179 GetString(_landinfo_data[6], STR_BUILD_DATE, lastof(_landinfo_data[6]));
00180 } else {
00181 _landinfo_data[6][0] = '\0';
00182 }
00183
00184 #if defined(_DEBUG)
00185 # define LANDINFOD_LEVEL 0
00186 #else
00187 # define LANDINFOD_LEVEL 1
00188 #endif
00189 DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile));
00190 DEBUG(misc, LANDINFOD_LEVEL, "type_height = %#x", _m[tile].type_height);
00191 DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", _m[tile].m1);
00192 DEBUG(misc, LANDINFOD_LEVEL, "m2 = %#x", _m[tile].m2);
00193 DEBUG(misc, LANDINFOD_LEVEL, "m3 = %#x", _m[tile].m3);
00194 DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", _m[tile].m4);
00195 DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", _m[tile].m5);
00196 DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", _m[tile].m6);
00197 DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", _me[tile].m7);
00198 #undef LANDINFOD_LEVEL
00199 }
00200
00201 void PlaceLandBlockInfo()
00202 {
00203 if (_cursor.sprite == SPR_CURSOR_QUERY) {
00204 ResetObjectToPlace();
00205 } else {
00206 _place_proc = Place_LandInfo;
00207 SetObjectToPlace(SPR_CURSOR_QUERY, PAL_NONE, VHM_RECT, WC_MAIN_TOOLBAR, 0);
00208 }
00209 }
00210
00211 static const char *credits[] = {
00212
00213
00214 "Original design by Chris Sawyer",
00215 "Original graphics by Simon Foster",
00216 "",
00217 "The OpenTTD team (in alphabetical order):",
00218 " Jean-Francois Claeys (Belugas) - GUI, newindustries and more",
00219 " Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles",
00220 " Matthijs Kooijman (blathijs) - Pathfinder-guru, pool rework",
00221 " Loïc Guilloux (glx) - General coding",
00222 " Christoph Elsenhans (frosch) - General coding",
00223 " Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;)",
00224 " Jonathan Coome (Maedhros) - High priest of the newGRF Temple",
00225 " Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host",
00226 " Owen Rudge (orudge) - Forum host, OS/2 port",
00227 " Peter Nelson (peter1138) - Spiritual descendant from newGRF gods",
00228 " Remko Bijker (Rubidium) - Lead coder and way more",
00229 " Benedikt Brüggemeier (skidd13) - Bug fixer and code reworker",
00230 " Zdenek Sojka (SmatZ) - Bug finder and fixer",
00231 "",
00232 "Inactive Developers:",
00233 " Victor Fischer (Celestar) - Programming everywhere you need him to",
00234 " Tamás Faragó (Darkvater) - Ex-Lead coder",
00235 " Christoph Mallon (Tron) - Programmer, code correctness police",
00236 "",
00237 "Retired Developers:",
00238 " Ludvig Strigeus (ludde) - OpenTTD author, main coder (0.1 - 0.3.3)",
00239 " Serge Paquet (vurlix) - Assistant project manager, coder (0.1 - 0.3.3)",
00240 " Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3.0 - 0.3.6)",
00241 " Patric Stout (TrueLight) - Programmer, webhoster (0.3 - pre0.6)",
00242 "",
00243 "Special thanks go out to:",
00244 " Josef Drexler - For his great work on TTDPatch",
00245 " Marcin Grzegorczyk - For his documentation of TTD internals",
00246 " Petr Baudis (pasky) - Many patches, newGRF support",
00247 " Stefan Meißner (sign_de) - For his work on the console",
00248 " Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with",
00249 " Cian Duffy (MYOB) - BeOS port / manual writing",
00250 " Christian Rosentreter (tokai) - MorphOS / AmigaOS port",
00251 " Richard Kempton (richK) - additional airports, initial TGP implementation",
00252 "",
00253 " Michael Blunck - Pre-Signals and Semaphores © 2003",
00254 " George - Canal/Lock graphics © 2003-2004",
00255 " David Dallaston - Tram tracks",
00256 " Marcin Grzegorczyk - Foundations for Tracks on Slopes",
00257 " All Translators - Who made OpenTTD a truly international game",
00258 " Bug Reporters - Without whom OpenTTD would still be full of bugs!",
00259 "",
00260 "",
00261 "And last but not least:",
00262 " Chris Sawyer - For an amazing game!"
00263 };
00264
00265 static void AboutWindowProc(Window *w, WindowEvent *e)
00266 {
00267 switch (e->event) {
00268 case WE_CREATE:
00269 WP(w, scroller_d).counter = 5;
00270 WP(w, scroller_d).height = w->height - 40;
00271 break;
00272 case WE_PAINT: {
00273 uint i;
00274 int y = WP(w, scroller_d).height;
00275 DrawWindowWidgets(w);
00276
00277
00278 DrawStringCentered(210, 17, STR_00B6_ORIGINAL_COPYRIGHT, TC_FROMSTRING);
00279 DrawStringCentered(210, 17 + 10, STR_00B7_VERSION, TC_FROMSTRING);
00280
00281
00282 for (i = 0; i < lengthof(credits); i++) {
00283 if (y >= 50 && y < (w->height - 40)) {
00284 DoDrawString(credits[i], 10, y, TC_BLACK);
00285 }
00286 y += 10;
00287 }
00288
00289
00290 if (y < 50) WP(w, scroller_d).height = w->height - 40;
00291
00292 DoDrawStringCentered(210, w->height - 25, "Website: http://www.openttd.org", TC_BLACK);
00293 DrawStringCentered(210, w->height - 15, STR_00BA_COPYRIGHT_OPENTTD, TC_FROMSTRING);
00294 } break;
00295 case WE_TICK:
00296 if (--WP(w, scroller_d).counter == 0) {
00297 WP(w, scroller_d).counter = 5;
00298 WP(w, scroller_d).height--;
00299 SetWindowDirty(w);
00300 }
00301 break;
00302 }
00303 }
00304
00305 static const Widget _about_widgets[] = {
00306 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00307 { WWT_CAPTION, RESIZE_NONE, 14, 11, 419, 0, 13, STR_015B_OPENTTD, STR_NULL},
00308 { WWT_PANEL, RESIZE_NONE, 14, 0, 419, 14, 271, 0x0, STR_NULL},
00309 { WWT_FRAME, RESIZE_NONE, 14, 5, 414, 40, 245, STR_NULL, STR_NULL},
00310 { WIDGETS_END},
00311 };
00312
00313 static const WindowDesc _about_desc = {
00314 WDP_CENTER, WDP_CENTER, 420, 272, 420, 272,
00315 WC_GAME_OPTIONS, WC_NONE,
00316 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
00317 _about_widgets,
00318 AboutWindowProc
00319 };
00320
00321
00322 void ShowAboutWindow()
00323 {
00324 DeleteWindowById(WC_GAME_OPTIONS, 0);
00325 AllocateWindowDesc(&_about_desc);
00326 }
00327
00328 static int _tree_to_plant;
00329
00330 static const PalSpriteID _tree_sprites[] = {
00331 { 0x655, PAL_NONE }, { 0x663, PAL_NONE }, { 0x678, PAL_NONE }, { 0x62B, PAL_NONE },
00332 { 0x647, PAL_NONE }, { 0x639, PAL_NONE }, { 0x64E, PAL_NONE }, { 0x632, PAL_NONE },
00333 { 0x67F, PAL_NONE }, { 0x68D, PAL_NONE }, { 0x69B, PAL_NONE }, { 0x6A9, PAL_NONE },
00334 { 0x6AF, PAL_NONE }, { 0x6D2, PAL_NONE }, { 0x6D9, PAL_NONE }, { 0x6C4, PAL_NONE },
00335 { 0x6CB, PAL_NONE }, { 0x6B6, PAL_NONE }, { 0x6BD, PAL_NONE }, { 0x6E0, PAL_NONE },
00336 { 0x72E, PAL_NONE }, { 0x734, PAL_NONE }, { 0x74A, PAL_NONE }, { 0x74F, PAL_NONE },
00337 { 0x76B, PAL_NONE }, { 0x78F, PAL_NONE }, { 0x788, PAL_NONE }, { 0x77B, PAL_NONE },
00338 { 0x75F, PAL_NONE }, { 0x774, PAL_NONE }, { 0x720, PAL_NONE }, { 0x797, PAL_NONE },
00339 { 0x79E, PAL_NONE }, { 0x7A5, PALETTE_TO_GREEN }, { 0x7AC, PALETTE_TO_RED }, { 0x7B3, PAL_NONE },
00340 { 0x7BA, PAL_NONE }, { 0x7C1, PALETTE_TO_RED, }, { 0x7C8, PALETTE_TO_PALE_GREEN }, { 0x7CF, PALETTE_TO_YELLOW }, { 0x7D6, PALETTE_TO_RED }
00341 };
00342
00343 static void BuildTreesWndProc(Window *w, WindowEvent *e)
00344 {
00345 switch (e->event) {
00346 case WE_PAINT: {
00347 int x,y;
00348 int i, count;
00349
00350 DrawWindowWidgets(w);
00351
00352 WP(w, tree_d).base = i = _tree_base_by_landscape[_opt.landscape];
00353 WP(w, tree_d).count = count = _tree_count_by_landscape[_opt.landscape];
00354
00355 x = 18;
00356 y = 54;
00357 do {
00358 DrawSprite(_tree_sprites[i].sprite, _tree_sprites[i].pal, x, y);
00359 x += 35;
00360 if (!(++i & 3)) {
00361 x -= 35 * 4;
00362 y += 47;
00363 }
00364 } while (--count);
00365 } break;
00366
00367 case WE_CLICK: {
00368 int wid = e->we.click.widget;
00369
00370 switch (wid) {
00371 case 0:
00372 ResetObjectToPlace();
00373 break;
00374
00375 case 3: case 4: case 5: case 6:
00376 case 7: case 8: case 9: case 10:
00377 case 11:case 12: case 13: case 14:
00378 if (wid - 3 >= WP(w, tree_d).count) break;
00379
00380 if (HandlePlacePushButton(w, wid, SPR_CURSOR_TREE, VHM_RECT, NULL))
00381 _tree_to_plant = WP(w, tree_d).base + wid - 3;
00382 break;
00383
00384 case 15:
00385 if (HandlePlacePushButton(w, 15, SPR_CURSOR_TREE, VHM_RECT, NULL))
00386 _tree_to_plant = -1;
00387 break;
00388
00389 case 16:
00390 w->LowerWidget(16);
00391 w->flags4 |= 5 << WF_TIMEOUT_SHL;
00392 SndPlayFx(SND_15_BEEP);
00393 PlaceTreesRandomly();
00394 MarkWholeScreenDirty();
00395 break;
00396 }
00397 } break;
00398
00399 case WE_PLACE_OBJ:
00400 VpStartPlaceSizing(e->we.place.tile, VPM_X_AND_Y_LIMITED, DDSP_PLANT_TREES);
00401 VpSetPlaceSizingLimit(20);
00402 break;
00403
00404 case WE_PLACE_DRAG:
00405 VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method);
00406 return;
00407
00408 case WE_PLACE_MOUSEUP:
00409 if (e->we.place.pt.x != -1 && e->we.place.select_proc == DDSP_PLANT_TREES) {
00410 DoCommandP(e->we.place.tile, _tree_to_plant, e->we.place.starttile, NULL,
00411 CMD_PLANT_TREE | CMD_MSG(STR_2805_CAN_T_PLANT_TREE_HERE));
00412 }
00413 break;
00414
00415 case WE_TIMEOUT:
00416 w->RaiseWidget(16);
00417 break;
00418
00419 case WE_ABORT_PLACE_OBJ:
00420 w->RaiseButtons();
00421 break;
00422 }
00423 }
00424
00425 static const Widget _build_trees_widgets[] = {
00426 { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00427 { WWT_CAPTION, RESIZE_NONE, 7, 11, 142, 0, 13, STR_2802_TREES, STR_018C_WINDOW_TITLE_DRAG_THIS},
00428 { WWT_PANEL, RESIZE_NONE, 7, 0, 142, 14, 170, 0x0, STR_NULL},
00429 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00430 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00431 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00432 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00433 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00434 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00435 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00436 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00437 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00438 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00439 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00440 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00441 { WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 157, 168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP},
00442 { WIDGETS_END},
00443 };
00444
00445 static const WindowDesc _build_trees_desc = {
00446 497, 22, 143, 171, 143, 171,
00447 WC_BUILD_TREES, WC_SCEN_LAND_GEN,
00448 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
00449 _build_trees_widgets,
00450 BuildTreesWndProc
00451 };
00452
00453 static const Widget _build_trees_scen_widgets[] = {
00454 { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00455 { WWT_CAPTION, RESIZE_NONE, 7, 11, 142, 0, 13, STR_2802_TREES, STR_018C_WINDOW_TITLE_DRAG_THIS},
00456 { WWT_PANEL, RESIZE_NONE, 7, 0, 142, 14, 183, 0x0, STR_NULL},
00457 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00458 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00459 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00460 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00461 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00462 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00463 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00464 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00465 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00466 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00467 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00468 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT},
00469 { WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 157, 168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP},
00470 { WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 170, 181, STR_028A_RANDOM_TREES, STR_028B_PLANT_TREES_RANDOMLY_OVER},
00471 { WIDGETS_END},
00472 };
00473
00474 static const WindowDesc _build_trees_scen_desc = {
00475 WDP_AUTO, WDP_AUTO, 143, 184, 143, 184,
00476 WC_BUILD_TREES, WC_NONE,
00477 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
00478 _build_trees_scen_widgets,
00479 BuildTreesWndProc
00480 };
00481
00482
00483 void ShowBuildTreesToolbar()
00484 {
00485 if (!IsValidPlayer(_current_player)) return;
00486 AllocateWindowDescFront(&_build_trees_desc, 0);
00487 }
00488
00489 void ShowBuildTreesScenToolbar()
00490 {
00491 AllocateWindowDescFront(&_build_trees_scen_desc, 0);
00492 }
00493
00494 static uint64 _errmsg_decode_params[20];
00495 static StringID _errmsg_message_1, _errmsg_message_2;
00496 static uint _errmsg_duration;
00497
00498
00499 static const Widget _errmsg_widgets[] = {
00500 { WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00501 { WWT_CAPTION, RESIZE_NONE, 4, 11, 239, 0, 13, STR_00B2_MESSAGE, STR_NULL},
00502 { WWT_PANEL, RESIZE_NONE, 4, 0, 239, 14, 45, 0x0, STR_NULL},
00503 { WIDGETS_END},
00504 };
00505
00506 static const Widget _errmsg_face_widgets[] = {
00507 { WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00508 { WWT_CAPTION, RESIZE_NONE, 4, 11, 333, 0, 13, STR_00B3_MESSAGE_FROM, STR_NULL},
00509 { WWT_PANEL, RESIZE_NONE, 4, 0, 333, 14, 136, 0x0, STR_NULL},
00510 { WIDGETS_END},
00511 };
00512
00513 static void ErrmsgWndProc(Window *w, WindowEvent *e)
00514 {
00515 switch (e->event) {
00516 case WE_PAINT:
00517 CopyInDParam(0, _errmsg_decode_params, lengthof(_errmsg_decode_params));
00518 DrawWindowWidgets(w);
00519 CopyInDParam(0, _errmsg_decode_params, lengthof(_errmsg_decode_params));
00520
00521
00522
00523
00524 SwitchToErrorRefStack();
00525 RewindTextRefStack();
00526
00527 if (!IsWindowOfPrototype(w, _errmsg_face_widgets)) {
00528 DrawStringMultiCenter(
00529 120,
00530 (_errmsg_message_1 == INVALID_STRING_ID ? 25 : 15),
00531 _errmsg_message_2,
00532 w->width - 2);
00533 if (_errmsg_message_1 != INVALID_STRING_ID)
00534 DrawStringMultiCenter(
00535 120,
00536 30,
00537 _errmsg_message_1,
00538 w->width - 2);
00539 } else {
00540 const Player *p = GetPlayer((PlayerID)GetDParamX(_errmsg_decode_params,2));
00541 DrawPlayerFace(p->face, p->player_color, 2, 16);
00542
00543 DrawStringMultiCenter(
00544 214,
00545 (_errmsg_message_1 == INVALID_STRING_ID ? 65 : 45),
00546 _errmsg_message_2,
00547 w->width - 2);
00548 if (_errmsg_message_1 != INVALID_STRING_ID)
00549 DrawStringMultiCenter(
00550 214,
00551 90,
00552 _errmsg_message_1,
00553 w->width - 2);
00554 }
00555
00556
00557 SwitchToNormalRefStack();
00558 break;
00559
00560 case WE_MOUSELOOP:
00561 if (_right_button_down) DeleteWindow(w);
00562 break;
00563
00564 case WE_4:
00565 if (--_errmsg_duration == 0) DeleteWindow(w);
00566 break;
00567
00568 case WE_DESTROY:
00569 SetRedErrorSquare(0);
00570 _switch_mode_errorstr = INVALID_STRING_ID;
00571 break;
00572
00573 case WE_KEYPRESS:
00574 if (e->we.keypress.keycode == WKC_SPACE) {
00575
00576 e->we.keypress.cont = false;
00577 DeleteWindow(w);
00578 }
00579 break;
00580 }
00581 }
00582
00583 void ShowErrorMessage(StringID msg_1, StringID msg_2, int x, int y)
00584 {
00585 Window *w;
00586 const ViewPort *vp;
00587 Point pt;
00588
00589 DeleteWindowById(WC_ERRMSG, 0);
00590
00591
00592 if (msg_2 == 0) msg_2 = STR_EMPTY;
00593
00594 _errmsg_message_1 = msg_1;
00595 _errmsg_message_2 = msg_2;
00596 CopyOutDParam(_errmsg_decode_params, 0, lengthof(_errmsg_decode_params));
00597 _errmsg_duration = _patches.errmsg_duration;
00598 if (!_errmsg_duration) return;
00599
00600 if (_errmsg_message_1 != STR_013B_OWNED_BY || GetDParamX(_errmsg_decode_params,2) >= 8) {
00601
00602 if ( (x|y) != 0) {
00603 pt = RemapCoords2(x, y);
00604 vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00605
00606
00607 pt.x = UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left;
00608 pt.x = (pt.x < (_screen.width >> 1)) ? _screen.width - 260 : 20;
00609
00610
00611 pt.y = UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top;
00612 pt.y = (pt.y < (_screen.height >> 1)) ? _screen.height - 80 : 100;
00613
00614 } else {
00615 pt.x = (_screen.width - 240) >> 1;
00616 pt.y = (_screen.height - 46) >> 1;
00617 }
00618 w = AllocateWindow(pt.x, pt.y, 240, 46, ErrmsgWndProc, WC_ERRMSG, _errmsg_widgets);
00619 } else {
00620 if ( (x|y) != 0) {
00621 pt = RemapCoords2(x, y);
00622 vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00623 pt.x = Clamp(UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left - (334/2), 0, _screen.width - 334);
00624 pt.y = Clamp(UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top - (137/2), 22, _screen.height - 137);
00625 } else {
00626 pt.x = (_screen.width - 334) >> 1;
00627 pt.y = (_screen.height - 137) >> 1;
00628 }
00629 w = AllocateWindow(pt.x, pt.y, 334, 137, ErrmsgWndProc, WC_ERRMSG, _errmsg_face_widgets);
00630 }
00631
00632 w->desc_flags = WDF_STD_BTN | WDF_DEF_WIDGET;
00633 }
00634
00635
00636 void ShowEstimatedCostOrIncome(Money cost, int x, int y)
00637 {
00638 StringID msg = STR_0805_ESTIMATED_COST;
00639
00640 if (cost < 0) {
00641 cost = -cost;
00642 msg = STR_0807_ESTIMATED_INCOME;
00643 }
00644 SetDParam(0, cost);
00645 ShowErrorMessage(INVALID_STRING_ID, msg, x, y);
00646 }
00647
00648 void ShowCostOrIncomeAnimation(int x, int y, int z, Money cost)
00649 {
00650 StringID msg;
00651 Point pt = RemapCoords(x,y,z);
00652
00653 msg = STR_0801_COST;
00654 if (cost < 0) {
00655 cost = -cost;
00656 msg = STR_0803_INCOME;
00657 }
00658 SetDParam(0, cost);
00659 AddTextEffect(msg, pt.x, pt.y, 0x250, TE_RISING);
00660 }
00661
00662 void ShowFeederIncomeAnimation(int x, int y, int z, Money cost)
00663 {
00664 Point pt = RemapCoords(x,y,z);
00665
00666 SetDParam(0, cost);
00667 AddTextEffect(STR_FEEDER, pt.x, pt.y, 0x250, TE_RISING);
00668 }
00669
00670 TextEffectID ShowFillingPercent(int x, int y, int z, uint8 percent, StringID string)
00671 {
00672 Point pt = RemapCoords(x, y, z);
00673
00674 assert(string != STR_NULL);
00675
00676 SetDParam(0, percent);
00677 return AddTextEffect(string, pt.x, pt.y, 0xFFFF, TE_STATIC);
00678 }
00679
00680 void UpdateFillingPercent(TextEffectID te_id, uint8 percent, StringID string)
00681 {
00682 assert(string != STR_NULL);
00683
00684 SetDParam(0, percent);
00685 UpdateTextEffect(te_id, string);
00686 }
00687
00688 void HideFillingPercent(TextEffectID *te_id)
00689 {
00690 if (*te_id == INVALID_TE_ID) return;
00691
00692 RemoveTextEffect(*te_id);
00693 *te_id = INVALID_TE_ID;
00694 }
00695
00696 static const Widget _tooltips_widgets[] = {
00697 { WWT_PANEL, RESIZE_NONE, 14, 0, 199, 0, 31, 0x0, STR_NULL},
00698 { WIDGETS_END},
00699 };
00700
00701
00702 static void TooltipsWndProc(Window *w, WindowEvent *e)
00703 {
00704 switch (e->event) {
00705 case WE_PAINT: {
00706 uint arg;
00707 GfxFillRect(0, 0, w->width - 1, w->height - 1, 0);
00708 GfxFillRect(1, 1, w->width - 2, w->height - 2, 0x44);
00709
00710 for (arg = 0; arg < WP(w, tooltips_d).paramcount; arg++) {
00711 SetDParam(arg, WP(w, tooltips_d).params[arg]);
00712 }
00713 DrawStringMultiCenter((w->width >> 1), (w->height >> 1) - 5, WP(w, tooltips_d).string_id, w->width - 2);
00714 break;
00715 }
00716
00717 case WE_MOUSELOOP:
00718
00719
00720 if (WP(w, tooltips_d).paramcount == 0 ) {
00721 if (!_right_button_down) DeleteWindow(w);
00722 } else {
00723 if (!_left_button_down) DeleteWindow(w);
00724 }
00725
00726 break;
00727 }
00728 }
00729
00735 void GuiShowTooltipsWithArgs(StringID str, uint paramcount, const uint64 params[])
00736 {
00737 char buffer[512];
00738 Dimension br;
00739 Window *w;
00740 uint i;
00741 int x, y;
00742
00743 DeleteWindowById(WC_TOOLTIPS, 0);
00744
00745
00746 if (str == STR_NULL || (paramcount != 0 && !_patches.measure_tooltip)) return;
00747
00748 for (i = 0; i != paramcount; i++) SetDParam(i, params[i]);
00749 GetString(buffer, str, lastof(buffer));
00750
00751 br = GetStringBoundingBox(buffer);
00752 br.width += 6; br.height += 4;
00753
00754
00755 if (br.width > 200) {
00756 br.height += ((br.width - 4) / 176) * 10;
00757 br.width = 200;
00758 }
00759
00760
00761
00762
00763 y = Clamp(_cursor.pos.y + _cursor.size.y + _cursor.offs.y + 5, 22, _screen.height - 12);
00764 if (y + br.height > _screen.height - 12) y = _cursor.pos.y + _cursor.offs.y - br.height - 5;
00765 x = Clamp(_cursor.pos.x - (br.width >> 1), 0, _screen.width - br.width);
00766
00767 w = AllocateWindow(x, y, br.width, br.height, TooltipsWndProc, WC_TOOLTIPS, _tooltips_widgets);
00768
00769 WP(w, tooltips_d).string_id = str;
00770 assert(sizeof(WP(w, tooltips_d).params[0]) == sizeof(params[0]));
00771 memcpy(WP(w, tooltips_d).params, params, sizeof(WP(w, tooltips_d).params[0]) * paramcount);
00772 WP(w, tooltips_d).paramcount = paramcount;
00773
00774 w->flags4 &= ~WF_WHITE_BORDER_MASK;
00775 w->widget[0].right = br.width;
00776 w->widget[0].bottom = br.height;
00777 }
00778
00779
00780 static int DrawStationCoverageText(const AcceptedCargo accepts,
00781 int str_x, int str_y, StationCoverageType sct)
00782 {
00783 char *b = _userstring;
00784 bool first = true;
00785
00786 b = InlineString(b, STR_000D_ACCEPTS);
00787
00788 for (CargoID i = 0; i < NUM_CARGO; i++) {
00789 if (b >= lastof(_userstring) - (1 + 2 * 4)) break;
00790 switch (sct) {
00791 case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00792 case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00793 case SCT_ALL: break;
00794 default: NOT_REACHED();
00795 }
00796 if (accepts[i] >= 8) {
00797 if (first) {
00798 first = false;
00799 } else {
00800
00801 *b++ = ',';
00802 *b++ = ' ';
00803 }
00804 b = InlineString(b, GetCargo(i)->name);
00805 }
00806 }
00807
00808
00809 if (first) b = InlineString(b, STR_00D0_NOTHING);
00810
00811 *b = '\0';
00812
00813
00814 assert(b < endof(_userstring));
00815
00816 return DrawStringMultiLine(str_x, str_y, STR_SPEC_USERSTRING, 144);
00817 }
00818
00819 int DrawStationCoverageAreaText(int sx, int sy, StationCoverageType sct, int rad)
00820 {
00821 TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
00822 AcceptedCargo accepts;
00823 if (tile < MapSize()) {
00824 GetAcceptanceAroundTiles(accepts, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad);
00825 return sy + DrawStationCoverageText(accepts, sx, sy, sct);
00826 }
00827
00828 return sy;
00829 }
00830
00831 void CheckRedrawStationCoverage(const Window *w)
00832 {
00833 if (_thd.dirty & 1) {
00834 _thd.dirty &= ~1;
00835 SetWindowDirty(w);
00836 }
00837 }
00838
00839 void SetVScrollCount(Window *w, int num)
00840 {
00841 w->vscroll.count = num;
00842 num -= w->vscroll.cap;
00843 if (num < 0) num = 0;
00844 if (num < w->vscroll.pos) w->vscroll.pos = num;
00845 }
00846
00847 void SetVScroll2Count(Window *w, int num)
00848 {
00849 w->vscroll2.count = num;
00850 num -= w->vscroll2.cap;
00851 if (num < 0) num = 0;
00852 if (num < w->vscroll2.pos) w->vscroll2.pos = num;
00853 }
00854
00855 void SetHScrollCount(Window *w, int num)
00856 {
00857 w->hscroll.count = num;
00858 num -= w->hscroll.cap;
00859 if (num < 0) num = 0;
00860 if (num < w->hscroll.pos) w->hscroll.pos = num;
00861 }
00862
00863
00864
00865
00866 static void DelChar(Textbuf *tb, bool backspace)
00867 {
00868 WChar c;
00869 uint width;
00870 size_t len;
00871 char *s = tb->buf + tb->caretpos;
00872
00873 if (backspace) s = Utf8PrevChar(s);
00874
00875 len = Utf8Decode(&c, s);
00876 width = GetCharacterWidth(FS_NORMAL, c);
00877
00878 tb->width -= width;
00879 if (backspace) {
00880 tb->caretpos -= len;
00881 tb->caretxoffs -= width;
00882 }
00883
00884
00885 memmove(s, s + len, tb->length - (s - tb->buf) - len + 1);
00886 tb->length -= len;
00887 }
00888
00896 bool DeleteTextBufferChar(Textbuf *tb, int delmode)
00897 {
00898 if (delmode == WKC_BACKSPACE && tb->caretpos != 0) {
00899 DelChar(tb, true);
00900 return true;
00901 } else if (delmode == WKC_DELETE && tb->caretpos < tb->length) {
00902 DelChar(tb, false);
00903 return true;
00904 }
00905
00906 return false;
00907 }
00908
00913 void DeleteTextBufferAll(Textbuf *tb)
00914 {
00915 memset(tb->buf, 0, tb->maxlength);
00916 tb->length = tb->width = 0;
00917 tb->caretpos = tb->caretxoffs = 0;
00918 }
00919
00928 bool InsertTextBufferChar(Textbuf *tb, WChar key)
00929 {
00930 const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
00931 size_t len = Utf8CharLen(key);
00932 if (tb->length < (tb->maxlength - len) && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) {
00933 memmove(tb->buf + tb->caretpos + len, tb->buf + tb->caretpos, tb->length - tb->caretpos + 1);
00934 Utf8Encode(tb->buf + tb->caretpos, key);
00935 tb->length += len;
00936 tb->width += charwidth;
00937
00938 tb->caretpos += len;
00939 tb->caretxoffs += charwidth;
00940 return true;
00941 }
00942 return false;
00943 }
00944
00952 bool MoveTextBufferPos(Textbuf *tb, int navmode)
00953 {
00954 switch (navmode) {
00955 case WKC_LEFT:
00956 if (tb->caretpos != 0) {
00957 WChar c;
00958 const char *s = Utf8PrevChar(tb->buf + tb->caretpos);
00959 Utf8Decode(&c, s);
00960 tb->caretpos = s - tb->buf;
00961 tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
00962
00963 return true;
00964 }
00965 break;
00966 case WKC_RIGHT:
00967 if (tb->caretpos < tb->length) {
00968 WChar c;
00969
00970 tb->caretpos += Utf8Decode(&c, tb->buf + tb->caretpos);
00971 tb->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
00972
00973 return true;
00974 }
00975 break;
00976 case WKC_HOME:
00977 tb->caretpos = 0;
00978 tb->caretxoffs = 0;
00979 return true;
00980 case WKC_END:
00981 tb->caretpos = tb->length;
00982 tb->caretxoffs = tb->width;
00983 return true;
00984 }
00985
00986 return false;
00987 }
00988
00998 void InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16 maxwidth)
00999 {
01000 tb->buf = (char*)buf;
01001 tb->maxlength = maxlength;
01002 tb->maxwidth = maxwidth;
01003 tb->caret = true;
01004 UpdateTextBufferSize(tb);
01005 }
01006
01013 void UpdateTextBufferSize(Textbuf *tb)
01014 {
01015 const char *buf = tb->buf;
01016 WChar c = Utf8Consume(&buf);
01017
01018 tb->width = 0;
01019 tb->length = 0;
01020
01021 for (; c != '\0' && tb->length < (tb->maxlength - 1); c = Utf8Consume(&buf)) {
01022 tb->width += GetCharacterWidth(FS_NORMAL, c);
01023 tb->length += Utf8CharLen(c);
01024 }
01025
01026 tb->caretpos = tb->length;
01027 tb->caretxoffs = tb->width;
01028 }
01029
01030 int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *e)
01031 {
01032 e->we.keypress.cont = false;
01033
01034 switch (e->we.keypress.keycode) {
01035 case WKC_ESC: return 2;
01036 case WKC_RETURN: case WKC_NUM_ENTER: return 1;
01037 case (WKC_CTRL | 'V'):
01038 if (InsertTextBufferClipboard(&string->text))
01039 w->InvalidateWidget(wid);
01040 break;
01041 case (WKC_CTRL | 'U'):
01042 DeleteTextBufferAll(&string->text);
01043 w->InvalidateWidget(wid);
01044 break;
01045 case WKC_BACKSPACE: case WKC_DELETE:
01046 if (DeleteTextBufferChar(&string->text, e->we.keypress.keycode))
01047 w->InvalidateWidget(wid);
01048 break;
01049 case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
01050 if (MoveTextBufferPos(&string->text, e->we.keypress.keycode))
01051 w->InvalidateWidget(wid);
01052 break;
01053 default:
01054 if (IsValidChar(e->we.keypress.key, string->afilter)) {
01055 if (InsertTextBufferChar(&string->text, e->we.keypress.key)) {
01056 w->InvalidateWidget(wid);
01057 }
01058 } else {
01059 e->we.keypress.cont = (string->afilter == CS_ALPHANUMERAL);
01060 }
01061 }
01062
01063 return 0;
01064 }
01065
01066 bool HandleCaret(Textbuf *tb)
01067 {
01068
01069 bool b = !!(_caret_timer & 0x20);
01070
01071 if (b != tb->caret) {
01072 tb->caret = b;
01073 return true;
01074 }
01075 return false;
01076 }
01077
01078 void HandleEditBox(Window *w, querystr_d *string, int wid)
01079 {
01080 if (HandleCaret(&string->text)) w->InvalidateWidget(wid);
01081 }
01082
01083 void DrawEditBox(Window *w, querystr_d *string, int wid)
01084 {
01085 DrawPixelInfo dpi, *old_dpi;
01086 int delta;
01087 const Widget *wi = &w->widget[wid];
01088 const Textbuf *tb = &string->text;
01089
01090 GfxFillRect(wi->left + 1, wi->top + 1, wi->right - 1, wi->bottom - 1, 215);
01091
01092
01093 if (!FillDrawPixelInfo(&dpi,
01094 wi->left + 4,
01095 wi->top + 1,
01096 wi->right - wi->left - 4,
01097 wi->bottom - wi->top - 1)
01098 ) return;
01099
01100 old_dpi = _cur_dpi;
01101 _cur_dpi = &dpi;
01102
01103
01104
01105 delta = (wi->right - wi->left) - tb->width - 10;
01106 if (delta > 0) delta = 0;
01107
01108 if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
01109
01110 DoDrawString(tb->buf, delta, 0, TC_YELLOW);
01111 if (tb->caret) DoDrawString("_", tb->caretxoffs + delta, 0, TC_WHITE);
01112
01113 _cur_dpi = old_dpi;
01114 }
01115
01116 enum QueryStringWidgets {
01117 QUERY_STR_WIDGET_TEXT = 3,
01118 QUERY_STR_WIDGET_CANCEL,
01119 QUERY_STR_WIDGET_OK
01120 };
01121
01122
01123 static void QueryStringWndProc(Window *w, WindowEvent *e)
01124 {
01125 querystr_d *qs = &WP(w, querystr_d);
01126
01127 switch (e->event) {
01128 case WE_CREATE:
01129 SetBit(_no_scroll, SCROLL_EDIT);
01130 break;
01131
01132 case WE_PAINT:
01133 SetDParam(0, qs->caption);
01134 DrawWindowWidgets(w);
01135
01136 DrawEditBox(w, qs, QUERY_STR_WIDGET_TEXT);
01137 break;
01138
01139 case WE_CLICK:
01140 switch (e->we.click.widget) {
01141 case QUERY_STR_WIDGET_OK:
01142 press_ok:;
01143 if (qs->orig == NULL || strcmp(qs->text.buf, qs->orig) != 0) {
01144 Window *parent = w->parent;
01145 qs->handled = true;
01146
01147
01148
01149 if (parent != NULL) {
01150 WindowEvent e;
01151 e.event = WE_ON_EDIT_TEXT;
01152 e.we.edittext.str = qs->text.buf;
01153 parent->wndproc(parent, &e);
01154 } else {
01155 HandleOnEditText(qs->text.buf);
01156 }
01157 }
01158
01159 case QUERY_STR_WIDGET_CANCEL:
01160 DeleteWindow(w);
01161 break;
01162 }
01163 break;
01164
01165 case WE_MOUSELOOP:
01166 HandleEditBox(w, qs, QUERY_STR_WIDGET_TEXT);
01167 break;
01168
01169 case WE_KEYPRESS:
01170 switch (HandleEditBoxKey(w, qs, QUERY_STR_WIDGET_TEXT, e)) {
01171 case 1: goto press_ok;
01172 case 2: DeleteWindow(w); break;
01173 }
01174 break;
01175
01176 case WE_DESTROY:
01177 if (!qs->handled && w->parent != NULL) {
01178 WindowEvent e;
01179 Window *parent = w->parent;
01180
01181 qs->handled = true;
01182 e.event = WE_ON_EDIT_TEXT_CANCEL;
01183 parent->wndproc(parent, &e);
01184 }
01185 ClrBit(_no_scroll, SCROLL_EDIT);
01186 break;
01187 }
01188 }
01189
01190 static const Widget _query_string_widgets[] = {
01191 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
01192 { WWT_CAPTION, RESIZE_NONE, 14, 11, 259, 0, 13, STR_012D, STR_NULL},
01193 { WWT_PANEL, RESIZE_NONE, 14, 0, 259, 14, 29, 0x0, STR_NULL},
01194 { WWT_PANEL, RESIZE_NONE, 14, 2, 257, 16, 27, 0x0, STR_NULL},
01195 { WWT_TEXTBTN, RESIZE_NONE, 14, 0, 129, 30, 41, STR_012E_CANCEL, STR_NULL},
01196 { WWT_TEXTBTN, RESIZE_NONE, 14, 130, 259, 30, 41, STR_012F_OK, STR_NULL},
01197 { WIDGETS_END},
01198 };
01199
01200 static const WindowDesc _query_string_desc = {
01201 190, 219, 260, 42, 260, 42,
01202 WC_QUERY_STRING, WC_NONE,
01203 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
01204 _query_string_widgets,
01205 QueryStringWndProc
01206 };
01207
01208 static char _edit_str_buf[64];
01209
01219 void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, Window *parent, CharSetFilter afilter)
01220 {
01221 static char orig_str_buf[lengthof(_edit_str_buf)];
01222 Window *w;
01223 uint realmaxlen = maxlen & ~0x1000;
01224
01225 assert(realmaxlen < lengthof(_edit_str_buf));
01226
01227 DeleteWindowById(WC_QUERY_STRING, 0);
01228 DeleteWindowById(WC_SAVELOAD, 0);
01229
01230 w = AllocateWindowDesc(&_query_string_desc);
01231 w->parent = parent;
01232
01233 GetString(_edit_str_buf, str, lastof(_edit_str_buf));
01234 _edit_str_buf[realmaxlen - 1] = '\0';
01235
01236 if (maxlen & 0x1000) {
01237 WP(w, querystr_d).orig = NULL;
01238 } else {
01239 strecpy(orig_str_buf, _edit_str_buf, lastof(orig_str_buf));
01240 WP(w, querystr_d).orig = orig_str_buf;
01241 }
01242
01243 w->LowerWidget(QUERY_STR_WIDGET_TEXT);
01244 WP(w, querystr_d).caption = caption;
01245 WP(w, querystr_d).afilter = afilter;
01246 InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, realmaxlen, maxwidth);
01247 }
01248
01249
01250 enum QueryWidgets {
01251 QUERY_WIDGET_CAPTION = 1,
01252 QUERY_WIDGET_NO = 3,
01253 QUERY_WIDGET_YES
01254 };
01255
01256
01257 struct query_d {
01258 void (*proc)(Window*, bool);
01259 uint64 params[10];
01260 StringID message;
01261 bool calledback;
01262 };
01263 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(query_d));
01264
01265
01266 static void QueryWndProc(Window *w, WindowEvent *e)
01267 {
01268 query_d *q = &WP(w, query_d);
01269
01270 switch (e->event) {
01271 case WE_PAINT:
01272 CopyInDParam(0, q->params, lengthof(q->params));
01273 DrawWindowWidgets(w);
01274 CopyInDParam(0, q->params, lengthof(q->params));
01275
01276 DrawStringMultiCenter(w->width / 2, (w->height / 2) - 10, q->message, w->width - 2);
01277 break;
01278
01279 case WE_CLICK:
01280 switch (e->we.click.widget) {
01281 case QUERY_WIDGET_YES:
01282 q->calledback = true;
01283 if (q->proc != NULL) q->proc(w->parent, true);
01284
01285 case QUERY_WIDGET_NO:
01286 DeleteWindow(w);
01287 break;
01288 }
01289 break;
01290
01291 case WE_KEYPRESS:
01292 switch (e->we.keypress.keycode) {
01293 case WKC_RETURN:
01294 case WKC_NUM_ENTER:
01295 q->calledback = true;
01296 if (q->proc != NULL) q->proc(w->parent, true);
01297
01298 case WKC_ESC:
01299 e->we.keypress.cont = false;
01300 DeleteWindow(w);
01301 break;
01302 }
01303 break;
01304
01305 case WE_DESTROY:
01306 if (!q->calledback && q->proc != NULL) {
01307 q->calledback = true;
01308 q->proc(w->parent, false);
01309 }
01310 break;
01311 }
01312 }
01313
01314
01315 static const Widget _query_widgets[] = {
01316 { WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
01317 { WWT_CAPTION, RESIZE_NONE, 4, 11, 209, 0, 13, STR_NULL, STR_NULL},
01318 { WWT_PANEL, RESIZE_NONE, 4, 0, 209, 14, 81, 0x0, STR_NULL},
01319 {WWT_PUSHTXTBTN, RESIZE_NONE, 3, 20, 90, 62, 73, STR_00C9_NO, STR_NULL},
01320 {WWT_PUSHTXTBTN, RESIZE_NONE, 3, 120, 190, 62, 73, STR_00C8_YES, STR_NULL},
01321 { WIDGETS_END },
01322 };
01323
01324 static const WindowDesc _query_desc = {
01325 WDP_CENTER, WDP_CENTER, 210, 82, 210, 82,
01326 WC_CONFIRM_POPUP_QUERY, WC_NONE,
01327 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_DEF_WIDGET | WDF_MODAL,
01328 _query_widgets,
01329 QueryWndProc
01330 };
01331
01340 void ShowQuery(StringID caption, StringID message, Window *parent, void (*callback)(Window*, bool))
01341 {
01342 Window *w = AllocateWindowDesc(&_query_desc);
01343 if (w == NULL) return;
01344
01345 if (parent == NULL) parent = FindWindowById(WC_MAIN_WINDOW, 0);
01346 w->parent = parent;
01347 w->left = parent->left + (parent->width / 2) - (w->width / 2);
01348 w->top = parent->top + (parent->height / 2) - (w->height / 2);
01349
01350
01351
01352 CopyOutDParam(WP(w, query_d).params, 0, lengthof(WP(w, query_d).params));
01353 w->widget[QUERY_WIDGET_CAPTION].data = caption;
01354 WP(w, query_d).message = message;
01355 WP(w, query_d).proc = callback;
01356 WP(w, query_d).calledback = false;
01357 }
01358
01359
01360 static const Widget _load_dialog_widgets[] = {
01361 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
01362 { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 256, 0, 13, STR_NULL, STR_018C_WINDOW_TITLE_DRAG_THIS},
01363 { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 127, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP},
01364 { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 128, 256, 14, 25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP},
01365 { WWT_PANEL, RESIZE_RIGHT, 14, 0, 256, 26, 47, 0x0, STR_NULL},
01366 { WWT_PANEL, RESIZE_RB, 14, 0, 256, 48, 153, 0x0, STR_NULL},
01367 { WWT_PUSHIMGBTN, RESIZE_LR, 14, 245, 256, 48, 59, SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON},
01368 { WWT_INSET, RESIZE_RB, 14, 2, 243, 50, 151, 0x0, STR_400A_LIST_OF_DRIVES_DIRECTORIES},
01369 { WWT_SCROLLBAR, RESIZE_LRB, 14, 245, 256, 60, 141, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
01370 { WWT_RESIZEBOX, RESIZE_LRTB, 14, 245, 256, 142, 153, 0x0, STR_RESIZE_BUTTON},
01371 { WIDGETS_END},
01372 };
01373
01374 static const Widget _save_dialog_widgets[] = {
01375 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
01376 { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 256, 0, 13, STR_NULL, STR_018C_WINDOW_TITLE_DRAG_THIS},
01377 { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 127, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP},
01378 { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 128, 256, 14, 25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP},
01379 { WWT_PANEL, RESIZE_RIGHT, 14, 0, 256, 26, 47, 0x0, STR_NULL},
01380 { WWT_PANEL, RESIZE_RB, 14, 0, 256, 48, 151, 0x0, STR_NULL},
01381 { WWT_PUSHIMGBTN, RESIZE_LR, 14, 245, 256, 48, 59, SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON},
01382 { WWT_INSET, RESIZE_RB, 14, 2, 243, 50, 150, 0x0, STR_400A_LIST_OF_DRIVES_DIRECTORIES},
01383 { WWT_SCROLLBAR, RESIZE_LRB, 14, 245, 256, 60, 151, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
01384 { WWT_PANEL, RESIZE_RTB, 14, 0, 256, 152, 167, 0x0, STR_NULL},
01385 { WWT_PANEL, RESIZE_RTB, 14, 2, 254, 154, 165, 0x0, STR_400B_CURRENTLY_SELECTED_NAME},
01386 { WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 127, 168, 179, STR_4003_DELETE, STR_400C_DELETE_THE_CURRENTLY_SELECTED},
01387 { WWT_PUSHTXTBTN, RESIZE_TB, 14, 128, 244, 168, 179, STR_4002_SAVE, STR_400D_SAVE_THE_CURRENT_GAME_USING},
01388 { WWT_RESIZEBOX, RESIZE_LRTB, 14, 245, 256, 168, 179, 0x0, STR_RESIZE_BUTTON},
01389 { WIDGETS_END},
01390 };
01391
01392
01393 const TextColour _fios_colors[] = {
01394 TC_LIGHT_BLUE, TC_DARK_GREEN, TC_DARK_GREEN, TC_ORANGE, TC_LIGHT_BROWN,
01395 TC_ORANGE, TC_LIGHT_BROWN, TC_ORANGE, TC_ORANGE, TC_YELLOW
01396 };
01397
01398 void BuildFileList()
01399 {
01400 _fios_path_changed = true;
01401 FiosFreeSavegameList();
01402
01403 switch (_saveload_mode) {
01404 case SLD_NEW_GAME:
01405 case SLD_LOAD_SCENARIO:
01406 case SLD_SAVE_SCENARIO:
01407 _fios_list = FiosGetScenarioList(_saveload_mode); break;
01408 case SLD_LOAD_HEIGHTMAP:
01409 _fios_list = FiosGetHeightmapList(_saveload_mode); break;
01410
01411 default: _fios_list = FiosGetSavegameList(_saveload_mode); break;
01412 }
01413 }
01414
01415 static void DrawFiosTexts(uint maxw)
01416 {
01417 static const char *path = NULL;
01418 static StringID str = STR_4006_UNABLE_TO_READ_DRIVE;
01419 static uint32 tot = 0;
01420
01421 if (_fios_path_changed) {
01422 str = FiosGetDescText(&path, &tot);
01423 _fios_path_changed = false;
01424 }
01425
01426 if (str != STR_4006_UNABLE_TO_READ_DRIVE) SetDParam(0, tot);
01427 DrawString(2, 37, str, TC_FROMSTRING);
01428 DoDrawStringTruncated(path, 2, 27, TC_BLACK, maxw);
01429 }
01430
01431 static void MakeSortedSaveGameList()
01432 {
01433 uint sort_start = 0;
01434 uint sort_end = 0;
01435 uint s_amount;
01436 int i;
01437
01438
01439
01440
01441
01442 for (i = 0; i < _fios_num; i++) {
01443 switch (_fios_list[i].type) {
01444 case FIOS_TYPE_DIR: sort_start++; break;
01445 case FIOS_TYPE_PARENT: sort_start++; break;
01446 case FIOS_TYPE_DRIVE: sort_end++; break;
01447 }
01448 }
01449
01450 s_amount = _fios_num - sort_start - sort_end;
01451 if (s_amount > 0)
01452 qsort(_fios_list + sort_start, s_amount, sizeof(FiosItem), compare_FiosItems);
01453 }
01454
01455 static void GenerateFileName()
01456 {
01457
01458
01459 const Player *p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : PLAYER_FIRST);
01460
01461 SetDParam(0, p->index);
01462 SetDParam(1, _date);
01463 GetString(_edit_str_buf, STR_4004, lastof(_edit_str_buf));
01464 SanitizeFilename(_edit_str_buf);
01465 }
01466
01467 extern void StartupEngines();
01468
01469 static void SaveLoadDlgWndProc(Window *w, WindowEvent *e)
01470 {
01471 static FiosItem o_dir;
01472
01473 switch (e->event) {
01474 case WE_CREATE:
01475 w->vscroll.cap = 10;
01476 w->resize.step_width = 2;
01477 w->resize.step_height = 10;
01478
01479 o_dir.type = FIOS_TYPE_DIRECT;
01480 switch (_saveload_mode) {
01481 case SLD_SAVE_GAME:
01482 case SLD_LOAD_GAME:
01483 FioGetDirectory(o_dir.name, lengthof(o_dir.name), SAVE_DIR);
01484 break;
01485
01486 case SLD_SAVE_SCENARIO:
01487 case SLD_LOAD_SCENARIO:
01488 FioGetDirectory(o_dir.name, lengthof(o_dir.name), SCENARIO_DIR);
01489 break;
01490
01491 case SLD_LOAD_HEIGHTMAP:
01492 FioGetDirectory(o_dir.name, lengthof(o_dir.name), HEIGHTMAP_DIR);
01493 break;
01494
01495 default:
01496 ttd_strlcpy(o_dir.name, _personal_dir, lengthof(o_dir.name));
01497 }
01498 break;
01499
01500 case WE_PAINT: {
01501 int pos;
01502 int y;
01503
01504 SetVScrollCount(w, _fios_num);
01505 DrawWindowWidgets(w);
01506 DrawFiosTexts(w->width);
01507
01508 if (_savegame_sort_dirty) {
01509 _savegame_sort_dirty = false;
01510 MakeSortedSaveGameList();
01511 }
01512
01513 GfxFillRect(w->widget[7].left + 1, w->widget[7].top + 1, w->widget[7].right, w->widget[7].bottom, 0xD7);
01514 DrawSortButtonState(w, _savegame_sort_order & SORT_BY_NAME ? 2 : 3, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP);
01515
01516 y = w->widget[7].top + 1;
01517 for (pos = w->vscroll.pos; pos < _fios_num; pos++) {
01518 const FiosItem *item = _fios_list + pos;
01519
01520 DoDrawStringTruncated(item->title, 4, y, _fios_colors[item->type], w->width - 18);
01521 y += 10;
01522 if (y >= w->vscroll.cap * 10 + w->widget[7].top + 1) break;
01523 }
01524
01525 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01526 DrawEditBox(w, &WP(w, querystr_d), 10);
01527 }
01528 break;
01529 }
01530
01531 case WE_CLICK:
01532 switch (e->we.click.widget) {
01533 case 2:
01534 _savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ?
01535 SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME;
01536 _savegame_sort_dirty = true;
01537 SetWindowDirty(w);
01538 break;
01539
01540 case 3:
01541 _savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ?
01542 SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE;
01543 _savegame_sort_dirty = true;
01544 SetWindowDirty(w);
01545 break;
01546
01547 case 6:
01548 FiosBrowseTo(&o_dir);
01549 SetWindowDirty(w);
01550 BuildFileList();
01551 break;
01552
01553 case 7: {
01554 int y = (e->we.click.pt.y - w->widget[e->we.click.widget].top - 1) / 10;
01555 char *name;
01556 const FiosItem *file;
01557
01558 if (y < 0 || (y += w->vscroll.pos) >= w->vscroll.count) return;
01559
01560 file = _fios_list + y;
01561
01562 name = FiosBrowseTo(file);
01563 if (name != NULL) {
01564 if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) {
01565 _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD;
01566
01567 SetFiosType(file->type);
01568 ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
01569 ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
01570
01571 DeleteWindow(w);
01572 } else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) {
01573 SetFiosType(file->type);
01574 ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
01575 ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
01576
01577 DeleteWindow(w);
01578 ShowHeightmapLoad();
01579 } else {
01580
01581 ttd_strlcpy(WP(w, querystr_d).text.buf, file->title, WP(w, querystr_d).text.maxlength);
01582 UpdateTextBufferSize(&WP(w, querystr_d).text);
01583 w->InvalidateWidget(10);
01584 }
01585 } else {
01586
01587 SetWindowDirty(w);
01588 BuildFileList();
01589 }
01590 break;
01591 }
01592
01593 case 11: case 12:
01594 break;
01595 }
01596 break;
01597 case WE_MOUSELOOP:
01598 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01599 HandleEditBox(w, &WP(w, querystr_d), 10);
01600 }
01601 break;
01602 case WE_KEYPRESS:
01603 if (e->we.keypress.keycode == WKC_ESC) {
01604 DeleteWindow(w);
01605 return;
01606 }
01607
01608 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01609 if (HandleEditBoxKey(w, &WP(w, querystr_d), 10, e) == 1)
01610 w->HandleButtonClick(12);
01611 }
01612 break;
01613 case WE_TIMEOUT:
01614
01615
01616 if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) break;
01617
01618 if (w->IsWidgetLowered(11)) {
01619 if (!FiosDelete(WP(w, querystr_d).text.buf)) {
01620 ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0);
01621 } else {
01622 BuildFileList();
01623
01624 if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName();
01625 }
01626
01627 UpdateTextBufferSize(&WP(w, querystr_d).text);
01628 SetWindowDirty(w);
01629 } else if (w->IsWidgetLowered(12)) {
01630 _switch_mode = SM_SAVE;
01631 FiosMakeSavegameName(_file_to_saveload.name, WP(w, querystr_d).text.buf, sizeof(_file_to_saveload.name));
01632
01633
01634 if (_game_mode == GM_EDITOR) StartupEngines();
01635 }
01636 break;
01637 case WE_DESTROY:
01638
01639 if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) {
01640 if (_pause_game >= 0) DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
01641 }
01642 FiosFreeSavegameList();
01643 ClrBit(_no_scroll, SCROLL_SAVE);
01644 break;
01645 case WE_RESIZE: {
01646
01647 uint diff = e->we.sizing.diff.x / 2;
01648 w->widget[2].right += diff;
01649 w->widget[3].left += diff;
01650 w->widget[3].right += e->we.sizing.diff.x;
01651
01652
01653 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01654 w->widget[11].right += diff;
01655 w->widget[12].left += diff;
01656 w->widget[12].right += e->we.sizing.diff.x;
01657 }
01658
01659 w->vscroll.cap += e->we.sizing.diff.y / 10;
01660 } break;
01661 }
01662 }
01663
01664 static const WindowDesc _load_dialog_desc = {
01665 WDP_CENTER, WDP_CENTER, 257, 154, 257, 294,
01666 WC_SAVELOAD, WC_NONE,
01667 WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
01668 _load_dialog_widgets,
01669 SaveLoadDlgWndProc,
01670 };
01671
01672 static const WindowDesc _save_dialog_desc = {
01673 WDP_CENTER, WDP_CENTER, 257, 180, 257, 320,
01674 WC_SAVELOAD, WC_NONE,
01675 WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
01676 _save_dialog_widgets,
01677 SaveLoadDlgWndProc,
01678 };
01679
01682 static const FileType _file_modetotype[] = {
01683 FT_SAVEGAME,
01684 FT_SCENARIO,
01685 FT_SAVEGAME,
01686 FT_SCENARIO,
01687 FT_HEIGHTMAP,
01688 FT_SAVEGAME,
01689 };
01690
01691 void ShowSaveLoadDialog(SaveLoadDialogMode mode)
01692 {
01693 static const StringID saveload_captions[] = {
01694 STR_4001_LOAD_GAME,
01695 STR_0298_LOAD_SCENARIO,
01696 STR_4000_SAVE_GAME,
01697 STR_0299_SAVE_SCENARIO,
01698 STR_LOAD_HEIGHTMAP,
01699 };
01700
01701 Window *w;
01702 const WindowDesc *sld = &_save_dialog_desc;
01703
01704
01705 SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, VHM_NONE, WC_MAIN_WINDOW, 0);
01706 DeleteWindowById(WC_QUERY_STRING, 0);
01707 DeleteWindowById(WC_SAVELOAD, 0);
01708
01709 _saveload_mode = mode;
01710 SetBit(_no_scroll, SCROLL_SAVE);
01711
01712
01713
01714 _file_to_saveload.filetype = _file_modetotype[mode];
01715 switch (mode) {
01716 case SLD_SAVE_GAME: GenerateFileName(); break;
01717 case SLD_SAVE_SCENARIO: strcpy(_edit_str_buf, "UNNAMED"); break;
01718 default: sld = &_load_dialog_desc; break;
01719 }
01720
01721 assert((uint)mode < lengthof(saveload_captions));
01722 w = AllocateWindowDesc(sld);
01723 w->widget[1].data = saveload_captions[mode];
01724 w->LowerWidget(7);
01725
01726 WP(w, querystr_d).afilter = CS_ALPHANUMERAL;
01727 InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, lengthof(_edit_str_buf), 240);
01728
01729
01730
01731 if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) {
01732 if (_pause_game >= 0) DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
01733 }
01734
01735 BuildFileList();
01736
01737 ResetObjectToPlace();
01738 }
01739
01740 void RedrawAutosave()
01741 {
01742 SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0));
01743 }
01744
01745 void SetFiosType(const byte fiostype)
01746 {
01747 switch (fiostype) {
01748 case FIOS_TYPE_FILE:
01749 case FIOS_TYPE_SCENARIO:
01750 _file_to_saveload.mode = SL_LOAD;
01751 break;
01752
01753 case FIOS_TYPE_OLDFILE:
01754 case FIOS_TYPE_OLD_SCENARIO:
01755 _file_to_saveload.mode = SL_OLD_LOAD;
01756 break;
01757
01758 #ifdef WITH_PNG
01759 case FIOS_TYPE_PNG:
01760 _file_to_saveload.mode = SL_PNG;
01761 break;
01762 #endif
01763
01764 case FIOS_TYPE_BMP:
01765 _file_to_saveload.mode = SL_BMP;
01766 break;
01767
01768 default:
01769 _file_to_saveload.mode = SL_INVALID;
01770 break;
01771 }
01772 }
01773
01779 static int32 _money_cheat_amount = 10000000;
01780
01781 static int32 ClickMoneyCheat(int32 p1, int32 p2)
01782 {
01783 DoCommandP(0, (uint32)(p2 * _money_cheat_amount), 0, NULL, CMD_MONEY_CHEAT);
01784 return _money_cheat_amount;
01785 }
01786
01791 static int32 ClickChangePlayerCheat(int32 p1, int32 p2)
01792 {
01793 while (IsValidPlayer((PlayerID)p1)) {
01794 if (_players[p1].is_active) {
01795 SetLocalPlayer((PlayerID)p1);
01796
01797 MarkWholeScreenDirty();
01798 return _local_player;
01799 }
01800 p1 += p2;
01801 }
01802
01803 return _local_player;
01804 }
01805
01810 static int32 ClickChangeClimateCheat(int32 p1, int32 p2)
01811 {
01812 if (p1 == -1) p1 = 3;
01813 if (p1 == 4) p1 = 0;
01814 _opt.landscape = p1;
01815 ReloadNewGRFData();
01816 return _opt.landscape;
01817 }
01818
01819 extern void EnginesMonthlyLoop();
01820
01825 static int32 ClickChangeDateCheat(int32 p1, int32 p2)
01826 {
01827 YearMonthDay ymd;
01828 ConvertDateToYMD(_date, &ymd);
01829
01830 if ((ymd.year == MIN_YEAR && p2 == -1) || (ymd.year == MAX_YEAR && p2 == 1)) return _cur_year;
01831
01832 SetDate(ConvertYMDToDate(_cur_year + p2, ymd.month, ymd.day));
01833 EnginesMonthlyLoop();
01834 SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0));
01835 return _cur_year;
01836 }
01837
01838 typedef int32 CheckButtonClick(int32, int32);
01839
01840 struct CheatEntry {
01841 VarType type;
01842 StringID str;
01843 void *variable;
01844 bool *been_used;
01845 CheckButtonClick *proc;
01846 };
01847
01848 static const CheatEntry _cheats_ui[] = {
01849 {SLE_INT32, STR_CHEAT_MONEY, &_money_cheat_amount, &_cheats.money.been_used, &ClickMoneyCheat },
01850 {SLE_UINT8, STR_CHEAT_CHANGE_PLAYER, &_local_player, &_cheats.switch_player.been_used, &ClickChangePlayerCheat },
01851 {SLE_BOOL, STR_CHEAT_EXTRA_DYNAMITE, &_cheats.magic_bulldozer.value, &_cheats.magic_bulldozer.been_used, NULL },
01852 {SLE_BOOL, STR_CHEAT_CROSSINGTUNNELS, &_cheats.crossing_tunnels.value, &_cheats.crossing_tunnels.been_used, NULL },
01853 {SLE_BOOL, STR_CHEAT_BUILD_IN_PAUSE, &_cheats.build_in_pause.value, &_cheats.build_in_pause.been_used, NULL },
01854 {SLE_BOOL, STR_CHEAT_NO_JETCRASH, &_cheats.no_jetcrash.value, &_cheats.no_jetcrash.been_used, NULL },
01855 {SLE_BOOL, STR_CHEAT_SETUP_PROD, &_cheats.setup_prod.value, &_cheats.setup_prod.been_used, NULL },
01856 {SLE_UINT8, STR_CHEAT_SWITCH_CLIMATE, &_opt.landscape, &_cheats.switch_climate.been_used, &ClickChangeClimateCheat},
01857 {SLE_INT32, STR_CHEAT_CHANGE_DATE, &_cur_year, &_cheats.change_date.been_used, &ClickChangeDateCheat },
01858 };
01859
01860
01861 static const Widget _cheat_widgets[] = {
01862 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
01863 { WWT_CAPTION, RESIZE_NONE, 14, 11, 399, 0, 13, STR_CHEATS, STR_018C_WINDOW_TITLE_DRAG_THIS},
01864 { WWT_PANEL, RESIZE_NONE, 14, 0, 399, 14, 169, 0x0, STR_NULL},
01865 { WWT_PANEL, RESIZE_NONE, 14, 0, 399, 14, 169, 0x0, STR_CHEATS_TIP},
01866 { WIDGETS_END},
01867 };
01868
01869 static void CheatsWndProc(Window *w, WindowEvent *e)
01870 {
01871 switch (e->event) {
01872 case WE_PAINT: {
01873 int clk = WP(w, def_d).data_1;
01874
01875 DrawWindowWidgets(w);
01876 DrawStringMultiCenter(200, 25, STR_CHEATS_WARNING, w->width - 50);
01877
01878 for (int i = 0, x = 0, y = 45; i != lengthof(_cheats_ui); i++) {
01879 const CheatEntry *ce = &_cheats_ui[i];
01880
01881 DrawSprite((*ce->been_used) ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, PAL_NONE, x + 5, y + 2);
01882
01883 switch (ce->type) {
01884 case SLE_BOOL: {
01885 bool on = (*(bool*)ce->variable);
01886
01887 DrawFrameRect(x + 20, y + 1, x + 30 + 9, y + 9, on ? 6 : 4, on ? FR_LOWERED : FR_NONE);
01888 SetDParam(0, on ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF);
01889 } break;
01890
01891 default: {
01892 int32 val = (int32)ReadValue(ce->variable, ce->type);
01893 char buf[512];
01894
01895
01896 DrawArrowButtons(x + 20, y, 3, clk - (i * 2), true, true);
01897
01898 switch (ce->str) {
01899
01900 case STR_CHEAT_CHANGE_DATE: SetDParam(0, _date); break;
01901
01902
01903 case STR_CHEAT_CHANGE_PLAYER:
01904 SetDParam(0, val);
01905 GetString(buf, STR_CHEAT_CHANGE_PLAYER, lastof(buf));
01906 DrawPlayerIcon(_current_player, 60 + GetStringBoundingBox(buf).width, y + 2);
01907 break;
01908
01909
01910 case STR_CHEAT_SWITCH_CLIMATE: val += STR_TEMPERATE_LANDSCAPE;
01911
01912
01913 default: SetDParam(0, val);
01914 }
01915 } break;
01916 }
01917
01918 DrawString(50, y + 1, ce->str, TC_FROMSTRING);
01919
01920 y += 12;
01921 }
01922 break;
01923 }
01924
01925 case WE_CLICK: {
01926 uint btn = (e->we.click.pt.y - 46) / 12;
01927 uint x = e->we.click.pt.x;
01928
01929
01930 if (!IsInsideMM(x, 20, 40) || btn >= lengthof(_cheats_ui)) break;
01931
01932 const CheatEntry *ce = &_cheats_ui[btn];
01933 int value = (int32)ReadValue(ce->variable, ce->type);
01934 int oldvalue = value;
01935
01936 *ce->been_used = true;
01937
01938 switch (ce->type) {
01939 case SLE_BOOL:
01940 value ^= 1;
01941 if (ce->proc != NULL) ce->proc(value, 0);
01942 break;
01943
01944 default:
01945
01946 value = ce->proc(value + ((x >= 30) ? 1 : -1), (x >= 30) ? 1 : -1);
01947
01948 if (value != oldvalue) WP(w, def_d).data_1 = btn * 2 + 1 + ((x >= 30) ? 1 : 0);
01949 break;
01950 }
01951
01952 if (value != oldvalue) WriteValue(ce->variable, ce->type, (int64)value);
01953
01954 w->flags4 |= 5 << WF_TIMEOUT_SHL;
01955
01956 SetWindowDirty(w);
01957 } break;
01958
01959 case WE_TIMEOUT:
01960 WP(w, def_d).data_1 = 0;
01961 SetWindowDirty(w);
01962 break;
01963 }
01964 }
01965
01966 static const WindowDesc _cheats_desc = {
01967 240, 22, 400, 170, 400, 170,
01968 WC_CHEATS, WC_NONE,
01969 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
01970 _cheat_widgets,
01971 CheatsWndProc
01972 };
01973
01974
01975 void ShowCheatWindow()
01976 {
01977 DeleteWindowById(WC_CHEATS, 0);
01978 AllocateWindowDesc(&_cheats_desc);
01979 }