|
1 /* $Id$ */ |
|
2 |
|
3 #include "stdafx.h" |
|
4 #include "openttd.h" |
|
5 #include "hal.h" |
|
6 #include "heightmap.h" |
|
7 #include "debug.h" |
|
8 #include "functions.h" |
|
9 #include "newgrf.h" |
|
10 #include "saveload.h" |
|
11 #include "strings.h" |
|
12 #include "table/sprites.h" |
|
13 #include "table/strings.h" |
|
14 #include "table/tree_land.h" |
|
15 #include "map.h" |
|
16 #include "window.h" |
|
17 #include "gui.h" |
|
18 #include "viewport.h" |
|
19 #include "gfx.h" |
|
20 #include "station.h" |
|
21 #include "command.h" |
|
22 #include "player.h" |
|
23 #include "town.h" |
|
24 #include "sound.h" |
|
25 #include "network/network.h" |
|
26 #include "string.h" |
|
27 #include "variables.h" |
|
28 #include "vehicle.h" |
|
29 #include "train.h" |
|
30 #include "tgp.h" |
|
31 #include "settings.h" |
|
32 #include "date.h" |
|
33 |
|
34 #include "fios.h" |
|
35 /* Variables to display file lists */ |
|
36 FiosItem *_fios_list; |
|
37 int _saveload_mode; |
|
38 |
|
39 extern void GenerateLandscape(byte mode); |
|
40 extern void SwitchMode(int new_mode); |
|
41 |
|
42 static bool _fios_path_changed; |
|
43 static bool _savegame_sort_dirty; |
|
44 |
|
45 enum { |
|
46 LAND_INFO_LINES = 7, |
|
47 LAND_INFO_LINE_BUFF_SIZE = 512, |
|
48 }; |
|
49 |
|
50 static char _landinfo_data[LAND_INFO_LINES][LAND_INFO_LINE_BUFF_SIZE]; |
|
51 |
|
52 static void LandInfoWndProc(Window *w, WindowEvent *e) |
|
53 { |
|
54 if (e->event == WE_PAINT) { |
|
55 DrawWindowWidgets(w); |
|
56 |
|
57 DoDrawStringCentered(140, 16, _landinfo_data[0], 13); |
|
58 DoDrawStringCentered(140, 27, _landinfo_data[1], 0); |
|
59 DoDrawStringCentered(140, 38, _landinfo_data[2], 0); |
|
60 DoDrawStringCentered(140, 49, _landinfo_data[3], 0); |
|
61 DoDrawStringCentered(140, 60, _landinfo_data[4], 0); |
|
62 if (_landinfo_data[5][0] != '\0') DrawStringMultiCenter(140, 76, BindCString(_landinfo_data[5]), 276); |
|
63 if (_landinfo_data[6][0] != '\0') DoDrawStringCentered(140, 71, _landinfo_data[6], 0); |
|
64 } |
|
65 } |
|
66 |
|
67 static const Widget _land_info_widgets[] = { |
|
68 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
69 { WWT_CAPTION, RESIZE_NONE, 14, 11, 279, 0, 13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS}, |
|
70 { WWT_PANEL, RESIZE_NONE, 14, 0, 279, 14, 92, 0x0, STR_NULL}, |
|
71 { WIDGETS_END}, |
|
72 }; |
|
73 |
|
74 static const WindowDesc _land_info_desc = { |
|
75 WDP_AUTO, WDP_AUTO, 280, 93, |
|
76 WC_LAND_INFO,0, |
|
77 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, |
|
78 _land_info_widgets, |
|
79 LandInfoWndProc |
|
80 }; |
|
81 |
|
82 static void Place_LandInfo(TileIndex tile) |
|
83 { |
|
84 Player *p; |
|
85 Window *w; |
|
86 Town *t; |
|
87 int64 old_money; |
|
88 int64 costclear; |
|
89 AcceptedCargo ac; |
|
90 TileDesc td; |
|
91 StringID str; |
|
92 |
|
93 DeleteWindowById(WC_LAND_INFO, 0); |
|
94 |
|
95 w = AllocateWindowDesc(&_land_info_desc); |
|
96 WP(w, void_d).data = &_landinfo_data; |
|
97 |
|
98 p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : 0); |
|
99 t = ClosestTownFromTile(tile, _patches.dist_local_authority); |
|
100 |
|
101 old_money = p->money64; |
|
102 p->money64 = p->player_money = 0x7fffffff; |
|
103 costclear = DoCommand(tile, 0, 0, 0, CMD_LANDSCAPE_CLEAR); |
|
104 p->money64 = old_money; |
|
105 UpdatePlayerMoney32(p); |
|
106 |
|
107 /* Because build_date is not set yet in every TileDesc, we make sure it is empty */ |
|
108 td.build_date = 0; |
|
109 GetAcceptedCargo(tile, ac); |
|
110 GetTileDesc(tile, &td); |
|
111 |
|
112 SetDParam(0, td.dparam[0]); |
|
113 GetString(_landinfo_data[0], td.str, lastof(_landinfo_data[0])); |
|
114 |
|
115 SetDParam(0, STR_01A6_N_A); |
|
116 if (td.owner != OWNER_NONE && td.owner != OWNER_WATER) GetNameOfOwner(td.owner, tile); |
|
117 GetString(_landinfo_data[1], STR_01A7_OWNER, lastof(_landinfo_data[1])); |
|
118 |
|
119 str = STR_01A4_COST_TO_CLEAR_N_A; |
|
120 if (!CmdFailed(costclear)) { |
|
121 SetDParam(0, costclear); |
|
122 str = STR_01A5_COST_TO_CLEAR; |
|
123 } |
|
124 GetString(_landinfo_data[2], str, lastof(_landinfo_data[2])); |
|
125 |
|
126 snprintf(_userstring, lengthof(_userstring), "0x%.4X", tile); |
|
127 SetDParam(0, TileX(tile)); |
|
128 SetDParam(1, TileY(tile)); |
|
129 SetDParam(2, STR_SPEC_USERSTRING); |
|
130 GetString(_landinfo_data[3], STR_LANDINFO_COORDS, lastof(_landinfo_data[3])); |
|
131 |
|
132 SetDParam(0, STR_01A9_NONE); |
|
133 if (t != NULL && IsValidTown(t)) { |
|
134 SetDParam(0, STR_TOWN); |
|
135 SetDParam(1, t->index); |
|
136 } |
|
137 GetString(_landinfo_data[4], STR_01A8_LOCAL_AUTHORITY, lastof(_landinfo_data[4])); |
|
138 |
|
139 { |
|
140 int i; |
|
141 char *p = GetString(_landinfo_data[5], STR_01CE_CARGO_ACCEPTED, lastof(_landinfo_data[5])); |
|
142 bool found = false; |
|
143 |
|
144 for (i = 0; i < NUM_CARGO; ++i) { |
|
145 if (ac[i] > 0) { |
|
146 /* Add a comma between each item. */ |
|
147 if (found) { |
|
148 *p++ = ','; |
|
149 *p++ = ' '; |
|
150 } |
|
151 found = true; |
|
152 |
|
153 /* If the accepted value is less than 8, show it in 1/8:ths */ |
|
154 if (ac[i] < 8) { |
|
155 SetDParam(0, ac[i]); |
|
156 SetDParam(1, _cargoc.names_s[i]); |
|
157 p = GetString(p, STR_01D1_8, lastof(_landinfo_data[5])); |
|
158 } else { |
|
159 p = GetString(p, _cargoc.names_s[i], lastof(_landinfo_data[5])); |
|
160 } |
|
161 } |
|
162 } |
|
163 |
|
164 if (!found) _landinfo_data[5][0] = '\0'; |
|
165 } |
|
166 |
|
167 if (td.build_date != 0) { |
|
168 SetDParam(0, td.build_date); |
|
169 GetString(_landinfo_data[6], STR_BUILD_DATE, lastof(_landinfo_data[6])); |
|
170 } else { |
|
171 _landinfo_data[6][0] = '\0'; |
|
172 } |
|
173 |
|
174 #if defined(_DEBUG) |
|
175 # define LANDINFOD_LEVEL 0 |
|
176 #else |
|
177 # define LANDINFOD_LEVEL 1 |
|
178 #endif |
|
179 DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile)); |
|
180 DEBUG(misc, LANDINFOD_LEVEL, "type_height = %#x", _m[tile].type_height); |
|
181 DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", _m[tile].m1); |
|
182 DEBUG(misc, LANDINFOD_LEVEL, "m2 = %#x", _m[tile].m2); |
|
183 DEBUG(misc, LANDINFOD_LEVEL, "m3 = %#x", _m[tile].m3); |
|
184 DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", _m[tile].m4); |
|
185 DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", _m[tile].m5); |
|
186 DEBUG(misc, LANDINFOD_LEVEL, "extra = %#x", _m[tile].extra); |
|
187 #undef LANDINFOD_LEVEL |
|
188 } |
|
189 |
|
190 void PlaceLandBlockInfo(void) |
|
191 { |
|
192 if (_cursor.sprite == SPR_CURSOR_QUERY) { |
|
193 ResetObjectToPlace(); |
|
194 } else { |
|
195 _place_proc = Place_LandInfo; |
|
196 SetObjectToPlace(SPR_CURSOR_QUERY, 1, 1, 0); |
|
197 } |
|
198 } |
|
199 |
|
200 static const char *credits[] = { |
|
201 /************************************************************************* |
|
202 * maximum length of string which fits in window -^*/ |
|
203 "Original design by Chris Sawyer", |
|
204 "Original graphics by Simon Foster", |
|
205 "", |
|
206 "The OpenTTD team (in alphabetical order):", |
|
207 " Jean-Francois Claeys (Belugas) - In training, not yet specialized", |
|
208 " Bjarni Corfitzen (Bjarni) - MacOSX port, coder", |
|
209 " Matthijs Kooijman (blathijs) - Pathfinder-guru", |
|
210 " Victor Fischer (Celestar) - Programming everywhere you need him to", |
|
211 " Tamás Faragó (Darkvater) - Lead coder", |
|
212 " Loïc Guilloux (glx) - In training, not yet specialized", |
|
213 " Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;)", |
|
214 " Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host", |
|
215 " Peter Nelson (peter1138) - Spiritual descendant from newgrf gods", |
|
216 " Remko Bijker (Rubidium) - Belugas code scrutinizer", |
|
217 " Christoph Mallon (Tron) - Programmer, code correctness police", |
|
218 " Patric Stout (TrueLight) - Coder, network guru, SVN- and website host", |
|
219 "", |
|
220 "Retired Developers:", |
|
221 " Ludvig Strigeus (ludde) - OpenTTD author, main coder (0.1 - 0.3.3)", |
|
222 " Serge Paquet (vurlix) - Assistant project manager, coder (0.1 - 0.3.3)", |
|
223 " Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3.0 - 0.3.6)", |
|
224 " Owen Rudge (orudge) - Forum- and masterserver host, OS/2 port (0.1 - 0.4.8)", |
|
225 "", |
|
226 "Special thanks go out to:", |
|
227 " Josef Drexler - For his great work on TTDPatch", |
|
228 " Marcin Grzegorczyk - For his documentation of TTD internals", |
|
229 " Petr Baudis (pasky) - Many patches, newgrf support", |
|
230 " Stefan Meißner (sign_de) - For his work on the console", |
|
231 " Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with (and PBS)", |
|
232 " Cian Duffy (MYOB) - BeOS port / manual writing", |
|
233 " Christian Rosentreter (tokai) - MorphOS / AmigaOS port", |
|
234 " Richard Kempton (richK) - additional airports, initial TGP implementation", |
|
235 "", |
|
236 " Michael Blunck - Pre-Signals and Semaphores © 2003", |
|
237 " George - Canal/Lock graphics © 2003-2004", |
|
238 " Marcin Grzegorczyk - Foundations for Tracks on Slopes", |
|
239 " All Translators - Who made OpenTTD a truly international game", |
|
240 " Bug Reporters - Without whom OpenTTD would still be full of bugs!", |
|
241 "", |
|
242 "", |
|
243 "And last but not least:", |
|
244 " Chris Sawyer - For an amazing game!" |
|
245 }; |
|
246 |
|
247 static void AboutWindowProc(Window *w, WindowEvent *e) |
|
248 { |
|
249 switch (e->event) { |
|
250 case WE_CREATE: /* Set up window counter and start position of scroller */ |
|
251 WP(w, scroller_d).counter = 0; |
|
252 WP(w, scroller_d).height = w->height - 40; |
|
253 break; |
|
254 case WE_PAINT: { |
|
255 uint i; |
|
256 int y = WP(w, scroller_d).height; |
|
257 DrawWindowWidgets(w); |
|
258 |
|
259 // Show original copyright and revision version |
|
260 DrawStringCentered(210, 17, STR_00B6_ORIGINAL_COPYRIGHT, 0); |
|
261 DrawStringCentered(210, 17 + 10, STR_00B7_VERSION, 0); |
|
262 |
|
263 // Show all scrolling credits |
|
264 for (i = 0; i < lengthof(credits); i++) { |
|
265 if (y >= 50 && y < (w->height - 40)) { |
|
266 DoDrawString(credits[i], 10, y, 0x10); |
|
267 } |
|
268 y += 10; |
|
269 } |
|
270 |
|
271 // If the last text has scrolled start anew from the start |
|
272 if (y < 50) WP(w, scroller_d).height = w->height - 40; |
|
273 |
|
274 DoDrawStringCentered(210, w->height - 25, "Website: http://www.openttd.org", 16); |
|
275 DrawStringCentered(210, w->height - 15, STR_00BA_COPYRIGHT_OPENTTD, 0); |
|
276 } break; |
|
277 case WE_MOUSELOOP: /* Timer to scroll the text and adjust the new top */ |
|
278 if (WP(w, scroller_d).counter++ % 3 == 0) { |
|
279 WP(w, scroller_d).height--; |
|
280 SetWindowDirty(w); |
|
281 } |
|
282 break; |
|
283 } |
|
284 } |
|
285 |
|
286 static const Widget _about_widgets[] = { |
|
287 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
288 { WWT_CAPTION, RESIZE_NONE, 14, 11, 419, 0, 13, STR_015B_OPENTTD, STR_NULL}, |
|
289 { WWT_PANEL, RESIZE_NONE, 14, 0, 419, 14, 271, 0x0, STR_NULL}, |
|
290 { WWT_FRAME, RESIZE_NONE, 14, 5, 414, 40, 245, STR_NULL, STR_NULL}, |
|
291 { WIDGETS_END}, |
|
292 }; |
|
293 |
|
294 static const WindowDesc _about_desc = { |
|
295 WDP_CENTER, WDP_CENTER, 420, 272, |
|
296 WC_GAME_OPTIONS,0, |
|
297 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, |
|
298 _about_widgets, |
|
299 AboutWindowProc |
|
300 }; |
|
301 |
|
302 |
|
303 void ShowAboutWindow(void) |
|
304 { |
|
305 DeleteWindowById(WC_GAME_OPTIONS, 0); |
|
306 AllocateWindowDesc(&_about_desc); |
|
307 } |
|
308 |
|
309 static int _tree_to_plant; |
|
310 |
|
311 static const uint32 _tree_sprites[] = { |
|
312 0x655, 0x663, 0x678, 0x62B, 0x647, 0x639, 0x64E, 0x632, 0x67F, 0x68D, 0x69B, 0x6A9, |
|
313 0x6AF, 0x6D2, 0x6D9, 0x6C4, 0x6CB, 0x6B6, 0x6BD, 0x6E0, |
|
314 0x72E, 0x734, 0x74A, 0x74F, 0x76B, 0x78F, 0x788, 0x77B, 0x75F, 0x774, 0x720, 0x797, |
|
315 0x79E, 0x7A5 | PALETTE_TO_GREEN, 0x7AC | PALETTE_TO_RED, 0x7B3, 0x7BA, 0x7C1 | PALETTE_TO_RED, 0x7C8 | PALETTE_TO_PALE_GREEN, 0x7CF | PALETTE_TO_YELLOW, 0x7D6 | PALETTE_TO_RED |
|
316 }; |
|
317 |
|
318 static void BuildTreesWndProc(Window *w, WindowEvent *e) |
|
319 { |
|
320 switch (e->event) { |
|
321 case WE_PAINT: { |
|
322 int x,y; |
|
323 int i, count; |
|
324 |
|
325 DrawWindowWidgets(w); |
|
326 |
|
327 WP(w,tree_d).base = i = _tree_base_by_landscape[_opt.landscape]; |
|
328 WP(w,tree_d).count = count = _tree_count_by_landscape[_opt.landscape]; |
|
329 |
|
330 x = 18; |
|
331 y = 54; |
|
332 do { |
|
333 DrawSprite(_tree_sprites[i], x, y); |
|
334 x += 35; |
|
335 if (!(++i & 3)) { |
|
336 x -= 35 * 4; |
|
337 y += 47; |
|
338 } |
|
339 } while (--count); |
|
340 } break; |
|
341 |
|
342 case WE_CLICK: { |
|
343 int wid = e->we.click.widget; |
|
344 |
|
345 switch (wid) { |
|
346 case 0: |
|
347 ResetObjectToPlace(); |
|
348 break; |
|
349 |
|
350 case 3: case 4: case 5: case 6: |
|
351 case 7: case 8: case 9: case 10: |
|
352 case 11:case 12: case 13: case 14: |
|
353 if (wid - 3 >= WP(w,tree_d).count) break; |
|
354 |
|
355 if (HandlePlacePushButton(w, wid, SPR_CURSOR_TREE, 1, NULL)) |
|
356 _tree_to_plant = WP(w,tree_d).base + wid - 3; |
|
357 break; |
|
358 |
|
359 case 15: // tree of random type. |
|
360 if (HandlePlacePushButton(w, 15, SPR_CURSOR_TREE, 1, NULL)) |
|
361 _tree_to_plant = -1; |
|
362 break; |
|
363 |
|
364 case 16: /* place trees randomly over the landscape*/ |
|
365 LowerWindowWidget(w, 16); |
|
366 w->flags4 |= 5 << WF_TIMEOUT_SHL; |
|
367 SndPlayFx(SND_15_BEEP); |
|
368 PlaceTreesRandomly(); |
|
369 MarkWholeScreenDirty(); |
|
370 break; |
|
371 } |
|
372 } break; |
|
373 |
|
374 case WE_PLACE_OBJ: |
|
375 VpStartPlaceSizing(e->we.place.tile, VPM_X_AND_Y_LIMITED); |
|
376 VpSetPlaceSizingLimit(20); |
|
377 break; |
|
378 |
|
379 case WE_PLACE_DRAG: |
|
380 VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata); |
|
381 return; |
|
382 |
|
383 case WE_PLACE_MOUSEUP: |
|
384 if (e->we.place.pt.x != -1) { |
|
385 DoCommandP(e->we.place.tile, _tree_to_plant, e->we.place.starttile, NULL, |
|
386 CMD_PLANT_TREE | CMD_AUTO | CMD_MSG(STR_2805_CAN_T_PLANT_TREE_HERE)); |
|
387 } |
|
388 break; |
|
389 |
|
390 case WE_TIMEOUT: |
|
391 RaiseWindowWidget(w, 16); |
|
392 break; |
|
393 |
|
394 case WE_ABORT_PLACE_OBJ: |
|
395 RaiseWindowButtons(w); |
|
396 break; |
|
397 } |
|
398 } |
|
399 |
|
400 static const Widget _build_trees_widgets[] = { |
|
401 { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
402 { WWT_CAPTION, RESIZE_NONE, 7, 11, 142, 0, 13, STR_2802_TREES, STR_018C_WINDOW_TITLE_DRAG_THIS}, |
|
403 { WWT_PANEL, RESIZE_NONE, 7, 0, 142, 14, 170, 0x0, STR_NULL}, |
|
404 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
405 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
406 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
407 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
408 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
409 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
410 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
411 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
412 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
413 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
414 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
415 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
416 { WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 157, 168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP}, |
|
417 { WIDGETS_END}, |
|
418 }; |
|
419 |
|
420 static const WindowDesc _build_trees_desc = { |
|
421 497, 22, 143, 171, |
|
422 WC_BUILD_TREES, WC_SCEN_LAND_GEN, |
|
423 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, |
|
424 _build_trees_widgets, |
|
425 BuildTreesWndProc |
|
426 }; |
|
427 |
|
428 static const Widget _build_trees_scen_widgets[] = { |
|
429 { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
430 { WWT_CAPTION, RESIZE_NONE, 7, 11, 142, 0, 13, STR_2802_TREES, STR_018C_WINDOW_TITLE_DRAG_THIS}, |
|
431 { WWT_PANEL, RESIZE_NONE, 7, 0, 142, 14, 183, 0x0, STR_NULL}, |
|
432 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
433 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
434 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
435 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
436 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
437 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
438 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
439 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
440 { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
441 { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
442 { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
443 { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, |
|
444 { WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 157, 168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP}, |
|
445 { WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 170, 181, STR_028A_RANDOM_TREES, STR_028B_PLANT_TREES_RANDOMLY_OVER}, |
|
446 { WIDGETS_END}, |
|
447 }; |
|
448 |
|
449 static const WindowDesc _build_trees_scen_desc = { |
|
450 WDP_AUTO, WDP_AUTO, 143, 184, |
|
451 WC_BUILD_TREES,0, |
|
452 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, |
|
453 _build_trees_scen_widgets, |
|
454 BuildTreesWndProc |
|
455 }; |
|
456 |
|
457 |
|
458 void ShowBuildTreesToolbar(void) |
|
459 { |
|
460 if (!IsValidPlayer(_current_player)) return; |
|
461 AllocateWindowDescFront(&_build_trees_desc, 0); |
|
462 } |
|
463 |
|
464 void ShowBuildTreesScenToolbar(void) |
|
465 { |
|
466 AllocateWindowDescFront(&_build_trees_scen_desc, 0); |
|
467 } |
|
468 |
|
469 static uint32 _errmsg_decode_params[20]; |
|
470 static StringID _errmsg_message_1, _errmsg_message_2; |
|
471 static uint _errmsg_duration; |
|
472 |
|
473 |
|
474 static const Widget _errmsg_widgets[] = { |
|
475 { WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
476 { WWT_CAPTION, RESIZE_NONE, 4, 11, 239, 0, 13, STR_00B2_MESSAGE, STR_NULL}, |
|
477 { WWT_PANEL, RESIZE_NONE, 4, 0, 239, 14, 45, 0x0, STR_NULL}, |
|
478 { WIDGETS_END}, |
|
479 }; |
|
480 |
|
481 static const Widget _errmsg_face_widgets[] = { |
|
482 { WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
483 { WWT_CAPTION, RESIZE_NONE, 4, 11, 333, 0, 13, STR_00B3_MESSAGE_FROM, STR_NULL}, |
|
484 { WWT_PANEL, RESIZE_NONE, 4, 0, 333, 14, 136, 0x0, STR_NULL}, |
|
485 { WIDGETS_END}, |
|
486 }; |
|
487 |
|
488 static void ErrmsgWndProc(Window *w, WindowEvent *e) |
|
489 { |
|
490 switch (e->event) { |
|
491 case WE_PAINT: |
|
492 COPY_IN_DPARAM(0, _errmsg_decode_params, lengthof(_errmsg_decode_params)); |
|
493 DrawWindowWidgets(w); |
|
494 COPY_IN_DPARAM(0, _errmsg_decode_params, lengthof(_errmsg_decode_params)); |
|
495 if (!IsWindowOfPrototype(w, _errmsg_face_widgets)) { |
|
496 DrawStringMultiCenter( |
|
497 120, |
|
498 (_errmsg_message_1 == INVALID_STRING_ID ? 25 : 15), |
|
499 _errmsg_message_2, |
|
500 238); |
|
501 if (_errmsg_message_1 != INVALID_STRING_ID) |
|
502 DrawStringMultiCenter( |
|
503 120, |
|
504 30, |
|
505 _errmsg_message_1, |
|
506 238); |
|
507 } else { |
|
508 const Player *p = GetPlayer(GetDParamX(_errmsg_decode_params,2)); |
|
509 DrawPlayerFace(p->face, p->player_color, 2, 16); |
|
510 |
|
511 DrawStringMultiCenter( |
|
512 214, |
|
513 (_errmsg_message_1 == INVALID_STRING_ID ? 65 : 45), |
|
514 _errmsg_message_2, |
|
515 238); |
|
516 if (_errmsg_message_1 != INVALID_STRING_ID) |
|
517 DrawStringMultiCenter( |
|
518 214, |
|
519 90, |
|
520 _errmsg_message_1, |
|
521 238); |
|
522 } |
|
523 break; |
|
524 |
|
525 case WE_MOUSELOOP: |
|
526 if (_right_button_down) DeleteWindow(w); |
|
527 break; |
|
528 |
|
529 case WE_4: |
|
530 if (--_errmsg_duration == 0) DeleteWindow(w); |
|
531 break; |
|
532 |
|
533 case WE_DESTROY: |
|
534 SetRedErrorSquare(0); |
|
535 _switch_mode_errorstr = INVALID_STRING_ID; |
|
536 break; |
|
537 |
|
538 case WE_KEYPRESS: |
|
539 if (e->we.keypress.keycode == WKC_SPACE) { |
|
540 // Don't continue. |
|
541 e->we.keypress.cont = false; |
|
542 DeleteWindow(w); |
|
543 } |
|
544 break; |
|
545 } |
|
546 } |
|
547 |
|
548 void ShowErrorMessage(StringID msg_1, StringID msg_2, int x, int y) |
|
549 { |
|
550 Window *w; |
|
551 const ViewPort *vp; |
|
552 Point pt; |
|
553 |
|
554 DeleteWindowById(WC_ERRMSG, 0); |
|
555 |
|
556 //assert(msg_2); |
|
557 if (msg_2 == 0) msg_2 = STR_EMPTY; |
|
558 |
|
559 _errmsg_message_1 = msg_1; |
|
560 _errmsg_message_2 = msg_2; |
|
561 COPY_OUT_DPARAM(_errmsg_decode_params, 0, lengthof(_errmsg_decode_params)); |
|
562 _errmsg_duration = _patches.errmsg_duration; |
|
563 if (!_errmsg_duration) return; |
|
564 |
|
565 if (_errmsg_message_1 != STR_013B_OWNED_BY || GetDParamX(_errmsg_decode_params,2) >= 8) { |
|
566 |
|
567 if ( (x|y) != 0) { |
|
568 pt = RemapCoords2(x, y); |
|
569 vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport; |
|
570 |
|
571 // move x pos to opposite corner |
|
572 pt.x = ((pt.x - vp->virtual_left) >> vp->zoom) + vp->left; |
|
573 pt.x = (pt.x < (_screen.width >> 1)) ? _screen.width - 260 : 20; |
|
574 |
|
575 // move y pos to opposite corner |
|
576 pt.y = ((pt.y - vp->virtual_top) >> vp->zoom) + vp->top; |
|
577 pt.y = (pt.y < (_screen.height >> 1)) ? _screen.height - 80 : 100; |
|
578 |
|
579 } else { |
|
580 pt.x = (_screen.width - 240) >> 1; |
|
581 pt.y = (_screen.height - 46) >> 1; |
|
582 } |
|
583 w = AllocateWindow(pt.x, pt.y, 240, 46, ErrmsgWndProc, WC_ERRMSG, _errmsg_widgets); |
|
584 } else { |
|
585 if ( (x|y) != 0) { |
|
586 pt = RemapCoords2(x, y); |
|
587 vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport; |
|
588 pt.x = clamp(((pt.x - vp->virtual_left) >> vp->zoom) + vp->left - (334/2), 0, _screen.width - 334); |
|
589 pt.y = clamp(((pt.y - vp->virtual_top) >> vp->zoom) + vp->top - (137/2), 22, _screen.height - 137); |
|
590 } else { |
|
591 pt.x = (_screen.width - 334) >> 1; |
|
592 pt.y = (_screen.height - 137) >> 1; |
|
593 } |
|
594 w = AllocateWindow(pt.x, pt.y, 334, 137, ErrmsgWndProc, WC_ERRMSG, _errmsg_face_widgets); |
|
595 } |
|
596 |
|
597 w->desc_flags = WDF_STD_BTN | WDF_DEF_WIDGET; |
|
598 } |
|
599 |
|
600 |
|
601 void ShowEstimatedCostOrIncome(int32 cost, int x, int y) |
|
602 { |
|
603 StringID msg = STR_0805_ESTIMATED_COST; |
|
604 |
|
605 if (cost < 0) { |
|
606 cost = -cost; |
|
607 msg = STR_0807_ESTIMATED_INCOME; |
|
608 } |
|
609 SetDParam(0, cost); |
|
610 ShowErrorMessage(INVALID_STRING_ID, msg, x, y); |
|
611 } |
|
612 |
|
613 void ShowCostOrIncomeAnimation(int x, int y, int z, int32 cost) |
|
614 { |
|
615 StringID msg; |
|
616 Point pt = RemapCoords(x,y,z); |
|
617 |
|
618 msg = STR_0801_COST; |
|
619 if (cost < 0) { |
|
620 cost = -cost; |
|
621 msg = STR_0803_INCOME; |
|
622 } |
|
623 SetDParam(0, cost); |
|
624 AddTextEffect(msg, pt.x, pt.y, 0x250); |
|
625 } |
|
626 |
|
627 void ShowFeederIncomeAnimation(int x, int y, int z, int32 cost) |
|
628 { |
|
629 Point pt = RemapCoords(x,y,z); |
|
630 |
|
631 SetDParam(0, cost); |
|
632 AddTextEffect(STR_FEEDER, pt.x, pt.y, 0x250); |
|
633 } |
|
634 |
|
635 static const Widget _tooltips_widgets[] = { |
|
636 { WWT_PANEL, RESIZE_NONE, 14, 0, 199, 0, 31, 0x0, STR_NULL}, |
|
637 { WIDGETS_END}, |
|
638 }; |
|
639 |
|
640 |
|
641 static void TooltipsWndProc(Window *w, WindowEvent *e) |
|
642 { |
|
643 switch (e->event) { |
|
644 case WE_PAINT: { |
|
645 uint arg; |
|
646 GfxFillRect(0, 0, w->width - 1, w->height - 1, 0); |
|
647 GfxFillRect(1, 1, w->width - 2, w->height - 2, 0x44); |
|
648 |
|
649 for (arg = 0; arg < WP(w, tooltips_d).paramcount; arg++) { |
|
650 SetDParam(arg, WP(w, tooltips_d).params[arg]); |
|
651 } |
|
652 DrawStringMultiCenter((w->width >> 1), (w->height >> 1) - 5, WP(w, tooltips_d).string_id, 197); |
|
653 break; |
|
654 } |
|
655 |
|
656 case WE_MOUSELOOP: |
|
657 /* We can show tooltips while dragging tools. These are shown as long as |
|
658 * we are dragging the tool. Normal tooltips work with rmb */ |
|
659 if (WP(w, tooltips_d).paramcount == 0 ) { |
|
660 if (!_right_button_down) DeleteWindow(w); |
|
661 } else { |
|
662 if (!_left_button_down) DeleteWindow(w); |
|
663 } |
|
664 |
|
665 break; |
|
666 } |
|
667 } |
|
668 |
|
669 /** Shows a tooltip |
|
670 * @param str String to be displayed |
|
671 * @param params (optional) up to 5 pieces of additional information that may be |
|
672 * added to a tooltip; currently only supports parameters of {NUM} (integer) */ |
|
673 void GuiShowTooltipsWithArgs(StringID str, uint paramcount, const uint32 params[]) |
|
674 { |
|
675 char buffer[512]; |
|
676 BoundingRect br; |
|
677 Window *w; |
|
678 uint i; |
|
679 int x, y; |
|
680 |
|
681 DeleteWindowById(WC_TOOLTIPS, 0); |
|
682 |
|
683 /* We only show measurement tooltips with patch setting on */ |
|
684 if (str == STR_NULL || (paramcount != 0 && !_patches.measure_tooltip)) return; |
|
685 |
|
686 for (i = 0; i != paramcount; i++) SetDParam(i, params[i]); |
|
687 GetString(buffer, str, lastof(buffer)); |
|
688 |
|
689 br = GetStringBoundingBox(buffer); |
|
690 br.width += 6; br.height += 4; // increase slightly to have some space around the box |
|
691 |
|
692 /* Cut tooltip length to 200 pixels max, wrap to new line if longer */ |
|
693 if (br.width > 200) { |
|
694 br.height += ((br.width - 4) / 176) * 10; |
|
695 br.width = 200; |
|
696 } |
|
697 |
|
698 /* Correctly position the tooltip position, watch out for window and cursor size |
|
699 * Clamp value to below main toolbar and above statusbar. If tooltip would |
|
700 * go below window, flip it so it is shown above the cursor */ |
|
701 y = clamp(_cursor.pos.y + _cursor.size.y + _cursor.offs.y + 5, 22, _screen.height - 12); |
|
702 if (y + br.height > _screen.height - 12) y = _cursor.pos.y + _cursor.offs.y - br.height - 5; |
|
703 x = clamp(_cursor.pos.x - (br.width >> 1), 0, _screen.width - br.width); |
|
704 |
|
705 w = AllocateWindow(x, y, br.width, br.height, TooltipsWndProc, WC_TOOLTIPS, _tooltips_widgets); |
|
706 |
|
707 WP(w, tooltips_d).string_id = str; |
|
708 assert(sizeof(WP(w, tooltips_d).params[0]) == sizeof(params[0])); |
|
709 memcpy(WP(w, tooltips_d).params, params, sizeof(WP(w, tooltips_d).params[0]) * paramcount); |
|
710 WP(w, tooltips_d).paramcount = paramcount; |
|
711 |
|
712 w->flags4 &= ~WF_WHITE_BORDER_MASK; // remove white-border from tooltip |
|
713 w->widget[0].right = br.width; |
|
714 w->widget[0].bottom = br.height; |
|
715 } |
|
716 |
|
717 |
|
718 static void DrawStationCoverageText(const AcceptedCargo accepts, |
|
719 int str_x, int str_y, uint mask) |
|
720 { |
|
721 char *b = _userstring; |
|
722 bool first = true; |
|
723 int i; |
|
724 |
|
725 b = InlineString(b, STR_000D_ACCEPTS); |
|
726 |
|
727 for (i = 0; i != NUM_CARGO; i++, mask >>= 1) { |
|
728 if (b >= lastof(_userstring) - 5) break; |
|
729 if (accepts[i] >= 8 && mask & 1) { |
|
730 if (first) { |
|
731 first = false; |
|
732 } else { |
|
733 /* Add a comma if this is not the first item */ |
|
734 *b++ = ','; |
|
735 *b++ = ' '; |
|
736 } |
|
737 b = InlineString(b, _cargoc.names_s[i]); |
|
738 } |
|
739 } |
|
740 |
|
741 /* If first is still true then no cargo is accepted */ |
|
742 if (first) b = InlineString(b, STR_00D0_NOTHING); |
|
743 |
|
744 *b = '\0'; |
|
745 DrawStringMultiLine(str_x, str_y, STR_SPEC_USERSTRING, 144); |
|
746 } |
|
747 |
|
748 void DrawStationCoverageAreaText(int sx, int sy, uint mask, int rad) { |
|
749 TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y); |
|
750 AcceptedCargo accepts; |
|
751 if (tile < MapSize()) { |
|
752 GetAcceptanceAroundTiles(accepts, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad); |
|
753 DrawStationCoverageText(accepts, sx, sy, mask); |
|
754 } |
|
755 } |
|
756 |
|
757 void CheckRedrawStationCoverage(const Window *w) |
|
758 { |
|
759 if (_thd.dirty & 1) { |
|
760 _thd.dirty &= ~1; |
|
761 SetWindowDirty(w); |
|
762 } |
|
763 } |
|
764 |
|
765 void SetVScrollCount(Window *w, int num) |
|
766 { |
|
767 w->vscroll.count = num; |
|
768 num -= w->vscroll.cap; |
|
769 if (num < 0) num = 0; |
|
770 if (num < w->vscroll.pos) w->vscroll.pos = num; |
|
771 } |
|
772 |
|
773 void SetVScroll2Count(Window *w, int num) |
|
774 { |
|
775 w->vscroll2.count = num; |
|
776 num -= w->vscroll2.cap; |
|
777 if (num < 0) num = 0; |
|
778 if (num < w->vscroll2.pos) w->vscroll2.pos = num; |
|
779 } |
|
780 |
|
781 void SetHScrollCount(Window *w, int num) |
|
782 { |
|
783 w->hscroll.count = num; |
|
784 num -= w->hscroll.cap; |
|
785 if (num < 0) num = 0; |
|
786 if (num < w->hscroll.pos) w->hscroll.pos = num; |
|
787 } |
|
788 |
|
789 /* Delete a character at the caret position in a text buf. |
|
790 * If backspace is set, delete the character before the caret, |
|
791 * else delete the character after it. */ |
|
792 static void DelChar(Textbuf *tb, bool backspace) |
|
793 { |
|
794 WChar c; |
|
795 uint width; |
|
796 size_t len; |
|
797 |
|
798 if (backspace) { |
|
799 do { |
|
800 tb->caretpos--; |
|
801 } while (IsUtf8Part(*(tb->buf + tb->caretpos))); |
|
802 } |
|
803 |
|
804 len = Utf8Decode(&c, tb->buf + tb->caretpos); |
|
805 width = GetCharacterWidth(FS_NORMAL, c); |
|
806 |
|
807 tb->width -= width; |
|
808 if (backspace) tb->caretxoffs -= width; |
|
809 |
|
810 /* Move the remaining characters over the marker */ |
|
811 memmove(tb->buf + tb->caretpos, tb->buf + tb->caretpos + len, tb->length - tb->caretpos - len + 1); |
|
812 tb->length -= len; |
|
813 } |
|
814 |
|
815 /** |
|
816 * Delete a character from a textbuffer, either with 'Delete' or 'Backspace' |
|
817 * The character is delete from the position the caret is at |
|
818 * @param tb @Textbuf type to be changed |
|
819 * @param delmode Type of deletion, either @WKC_BACKSPACE or @WKC_DELETE |
|
820 * @return Return true on successfull change of Textbuf, or false otherwise |
|
821 */ |
|
822 bool DeleteTextBufferChar(Textbuf *tb, int delmode) |
|
823 { |
|
824 if (delmode == WKC_BACKSPACE && tb->caretpos != 0) { |
|
825 DelChar(tb, true); |
|
826 return true; |
|
827 } else if (delmode == WKC_DELETE && tb->caretpos < tb->length) { |
|
828 DelChar(tb, false); |
|
829 return true; |
|
830 } |
|
831 |
|
832 return false; |
|
833 } |
|
834 |
|
835 /** |
|
836 * Delete every character in the textbuffer |
|
837 * @param tb @Textbuf buffer to be emptied |
|
838 */ |
|
839 void DeleteTextBufferAll(Textbuf *tb) |
|
840 { |
|
841 memset(tb->buf, 0, tb->maxlength); |
|
842 tb->length = tb->width = 0; |
|
843 tb->caretpos = tb->caretxoffs = 0; |
|
844 } |
|
845 |
|
846 /** |
|
847 * Insert a character to a textbuffer. If maxwidth of the Textbuf is zero, |
|
848 * we don't care about the visual-length but only about the physical |
|
849 * length of the string |
|
850 * @param tb @Textbuf type to be changed |
|
851 * @param key Character to be inserted |
|
852 * @return Return true on successfull change of Textbuf, or false otherwise |
|
853 */ |
|
854 bool InsertTextBufferChar(Textbuf *tb, WChar key) |
|
855 { |
|
856 const byte charwidth = GetCharacterWidth(FS_NORMAL, key); |
|
857 size_t len = Utf8CharLen(key); |
|
858 if (tb->length < (tb->maxlength - len) && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) { |
|
859 memmove(tb->buf + tb->caretpos + len, tb->buf + tb->caretpos, tb->length - tb->caretpos + 1); |
|
860 Utf8Encode(tb->buf + tb->caretpos, key); |
|
861 tb->length += len; |
|
862 tb->width += charwidth; |
|
863 |
|
864 tb->caretpos += len; |
|
865 tb->caretxoffs += charwidth; |
|
866 return true; |
|
867 } |
|
868 return false; |
|
869 } |
|
870 |
|
871 /** |
|
872 * Handle text navigation with arrow keys left/right. |
|
873 * This defines where the caret will blink and the next characer interaction will occur |
|
874 * @param tb @Textbuf type where navigation occurs |
|
875 * @param navmode Direction in which navigation occurs @WKC_LEFT, @WKC_RIGHT, @WKC_END, @WKC_HOME |
|
876 * @return Return true on successfull change of Textbuf, or false otherwise |
|
877 */ |
|
878 bool MoveTextBufferPos(Textbuf *tb, int navmode) |
|
879 { |
|
880 switch (navmode) { |
|
881 case WKC_LEFT: |
|
882 if (tb->caretpos != 0) { |
|
883 WChar c; |
|
884 |
|
885 do { |
|
886 tb->caretpos--; |
|
887 } while (IsUtf8Part(*(tb->buf + tb->caretpos))); |
|
888 |
|
889 Utf8Decode(&c, tb->buf + tb->caretpos); |
|
890 tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, c); |
|
891 |
|
892 return true; |
|
893 } |
|
894 break; |
|
895 case WKC_RIGHT: |
|
896 if (tb->caretpos < tb->length) { |
|
897 WChar c; |
|
898 |
|
899 tb->caretpos += Utf8Decode(&c, tb->buf + tb->caretpos); |
|
900 tb->caretxoffs += GetCharacterWidth(FS_NORMAL, c); |
|
901 |
|
902 return true; |
|
903 } |
|
904 break; |
|
905 case WKC_HOME: |
|
906 tb->caretpos = 0; |
|
907 tb->caretxoffs = 0; |
|
908 return true; |
|
909 case WKC_END: |
|
910 tb->caretpos = tb->length; |
|
911 tb->caretxoffs = tb->width; |
|
912 return true; |
|
913 } |
|
914 |
|
915 return false; |
|
916 } |
|
917 |
|
918 /** |
|
919 * Initialize the textbuffer by supplying it the buffer to write into |
|
920 * and the maximum length of this buffer |
|
921 * @param tb @Textbuf type which is getting initialized |
|
922 * @param buf the buffer that will be holding the data for input |
|
923 * @param maxlength maximum length in characters of this buffer |
|
924 * @param maxwidth maximum length in pixels of this buffer. If reached, buffer |
|
925 * cannot grow, even if maxlength would allow because there is space. A length |
|
926 * of zero '0' means the buffer is only restricted by maxlength */ |
|
927 void InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16 maxwidth) |
|
928 { |
|
929 tb->buf = (char*)buf; |
|
930 tb->maxlength = maxlength; |
|
931 tb->maxwidth = maxwidth; |
|
932 tb->caret = true; |
|
933 UpdateTextBufferSize(tb); |
|
934 } |
|
935 |
|
936 /** |
|
937 * Update @Textbuf type with its actual physical character and screenlength |
|
938 * Get the count of characters in the string as well as the width in pixels. |
|
939 * Useful when copying in a larger amount of text at once |
|
940 * @param tb @Textbuf type which length is calculated |
|
941 */ |
|
942 void UpdateTextBufferSize(Textbuf *tb) |
|
943 { |
|
944 const char *buf = tb->buf; |
|
945 WChar c = Utf8Consume(&buf); |
|
946 |
|
947 tb->width = 0; |
|
948 tb->length = 0; |
|
949 |
|
950 for (; c != '\0' && tb->length < (tb->maxlength - 1); c = Utf8Consume(&buf)) { |
|
951 tb->width += GetCharacterWidth(FS_NORMAL, c); |
|
952 tb->length += Utf8CharLen(c); |
|
953 } |
|
954 |
|
955 tb->caretpos = tb->length; |
|
956 tb->caretxoffs = tb->width; |
|
957 } |
|
958 |
|
959 int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *e) |
|
960 { |
|
961 e->we.keypress.cont = false; |
|
962 |
|
963 switch (e->we.keypress.keycode) { |
|
964 case WKC_ESC: return 2; |
|
965 case WKC_RETURN: case WKC_NUM_ENTER: return 1; |
|
966 case (WKC_CTRL | 'V'): |
|
967 if (InsertTextBufferClipboard(&string->text)) |
|
968 InvalidateWidget(w, wid); |
|
969 break; |
|
970 case (WKC_CTRL | 'U'): |
|
971 DeleteTextBufferAll(&string->text); |
|
972 InvalidateWidget(w, wid); |
|
973 break; |
|
974 case WKC_BACKSPACE: case WKC_DELETE: |
|
975 if (DeleteTextBufferChar(&string->text, e->we.keypress.keycode)) |
|
976 InvalidateWidget(w, wid); |
|
977 break; |
|
978 case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: |
|
979 if (MoveTextBufferPos(&string->text, e->we.keypress.keycode)) |
|
980 InvalidateWidget(w, wid); |
|
981 break; |
|
982 default: |
|
983 if (IsValidChar(e->we.keypress.key, string->afilter)) { |
|
984 if (InsertTextBufferChar(&string->text, e->we.keypress.key)) { |
|
985 InvalidateWidget(w, wid); |
|
986 } |
|
987 } else { // key wasn't caught. Continue only if standard entry specified |
|
988 e->we.keypress.cont = (string->afilter == CS_ALPHANUMERAL); |
|
989 } |
|
990 } |
|
991 |
|
992 return 0; |
|
993 } |
|
994 |
|
995 bool HandleCaret(Textbuf *tb) |
|
996 { |
|
997 /* caret changed? */ |
|
998 bool b = !!(_caret_timer & 0x20); |
|
999 |
|
1000 if (b != tb->caret) { |
|
1001 tb->caret = b; |
|
1002 return true; |
|
1003 } |
|
1004 return false; |
|
1005 } |
|
1006 |
|
1007 void HandleEditBox(Window *w, querystr_d *string, int wid) |
|
1008 { |
|
1009 if (HandleCaret(&string->text)) InvalidateWidget(w, wid); |
|
1010 } |
|
1011 |
|
1012 void DrawEditBox(Window *w, querystr_d *string, int wid) |
|
1013 { |
|
1014 DrawPixelInfo dpi, *old_dpi; |
|
1015 int delta; |
|
1016 const Widget *wi = &w->widget[wid]; |
|
1017 const Textbuf *tb = &string->text; |
|
1018 |
|
1019 /* Limit the drawing of the string inside the widget boundaries */ |
|
1020 if (!FillDrawPixelInfo(&dpi, |
|
1021 wi->left + 4, |
|
1022 wi->top + 1, |
|
1023 wi->right - wi->left - 4, |
|
1024 wi->bottom - wi->top - 1) |
|
1025 ) return; |
|
1026 |
|
1027 GfxFillRect(wi->left + 1, wi->top + 1, wi->right - 1, wi->bottom - 1, 215); |
|
1028 |
|
1029 old_dpi = _cur_dpi; |
|
1030 _cur_dpi = &dpi; |
|
1031 |
|
1032 /* We will take the current widget length as maximum width, with a small |
|
1033 * space reserved at the end for the caret to show */ |
|
1034 delta = (wi->right - wi->left) - tb->width - 10; |
|
1035 if (delta > 0) delta = 0; |
|
1036 |
|
1037 if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs; |
|
1038 |
|
1039 DoDrawString(tb->buf, delta, 0, 8); |
|
1040 if (tb->caret) DoDrawString("_", tb->caretxoffs + delta, 0, 12); |
|
1041 |
|
1042 _cur_dpi = old_dpi; |
|
1043 } |
|
1044 |
|
1045 enum QueryStringWidgets { |
|
1046 QUERY_STR_WIDGET_TEXT = 3, |
|
1047 QUERY_STR_WIDGET_CANCEL, |
|
1048 QUERY_STR_WIDGET_OK |
|
1049 }; |
|
1050 |
|
1051 |
|
1052 static void QueryStringWndProc(Window *w, WindowEvent *e) |
|
1053 { |
|
1054 querystr_d *qs = &WP(w, querystr_d); |
|
1055 |
|
1056 switch (e->event) { |
|
1057 case WE_CREATE: |
|
1058 SETBIT(_no_scroll, SCROLL_EDIT); |
|
1059 break; |
|
1060 |
|
1061 case WE_PAINT: |
|
1062 SetDParam(0, qs->caption); |
|
1063 DrawWindowWidgets(w); |
|
1064 |
|
1065 DrawEditBox(w, qs, QUERY_STR_WIDGET_TEXT); |
|
1066 break; |
|
1067 |
|
1068 case WE_CLICK: |
|
1069 switch (e->we.click.widget) { |
|
1070 case QUERY_STR_WIDGET_OK: |
|
1071 press_ok:; |
|
1072 if (qs->orig == NULL || strcmp(qs->text.buf, qs->orig) != 0) { |
|
1073 Window *parent = w->parent; |
|
1074 qs->handled = true; |
|
1075 |
|
1076 /* If the parent is NULL, the editbox is handled by general function |
|
1077 * HandleOnEditText */ |
|
1078 if (parent != NULL) { |
|
1079 WindowEvent e; |
|
1080 e.event = WE_ON_EDIT_TEXT; |
|
1081 e.we.edittext.str = qs->text.buf; |
|
1082 parent->wndproc(parent, &e); |
|
1083 } else { |
|
1084 HandleOnEditText(qs->text.buf); |
|
1085 } |
|
1086 } |
|
1087 /* Fallthrough */ |
|
1088 case QUERY_STR_WIDGET_CANCEL: |
|
1089 DeleteWindow(w); |
|
1090 break; |
|
1091 } |
|
1092 break; |
|
1093 |
|
1094 case WE_MOUSELOOP: |
|
1095 HandleEditBox(w, qs, QUERY_STR_WIDGET_TEXT); |
|
1096 break; |
|
1097 |
|
1098 case WE_KEYPRESS: |
|
1099 switch (HandleEditBoxKey(w, qs, QUERY_STR_WIDGET_TEXT, e)) { |
|
1100 case 1: goto press_ok; /* Enter pressed, confirms change */ |
|
1101 case 2: DeleteWindow(w); break; /* ESC pressed, closes window, abandons changes */ |
|
1102 } |
|
1103 break; |
|
1104 |
|
1105 case WE_DESTROY: /* Call cancellation of query, if we have not handled it before */ |
|
1106 if (!qs->handled && w->parent != NULL) { |
|
1107 WindowEvent e; |
|
1108 Window *parent = w->parent; |
|
1109 |
|
1110 qs->handled = true; |
|
1111 e.event = WE_ON_EDIT_TEXT_CANCEL; |
|
1112 parent->wndproc(parent, &e); |
|
1113 } |
|
1114 CLRBIT(_no_scroll, SCROLL_EDIT); |
|
1115 break; |
|
1116 } |
|
1117 } |
|
1118 |
|
1119 static const Widget _query_string_widgets[] = { |
|
1120 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
1121 { WWT_CAPTION, RESIZE_NONE, 14, 11, 259, 0, 13, STR_012D, STR_NULL}, |
|
1122 { WWT_PANEL, RESIZE_NONE, 14, 0, 259, 14, 29, 0x0, STR_NULL}, |
|
1123 { WWT_PANEL, RESIZE_NONE, 14, 2, 257, 16, 27, 0x0, STR_NULL}, |
|
1124 { WWT_TEXTBTN, RESIZE_NONE, 14, 0, 129, 30, 41, STR_012E_CANCEL, STR_NULL}, |
|
1125 { WWT_TEXTBTN, RESIZE_NONE, 14, 130, 259, 30, 41, STR_012F_OK, STR_NULL}, |
|
1126 { WIDGETS_END}, |
|
1127 }; |
|
1128 |
|
1129 static const WindowDesc _query_string_desc = { |
|
1130 190, 219, 260, 42, |
|
1131 WC_QUERY_STRING,0, |
|
1132 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, |
|
1133 _query_string_widgets, |
|
1134 QueryStringWndProc |
|
1135 }; |
|
1136 |
|
1137 static char _edit_str_buf[64]; |
|
1138 |
|
1139 /** Show a query popup window with a textbox in it. |
|
1140 * @param str StringID for the text shown in the textbox |
|
1141 * @param caption StringID of text shown in caption of querywindow |
|
1142 * @param maxlen maximum length in characters allowed. If bit 12 is set we |
|
1143 * will not check the resulting string against to original string to return success |
|
1144 * @param maxwidth maximum width in pixels allowed |
|
1145 * @param parent pointer to a Window that will handle the events (ok/cancel) of this |
|
1146 * window. If NULL, results are handled by global function HandleOnEditText |
|
1147 * @param afilter filters out unwanted character input */ |
|
1148 void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, Window *parent, CharSetFilter afilter) |
|
1149 { |
|
1150 static char orig_str_buf[lengthof(_edit_str_buf)]; |
|
1151 Window *w; |
|
1152 uint realmaxlen = maxlen & ~0x1000; |
|
1153 |
|
1154 assert(realmaxlen < lengthof(_edit_str_buf)); |
|
1155 |
|
1156 DeleteWindowById(WC_QUERY_STRING, 0); |
|
1157 DeleteWindowById(WC_SAVELOAD, 0); |
|
1158 |
|
1159 w = AllocateWindowDesc(&_query_string_desc); |
|
1160 w->parent = parent; |
|
1161 |
|
1162 GetString(_edit_str_buf, str, lastof(_edit_str_buf)); |
|
1163 _edit_str_buf[realmaxlen - 1] = '\0'; |
|
1164 |
|
1165 if (maxlen & 0x1000) { |
|
1166 WP(w, querystr_d).orig = NULL; |
|
1167 } else { |
|
1168 strecpy(orig_str_buf, _edit_str_buf, lastof(orig_str_buf)); |
|
1169 WP(w, querystr_d).orig = orig_str_buf; |
|
1170 } |
|
1171 |
|
1172 LowerWindowWidget(w, QUERY_STR_WIDGET_TEXT); |
|
1173 WP(w, querystr_d).caption = caption; |
|
1174 WP(w, querystr_d).afilter = afilter; |
|
1175 InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, realmaxlen, maxwidth); |
|
1176 } |
|
1177 |
|
1178 |
|
1179 enum QueryWidgets { |
|
1180 QUERY_WIDGET_CAPTION = 1, |
|
1181 QUERY_WIDGET_NO = 3, |
|
1182 QUERY_WIDGET_YES |
|
1183 }; |
|
1184 |
|
1185 |
|
1186 typedef struct query_d { |
|
1187 void (*proc)(Window*, bool); ///< callback function executed on closing of popup. Window* points to parent, bool is true if 'yes' clicked, false otherwise |
|
1188 StringID message; ///< message shown for query window |
|
1189 uint32 params[20]; ///< local copy of _decode_parameters |
|
1190 bool calledback; ///< has callback been executed already (internal usage for WE_DESTROY event) |
|
1191 } query_d; |
|
1192 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(query_d)); |
|
1193 |
|
1194 |
|
1195 static void QueryWndProc(Window *w, WindowEvent *e) |
|
1196 { |
|
1197 query_d *q = &WP(w, query_d); |
|
1198 |
|
1199 switch (e->event) { |
|
1200 case WE_PAINT: |
|
1201 COPY_IN_DPARAM(0, q->params, lengthof(q->params)); |
|
1202 DrawWindowWidgets(w); |
|
1203 COPY_IN_DPARAM(0, q->params, lengthof(q->params)); |
|
1204 |
|
1205 DrawStringMultiCenter(w->width / 2, (w->height / 2) - 10, q->message, w->width); |
|
1206 break; |
|
1207 |
|
1208 case WE_CLICK: |
|
1209 switch (e->we.click.widget) { |
|
1210 case QUERY_WIDGET_YES: |
|
1211 q->calledback = true; |
|
1212 if (q->proc != NULL) q->proc(w->parent, true); |
|
1213 /* Fallthrough */ |
|
1214 case QUERY_WIDGET_NO: |
|
1215 DeleteWindow(w); |
|
1216 break; |
|
1217 } |
|
1218 break; |
|
1219 |
|
1220 case WE_KEYPRESS: /* ESC closes the window, Enter confirms the action */ |
|
1221 switch (e->we.keypress.keycode) { |
|
1222 case WKC_RETURN: |
|
1223 case WKC_NUM_ENTER: |
|
1224 q->calledback = true; |
|
1225 if (q->proc != NULL) q->proc(w->parent, true); |
|
1226 /* Fallthrough */ |
|
1227 case WKC_ESC: |
|
1228 e->we.keypress.cont = false; |
|
1229 DeleteWindow(w); |
|
1230 break; |
|
1231 } |
|
1232 break; |
|
1233 |
|
1234 case WE_DESTROY: /* Call callback function (if any) on window close if not yet called */ |
|
1235 if (!q->calledback && q->proc != NULL) { |
|
1236 q->calledback = true; |
|
1237 q->proc(w->parent, false); |
|
1238 } |
|
1239 break; |
|
1240 } |
|
1241 } |
|
1242 |
|
1243 |
|
1244 static const Widget _query_widgets[] = { |
|
1245 { WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
1246 { WWT_CAPTION, RESIZE_NONE, 4, 11, 209, 0, 13, STR_NULL, STR_NULL}, |
|
1247 { WWT_PANEL, RESIZE_NONE, 4, 0, 209, 14, 81, 0x0, /*OVERRIDE*/STR_NULL}, |
|
1248 {WWT_PUSHTXTBTN, RESIZE_NONE, 3, 20, 90, 62, 73, STR_00C9_NO, STR_NULL}, |
|
1249 {WWT_PUSHTXTBTN, RESIZE_NONE, 3, 120, 190, 62, 73, STR_00C8_YES, STR_NULL}, |
|
1250 { WIDGETS_END }, |
|
1251 }; |
|
1252 |
|
1253 static const WindowDesc _query_desc = { |
|
1254 WDP_CENTER, WDP_CENTER, 210, 82, |
|
1255 WC_CONFIRM_POPUP_QUERY, 0, |
|
1256 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_DEF_WIDGET | WDF_MODAL, |
|
1257 _query_widgets, |
|
1258 QueryWndProc |
|
1259 }; |
|
1260 |
|
1261 /** Show a modal confirmation window with standard 'yes' and 'no' buttons |
|
1262 * The window is aligned to the centre of its parent. |
|
1263 * NOTE: You cannot use BindCString as parameter for this window! |
|
1264 * @param caption string shown as window caption |
|
1265 * @param message string that will be shown for the window |
|
1266 * @param parent pointer to parent window, if this pointer is NULL the parent becomes |
|
1267 * the main window WC_MAIN_WINDOW |
|
1268 * @param x,y coordinates to show the window at |
|
1269 * @param yes_no_callback callback function called when window is closed through any button */ |
|
1270 void ShowQuery(StringID caption, StringID message, Window *parent, void (*callback)(Window*, bool)) |
|
1271 { |
|
1272 Window *w = AllocateWindowDesc(&_query_desc); |
|
1273 if (w == NULL) return; |
|
1274 |
|
1275 if (parent == NULL) parent = FindWindowById(WC_MAIN_WINDOW, 0); |
|
1276 w->parent = parent; |
|
1277 w->left = parent->left + (parent->width / 2) - (w->width / 2); |
|
1278 w->top = parent->top + (parent->height / 2) - (w->height / 2); |
|
1279 |
|
1280 /* Create a backup of the variadic arguments to strings because it will be |
|
1281 * overridden pretty often. We will copy these back for drawing */ |
|
1282 COPY_OUT_DPARAM(WP(w, query_d).params, 0, lengthof(WP(w, query_d).params)); |
|
1283 w->widget[QUERY_WIDGET_CAPTION].data = caption; |
|
1284 WP(w, query_d).message = message; |
|
1285 WP(w, query_d).proc = callback; |
|
1286 WP(w, query_d).calledback = false; |
|
1287 } |
|
1288 |
|
1289 |
|
1290 static const Widget _load_dialog_widgets[] = { |
|
1291 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
1292 { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 256, 0, 13, STR_NULL, STR_018C_WINDOW_TITLE_DRAG_THIS}, |
|
1293 { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 127, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP}, |
|
1294 { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 128, 256, 14, 25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP}, |
|
1295 { WWT_PANEL, RESIZE_RIGHT, 14, 0, 256, 26, 47, 0x0, STR_NULL}, |
|
1296 { WWT_PANEL, RESIZE_RB, 14, 0, 256, 48, 293, 0x0, STR_NULL}, |
|
1297 { WWT_PUSHIMGBTN, RESIZE_LR, 14, 245, 256, 48, 59, SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON}, |
|
1298 { WWT_INSET, RESIZE_RB, 14, 2, 243, 50, 291, 0x0, STR_400A_LIST_OF_DRIVES_DIRECTORIES}, |
|
1299 { WWT_SCROLLBAR, RESIZE_LRB, 14, 245, 256, 60, 281, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, |
|
1300 { WWT_RESIZEBOX, RESIZE_LRTB, 14, 245, 256, 282, 293, 0x0, STR_RESIZE_BUTTON}, |
|
1301 { WIDGETS_END}, |
|
1302 }; |
|
1303 |
|
1304 static const Widget _save_dialog_widgets[] = { |
|
1305 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
1306 { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 256, 0, 13, STR_NULL, STR_018C_WINDOW_TITLE_DRAG_THIS}, |
|
1307 { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 127, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP}, |
|
1308 { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 128, 256, 14, 25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP}, |
|
1309 { WWT_PANEL, RESIZE_RIGHT, 14, 0, 256, 26, 47, 0x0, STR_NULL}, |
|
1310 { WWT_PANEL, RESIZE_RB, 14, 0, 256, 48, 291, 0x0, STR_NULL}, |
|
1311 { WWT_PUSHIMGBTN, RESIZE_LR, 14, 245, 256, 48, 59, SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON}, |
|
1312 { WWT_INSET, RESIZE_RB, 14, 2, 243, 50, 290, 0x0, STR_400A_LIST_OF_DRIVES_DIRECTORIES}, |
|
1313 { WWT_SCROLLBAR, RESIZE_LRB, 14, 245, 256, 60, 291, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, |
|
1314 { WWT_PANEL, RESIZE_RTB, 14, 0, 256, 292, 307, 0x0, STR_NULL}, |
|
1315 { WWT_PANEL, RESIZE_RTB, 14, 2, 254, 294, 305, 0x0, STR_400B_CURRENTLY_SELECTED_NAME}, |
|
1316 { WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 127, 308, 319, STR_4003_DELETE, STR_400C_DELETE_THE_CURRENTLY_SELECTED}, |
|
1317 { WWT_PUSHTXTBTN, RESIZE_TB, 14, 128, 244, 308, 319, STR_4002_SAVE, STR_400D_SAVE_THE_CURRENT_GAME_USING}, |
|
1318 { WWT_RESIZEBOX, RESIZE_LRTB, 14, 245, 256, 308, 319, 0x0, STR_RESIZE_BUTTON}, |
|
1319 { WIDGETS_END}, |
|
1320 }; |
|
1321 |
|
1322 // Colors for fios types |
|
1323 const byte _fios_colors[] = {13, 9, 9, 6, 5, 6, 5, 6, 6, 8}; |
|
1324 |
|
1325 void BuildFileList(void) |
|
1326 { |
|
1327 _fios_path_changed = true; |
|
1328 FiosFreeSavegameList(); |
|
1329 |
|
1330 switch (_saveload_mode) { |
|
1331 case SLD_NEW_GAME: |
|
1332 case SLD_LOAD_SCENARIO: |
|
1333 case SLD_SAVE_SCENARIO: |
|
1334 _fios_list = FiosGetScenarioList(_saveload_mode); break; |
|
1335 case SLD_LOAD_HEIGHTMAP: |
|
1336 _fios_list = FiosGetHeightmapList(_saveload_mode); break; |
|
1337 |
|
1338 default: _fios_list = FiosGetSavegameList(_saveload_mode); break; |
|
1339 } |
|
1340 } |
|
1341 |
|
1342 static void DrawFiosTexts(uint maxw) |
|
1343 { |
|
1344 static const char *path = NULL; |
|
1345 static StringID str = STR_4006_UNABLE_TO_READ_DRIVE; |
|
1346 static uint32 tot = 0; |
|
1347 |
|
1348 if (_fios_path_changed) { |
|
1349 str = FiosGetDescText(&path, &tot); |
|
1350 _fios_path_changed = false; |
|
1351 } |
|
1352 |
|
1353 if (str != STR_4006_UNABLE_TO_READ_DRIVE) SetDParam(0, tot); |
|
1354 DrawString(2, 37, str, 0); |
|
1355 DoDrawStringTruncated(path, 2, 27, 16, maxw); |
|
1356 } |
|
1357 |
|
1358 static void MakeSortedSaveGameList(void) |
|
1359 { |
|
1360 uint sort_start = 0; |
|
1361 uint sort_end = 0; |
|
1362 uint s_amount; |
|
1363 int i; |
|
1364 |
|
1365 /* Directories are always above the files (FIOS_TYPE_DIR) |
|
1366 * Drives (A:\ (windows only) are always under the files (FIOS_TYPE_DRIVE) |
|
1367 * Only sort savegames/scenarios, not directories |
|
1368 */ |
|
1369 for (i = 0; i < _fios_num; i++) { |
|
1370 switch (_fios_list[i].type) { |
|
1371 case FIOS_TYPE_DIR: sort_start++; break; |
|
1372 case FIOS_TYPE_PARENT: sort_start++; break; |
|
1373 case FIOS_TYPE_DRIVE: sort_end++; break; |
|
1374 } |
|
1375 } |
|
1376 |
|
1377 s_amount = _fios_num - sort_start - sort_end; |
|
1378 if (s_amount > 0) |
|
1379 qsort(_fios_list + sort_start, s_amount, sizeof(FiosItem), compare_FiosItems); |
|
1380 } |
|
1381 |
|
1382 static void GenerateFileName(void) |
|
1383 { |
|
1384 /* Check if we are not a specatator who wants to generate a name.. |
|
1385 Let's use the name of player #0 for now. */ |
|
1386 const Player *p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : 0); |
|
1387 |
|
1388 SetDParam(0, p->name_1); |
|
1389 SetDParam(1, p->name_2); |
|
1390 SetDParam(2, _date); |
|
1391 GetString(_edit_str_buf, STR_4004, lastof(_edit_str_buf)); |
|
1392 } |
|
1393 |
|
1394 extern void StartupEngines(void); |
|
1395 |
|
1396 static void SaveLoadDlgWndProc(Window *w, WindowEvent *e) |
|
1397 { |
|
1398 static FiosItem o_dir; |
|
1399 |
|
1400 switch (e->event) { |
|
1401 case WE_CREATE: { /* Set up OPENTTD button */ |
|
1402 o_dir.type = FIOS_TYPE_DIRECT; |
|
1403 switch (_saveload_mode) { |
|
1404 case SLD_SAVE_GAME: |
|
1405 case SLD_LOAD_GAME: |
|
1406 ttd_strlcpy(&o_dir.name[0], _paths.save_dir, sizeof(o_dir.name)); |
|
1407 break; |
|
1408 |
|
1409 case SLD_SAVE_SCENARIO: |
|
1410 case SLD_LOAD_SCENARIO: |
|
1411 ttd_strlcpy(&o_dir.name[0], _paths.scenario_dir, sizeof(o_dir.name)); |
|
1412 break; |
|
1413 |
|
1414 case SLD_LOAD_HEIGHTMAP: |
|
1415 ttd_strlcpy(&o_dir.name[0], _paths.heightmap_dir, sizeof(o_dir.name)); |
|
1416 break; |
|
1417 |
|
1418 default: |
|
1419 ttd_strlcpy(&o_dir.name[0], _paths.personal_dir, sizeof(o_dir.name)); |
|
1420 } |
|
1421 break; |
|
1422 } |
|
1423 |
|
1424 case WE_PAINT: { |
|
1425 int pos; |
|
1426 int y; |
|
1427 |
|
1428 SetVScrollCount(w, _fios_num); |
|
1429 DrawWindowWidgets(w); |
|
1430 DrawFiosTexts(w->width); |
|
1431 |
|
1432 if (_savegame_sort_dirty) { |
|
1433 _savegame_sort_dirty = false; |
|
1434 MakeSortedSaveGameList(); |
|
1435 } |
|
1436 |
|
1437 GfxFillRect(w->widget[7].left + 1, w->widget[7].top + 1, w->widget[7].right, w->widget[7].bottom, 0xD7); |
|
1438 DoDrawString( |
|
1439 _savegame_sort_order & SORT_DESCENDING ? DOWNARROW : UPARROW, |
|
1440 _savegame_sort_order & SORT_BY_NAME ? w->widget[2].right - 9 : w->widget[3].right - 9, |
|
1441 15, 16 |
|
1442 ); |
|
1443 |
|
1444 y = w->widget[7].top + 1; |
|
1445 for (pos = w->vscroll.pos; pos < _fios_num; pos++) { |
|
1446 const FiosItem *item = _fios_list + pos; |
|
1447 |
|
1448 DoDrawStringTruncated(item->title, 4, y, _fios_colors[item->type], w->width - 18); |
|
1449 y += 10; |
|
1450 if (y >= w->vscroll.cap * 10 + w->widget[7].top + 1) break; |
|
1451 } |
|
1452 |
|
1453 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { |
|
1454 DrawEditBox(w, &WP(w,querystr_d), 10); |
|
1455 } |
|
1456 break; |
|
1457 } |
|
1458 |
|
1459 case WE_CLICK: |
|
1460 switch (e->we.click.widget) { |
|
1461 case 2: /* Sort save names by name */ |
|
1462 _savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ? |
|
1463 SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME; |
|
1464 _savegame_sort_dirty = true; |
|
1465 SetWindowDirty(w); |
|
1466 break; |
|
1467 |
|
1468 case 3: /* Sort save names by date */ |
|
1469 _savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ? |
|
1470 SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE; |
|
1471 _savegame_sort_dirty = true; |
|
1472 SetWindowDirty(w); |
|
1473 break; |
|
1474 |
|
1475 case 6: /* OpenTTD 'button', jumps to OpenTTD directory */ |
|
1476 FiosBrowseTo(&o_dir); |
|
1477 SetWindowDirty(w); |
|
1478 BuildFileList(); |
|
1479 break; |
|
1480 |
|
1481 case 7: { /* Click the listbox */ |
|
1482 int y = (e->we.click.pt.y - w->widget[e->we.click.widget].top - 1) / 10; |
|
1483 char *name; |
|
1484 const FiosItem *file; |
|
1485 |
|
1486 if (y < 0 || (y += w->vscroll.pos) >= w->vscroll.count) return; |
|
1487 |
|
1488 file = _fios_list + y; |
|
1489 |
|
1490 name = FiosBrowseTo(file); |
|
1491 if (name != NULL) { |
|
1492 if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) { |
|
1493 _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD; |
|
1494 |
|
1495 SetFiosType(file->type); |
|
1496 ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); |
|
1497 ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title)); |
|
1498 |
|
1499 DeleteWindow(w); |
|
1500 } else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) { |
|
1501 SetFiosType(file->type); |
|
1502 ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); |
|
1503 ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title)); |
|
1504 |
|
1505 DeleteWindow(w); |
|
1506 ShowHeightmapLoad(); |
|
1507 } else { |
|
1508 // SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox |
|
1509 ttd_strlcpy(WP(w, querystr_d).text.buf, file->title, WP(w, querystr_d).text.maxlength); |
|
1510 UpdateTextBufferSize(&WP(w, querystr_d).text); |
|
1511 InvalidateWidget(w, 10); |
|
1512 } |
|
1513 } else { |
|
1514 // Changed directory, need repaint. |
|
1515 SetWindowDirty(w); |
|
1516 BuildFileList(); |
|
1517 } |
|
1518 break; |
|
1519 } |
|
1520 |
|
1521 case 11: case 12: /* Delete, Save game */ |
|
1522 break; |
|
1523 } |
|
1524 break; |
|
1525 case WE_MOUSELOOP: |
|
1526 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { |
|
1527 HandleEditBox(w, &WP(w, querystr_d), 10); |
|
1528 } |
|
1529 break; |
|
1530 case WE_KEYPRESS: |
|
1531 if (e->we.keypress.keycode == WKC_ESC) { |
|
1532 DeleteWindow(w); |
|
1533 return; |
|
1534 } |
|
1535 |
|
1536 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { |
|
1537 if (HandleEditBoxKey(w, &WP(w, querystr_d), 10, e) == 1) /* Press Enter */ |
|
1538 HandleButtonClick(w, 12); |
|
1539 } |
|
1540 break; |
|
1541 case WE_TIMEOUT: |
|
1542 /* This test protects against using widgets 11 and 12 which are only available |
|
1543 * in those two saveload mode */ |
|
1544 if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) break; |
|
1545 |
|
1546 if (IsWindowWidgetLowered(w, 11)) { /* Delete button clicked */ |
|
1547 if (!FiosDelete(WP(w,querystr_d).text.buf)) { |
|
1548 ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0); |
|
1549 } else { |
|
1550 BuildFileList(); |
|
1551 /* Reset file name to current date on successfull delete */ |
|
1552 if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName(); |
|
1553 } |
|
1554 |
|
1555 UpdateTextBufferSize(&WP(w, querystr_d).text); |
|
1556 SetWindowDirty(w); |
|
1557 } else if (IsWindowWidgetLowered(w, 12)) { /* Save button clicked */ |
|
1558 _switch_mode = SM_SAVE; |
|
1559 FiosMakeSavegameName(_file_to_saveload.name, WP(w,querystr_d).text.buf, sizeof(_file_to_saveload.name)); |
|
1560 |
|
1561 /* In the editor set up the vehicle engines correctly (date might have changed) */ |
|
1562 if (_game_mode == GM_EDITOR) StartupEngines(); |
|
1563 } |
|
1564 break; |
|
1565 case WE_DESTROY: |
|
1566 // pause is only used in single-player, non-editor mode, non menu mode |
|
1567 if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) { |
|
1568 DoCommandP(0, 0, 0, NULL, CMD_PAUSE); |
|
1569 } |
|
1570 FiosFreeSavegameList(); |
|
1571 CLRBIT(_no_scroll, SCROLL_SAVE); |
|
1572 break; |
|
1573 case WE_RESIZE: { |
|
1574 /* Widget 2 and 3 have to go with halve speed, make it so obiwan */ |
|
1575 uint diff = e->we.sizing.diff.x / 2; |
|
1576 w->widget[2].right += diff; |
|
1577 w->widget[3].left += diff; |
|
1578 w->widget[3].right += e->we.sizing.diff.x; |
|
1579 |
|
1580 /* Same for widget 11 and 12 in save-dialog */ |
|
1581 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { |
|
1582 w->widget[11].right += diff; |
|
1583 w->widget[12].left += diff; |
|
1584 w->widget[12].right += e->we.sizing.diff.x; |
|
1585 } |
|
1586 |
|
1587 w->vscroll.cap += e->we.sizing.diff.y / 10; |
|
1588 } break; |
|
1589 } |
|
1590 } |
|
1591 |
|
1592 static const WindowDesc _load_dialog_desc = { |
|
1593 WDP_CENTER, WDP_CENTER, 257, 294, |
|
1594 WC_SAVELOAD,0, |
|
1595 WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, |
|
1596 _load_dialog_widgets, |
|
1597 SaveLoadDlgWndProc, |
|
1598 }; |
|
1599 |
|
1600 static const WindowDesc _save_dialog_desc = { |
|
1601 WDP_CENTER, WDP_CENTER, 257, 320, |
|
1602 WC_SAVELOAD,0, |
|
1603 WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, |
|
1604 _save_dialog_widgets, |
|
1605 SaveLoadDlgWndProc, |
|
1606 }; |
|
1607 |
|
1608 void ShowSaveLoadDialog(int mode) |
|
1609 { |
|
1610 static const StringID saveload_captions[] = { |
|
1611 STR_4001_LOAD_GAME, |
|
1612 STR_0298_LOAD_SCENARIO, |
|
1613 STR_4000_SAVE_GAME, |
|
1614 STR_0299_SAVE_SCENARIO, |
|
1615 STR_4011_LOAD_HEIGHTMAP, |
|
1616 }; |
|
1617 |
|
1618 Window *w; |
|
1619 const WindowDesc *sld = &_save_dialog_desc; |
|
1620 |
|
1621 |
|
1622 SetObjectToPlace(SPR_CURSOR_ZZZ, 0, 0, 0); |
|
1623 DeleteWindowById(WC_QUERY_STRING, 0); |
|
1624 DeleteWindowById(WC_SAVELOAD, 0); |
|
1625 |
|
1626 _saveload_mode = mode; |
|
1627 SETBIT(_no_scroll, SCROLL_SAVE); |
|
1628 |
|
1629 switch (mode) { |
|
1630 case SLD_SAVE_GAME: GenerateFileName(); break; |
|
1631 case SLD_SAVE_SCENARIO: strcpy(_edit_str_buf, "UNNAMED"); break; |
|
1632 default: sld = &_load_dialog_desc; break; |
|
1633 } |
|
1634 |
|
1635 assert((uint)mode < lengthof(saveload_captions)); |
|
1636 w = AllocateWindowDesc(sld); |
|
1637 w->widget[1].data = saveload_captions[mode]; |
|
1638 w->vscroll.cap = 24; |
|
1639 w->resize.step_width = 2; |
|
1640 w->resize.step_height = 10; |
|
1641 w->resize.height = w->height - 14 * 10; // Minimum of 10 items |
|
1642 LowerWindowWidget(w, 7); |
|
1643 |
|
1644 WP(w, querystr_d).afilter = CS_ALPHANUMERAL; |
|
1645 InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, lengthof(_edit_str_buf), 240); |
|
1646 |
|
1647 // pause is only used in single-player, non-editor mode, non-menu mode. It |
|
1648 // will be unpaused in the WE_DESTROY event handler. |
|
1649 if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) { |
|
1650 DoCommandP(0, 1, 0, NULL, CMD_PAUSE); |
|
1651 } |
|
1652 |
|
1653 BuildFileList(); |
|
1654 |
|
1655 ResetObjectToPlace(); |
|
1656 } |
|
1657 |
|
1658 void RedrawAutosave(void) |
|
1659 { |
|
1660 SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0)); |
|
1661 } |
|
1662 |
|
1663 void SetFiosType(const byte fiostype) |
|
1664 { |
|
1665 switch (fiostype) { |
|
1666 case FIOS_TYPE_FILE: |
|
1667 case FIOS_TYPE_SCENARIO: |
|
1668 _file_to_saveload.mode = SL_LOAD; |
|
1669 break; |
|
1670 |
|
1671 case FIOS_TYPE_OLDFILE: |
|
1672 case FIOS_TYPE_OLD_SCENARIO: |
|
1673 _file_to_saveload.mode = SL_OLD_LOAD; |
|
1674 break; |
|
1675 |
|
1676 #ifdef WITH_PNG |
|
1677 case FIOS_TYPE_PNG: |
|
1678 _file_to_saveload.mode = SL_PNG; |
|
1679 break; |
|
1680 #endif /* WITH_PNG */ |
|
1681 |
|
1682 case FIOS_TYPE_BMP: |
|
1683 _file_to_saveload.mode = SL_BMP; |
|
1684 break; |
|
1685 |
|
1686 default: |
|
1687 _file_to_saveload.mode = SL_INVALID; |
|
1688 break; |
|
1689 } |
|
1690 } |
|
1691 |
|
1692 static int32 ClickMoneyCheat(int32 p1, int32 p2) |
|
1693 { |
|
1694 DoCommandP(0, -10000000, 0, NULL, CMD_MONEY_CHEAT); |
|
1695 return true; |
|
1696 } |
|
1697 |
|
1698 // p1 player to set to, p2 is -1 or +1 (down/up) |
|
1699 static int32 ClickChangePlayerCheat(int32 p1, int32 p2) |
|
1700 { |
|
1701 while (IsValidPlayer((PlayerID)p1)) { |
|
1702 if (_players[p1].is_active) { |
|
1703 SetLocalPlayer((PlayerID)p1); |
|
1704 |
|
1705 MarkWholeScreenDirty(); |
|
1706 return _local_player; |
|
1707 } |
|
1708 p1 += p2; |
|
1709 } |
|
1710 |
|
1711 return _local_player; |
|
1712 } |
|
1713 |
|
1714 // p1 -1 or +1 (down/up) |
|
1715 static int32 ClickChangeClimateCheat(int32 p1, int32 p2) |
|
1716 { |
|
1717 if (p1 == -1) p1 = 3; |
|
1718 if (p1 == 4) p1 = 0; |
|
1719 _opt.landscape = p1; |
|
1720 ReloadNewGRFData(); |
|
1721 return _opt.landscape; |
|
1722 } |
|
1723 |
|
1724 extern void EnginesMonthlyLoop(void); |
|
1725 |
|
1726 // p2 1 (increase) or -1 (decrease) |
|
1727 static int32 ClickChangeDateCheat(int32 p1, int32 p2) |
|
1728 { |
|
1729 YearMonthDay ymd; |
|
1730 ConvertDateToYMD(_date, &ymd); |
|
1731 |
|
1732 if ((ymd.year == MIN_YEAR && p2 == -1) || (ymd.year == MAX_YEAR && p2 == 1)) return _cur_year; |
|
1733 |
|
1734 SetDate(ConvertYMDToDate(_cur_year + p2, ymd.month, ymd.day)); |
|
1735 EnginesMonthlyLoop(); |
|
1736 SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0)); |
|
1737 return _cur_year; |
|
1738 } |
|
1739 |
|
1740 typedef int32 CheckButtonClick(int32, int32); |
|
1741 |
|
1742 enum ce_flags {CE_CLICK = 1 << 0}; |
|
1743 |
|
1744 typedef byte ce_flags; |
|
1745 |
|
1746 typedef struct CheatEntry { |
|
1747 VarType type; // type of selector |
|
1748 ce_flags flags; // selector flags |
|
1749 StringID str; // string with descriptive text |
|
1750 void *variable; // pointer to the variable |
|
1751 bool *been_used; // has this cheat been used before? |
|
1752 CheckButtonClick *proc;// procedure |
|
1753 int16 min, max; // range for spinbox setting |
|
1754 } CheatEntry; |
|
1755 |
|
1756 static const CheatEntry _cheats_ui[] = { |
|
1757 {SLE_BOOL,CE_CLICK, STR_CHEAT_MONEY, &_cheats.money.value, &_cheats.money.been_used, &ClickMoneyCheat, 0, 0}, |
|
1758 {SLE_UINT8, 0, STR_CHEAT_CHANGE_PLAYER, &_local_player, &_cheats.switch_player.been_used, &ClickChangePlayerCheat, 0, 11}, |
|
1759 {SLE_BOOL, 0, STR_CHEAT_EXTRA_DYNAMITE, &_cheats.magic_bulldozer.value, &_cheats.magic_bulldozer.been_used, NULL, 0, 0}, |
|
1760 {SLE_BOOL, 0, STR_CHEAT_CROSSINGTUNNELS,&_cheats.crossing_tunnels.value,&_cheats.crossing_tunnels.been_used,NULL, 0, 0}, |
|
1761 {SLE_BOOL, 0, STR_CHEAT_BUILD_IN_PAUSE, &_cheats.build_in_pause.value, &_cheats.build_in_pause.been_used, NULL, 0, 0}, |
|
1762 {SLE_BOOL, 0, STR_CHEAT_NO_JETCRASH, &_cheats.no_jetcrash.value, &_cheats.no_jetcrash.been_used, NULL, 0, 0}, |
|
1763 {SLE_BOOL, 0, STR_CHEAT_SETUP_PROD, &_cheats.setup_prod.value, &_cheats.setup_prod.been_used, NULL, 0, 0}, |
|
1764 {SLE_UINT8, 0, STR_CHEAT_SWITCH_CLIMATE, &_opt.landscape, &_cheats.switch_climate.been_used, &ClickChangeClimateCheat,-1, 4}, |
|
1765 {SLE_INT32, 0, STR_CHEAT_CHANGE_DATE, &_cur_year, &_cheats.change_date.been_used, &ClickChangeDateCheat, -1, 1}, |
|
1766 }; |
|
1767 |
|
1768 |
|
1769 static const Widget _cheat_widgets[] = { |
|
1770 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, |
|
1771 { WWT_CAPTION, RESIZE_NONE, 14, 11, 399, 0, 13, STR_CHEATS, STR_018C_WINDOW_TITLE_DRAG_THIS}, |
|
1772 { WWT_PANEL, RESIZE_NONE, 14, 0, 399, 14, 169, 0x0, STR_NULL}, |
|
1773 { WWT_PANEL, RESIZE_NONE, 14, 0, 399, 14, 169, 0x0, STR_CHEATS_TIP}, |
|
1774 { WIDGETS_END}, |
|
1775 }; |
|
1776 |
|
1777 extern void DrawPlayerIcon(PlayerID pid, int x, int y); |
|
1778 |
|
1779 static void CheatsWndProc(Window *w, WindowEvent *e) |
|
1780 { |
|
1781 switch (e->event) { |
|
1782 case WE_PAINT: { |
|
1783 int clk = WP(w,def_d).data_1; |
|
1784 int x, y; |
|
1785 int i; |
|
1786 |
|
1787 DrawWindowWidgets(w); |
|
1788 |
|
1789 DrawStringMultiCenter(200, 25, STR_CHEATS_WARNING, 350); |
|
1790 |
|
1791 x = 0; |
|
1792 y = 45; |
|
1793 |
|
1794 for (i = 0; i != lengthof(_cheats_ui); i++) { |
|
1795 const CheatEntry *ce = &_cheats_ui[i]; |
|
1796 |
|
1797 DrawSprite((*ce->been_used) ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, x + 5, y + 2); |
|
1798 |
|
1799 switch (ce->type) { |
|
1800 case SLE_BOOL: { |
|
1801 bool on = (*(bool*)ce->variable); |
|
1802 |
|
1803 if (ce->flags & CE_CLICK) { |
|
1804 DrawFrameRect(x + 20, y + 1, x + 30 + 9, y + 9, 0, (clk - (i * 2) == 1) ? FR_LOWERED : 0); |
|
1805 if (i == 0) { // XXX - hack/hack for first element which is increase money. Told ya it's a mess |
|
1806 SetDParam64(0, 10000000); |
|
1807 } else { |
|
1808 SetDParam(0, false); |
|
1809 } |
|
1810 } else { |
|
1811 DrawFrameRect(x + 20, y + 1, x + 30 + 9, y + 9, on ? 6 : 4, on ? FR_LOWERED : 0); |
|
1812 SetDParam(0, on ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF); |
|
1813 } |
|
1814 } break; |
|
1815 default: { |
|
1816 int32 val = (int32)ReadValue(ce->variable, ce->type); |
|
1817 char buf[512]; |
|
1818 |
|
1819 /* Draw [<][>] boxes for settings of an integer-type */ |
|
1820 DrawArrowButtons(x + 20, y, 3, clk - (i * 2), true, true); |
|
1821 |
|
1822 switch (ce->str) { |
|
1823 /* Display date for change date cheat */ |
|
1824 case STR_CHEAT_CHANGE_DATE: SetDParam(0, _date); break; |
|
1825 /* Draw colored flag for change player cheat */ |
|
1826 case STR_CHEAT_CHANGE_PLAYER: |
|
1827 SetDParam(0, val); |
|
1828 GetString(buf, STR_CHEAT_CHANGE_PLAYER, lastof(buf)); |
|
1829 DrawPlayerIcon(_current_player, 60 + GetStringBoundingBox(buf).width, y + 2); |
|
1830 break; |
|
1831 /* Set correct string for switch climate cheat */ |
|
1832 case STR_CHEAT_SWITCH_CLIMATE: val += STR_TEMPERATE_LANDSCAPE; |
|
1833 /* Fallthrough */ |
|
1834 default: SetDParam(0, val); |
|
1835 } |
|
1836 } break; |
|
1837 } |
|
1838 |
|
1839 DrawString(50, y + 1, ce->str, 0); |
|
1840 |
|
1841 y += 12; |
|
1842 } |
|
1843 break; |
|
1844 } |
|
1845 |
|
1846 case WE_CLICK: { |
|
1847 const CheatEntry *ce; |
|
1848 uint btn = (e->we.click.pt.y - 46) / 12; |
|
1849 int32 value, oldvalue; |
|
1850 uint x = e->we.click.pt.x; |
|
1851 |
|
1852 // not clicking a button? |
|
1853 if (!IS_INT_INSIDE(x, 20, 40) || btn >= lengthof(_cheats_ui)) break; |
|
1854 |
|
1855 ce = &_cheats_ui[btn]; |
|
1856 oldvalue = value = (int32)ReadValue(ce->variable, ce->type); |
|
1857 |
|
1858 *ce->been_used = true; |
|
1859 |
|
1860 switch (ce->type) { |
|
1861 case SLE_BOOL: |
|
1862 if (ce->flags & CE_CLICK) WP(w,def_d).data_1 = btn * 2 + 1; |
|
1863 value ^= 1; |
|
1864 if (ce->proc != NULL) ce->proc(value, 0); |
|
1865 break; |
|
1866 default: { |
|
1867 /* Add a dynamic step-size to the scroller. In a maximum of |
|
1868 * 50-steps you should be able to get from min to max */ |
|
1869 uint16 step = ((ce->max - ce->min) / 20); |
|
1870 if (step == 0) step = 1; |
|
1871 |
|
1872 /* Increase or decrease the value and clamp it to extremes */ |
|
1873 value += (x >= 30) ? step : -step; |
|
1874 clamp(value, ce->min, ce->max); |
|
1875 |
|
1876 // take whatever the function returns |
|
1877 value = ce->proc(value, (x >= 30) ? 1 : -1); |
|
1878 |
|
1879 if (value != oldvalue) { |
|
1880 WP(w,def_d).data_1 = btn * 2 + 1 + ((x >= 30) ? 1 : 0); |
|
1881 } |
|
1882 } break; |
|
1883 } |
|
1884 |
|
1885 if (value != oldvalue) { |
|
1886 WriteValue(ce->variable, ce->type, (int64)value); |
|
1887 SetWindowDirty(w); |
|
1888 } |
|
1889 |
|
1890 w->flags4 |= 5 << WF_TIMEOUT_SHL; |
|
1891 |
|
1892 SetWindowDirty(w); |
|
1893 } |
|
1894 break; |
|
1895 case WE_TIMEOUT: |
|
1896 WP(w,def_d).data_1 = 0; |
|
1897 SetWindowDirty(w); |
|
1898 break; |
|
1899 } |
|
1900 } |
|
1901 |
|
1902 static const WindowDesc _cheats_desc = { |
|
1903 240, 22, 400, 170, |
|
1904 WC_CHEATS,0, |
|
1905 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, |
|
1906 _cheat_widgets, |
|
1907 CheatsWndProc |
|
1908 }; |
|
1909 |
|
1910 |
|
1911 void ShowCheatWindow(void) |
|
1912 { |
|
1913 DeleteWindowById(WC_CHEATS, 0); |
|
1914 AllocateWindowDesc(&_cheats_desc); |
|
1915 } |