1 #include <math.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <stdarg.h>
6 #include <limits.h>
7 #include <float.h>
8 #include <errno.h>
9 //For stat() etc.. below
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #ifndef _MSC_VER
13 #include <unistd.h>
14 #endif //_MSC_VER
15 #include "user_menus.h"
16
17 #ifdef MAP_EDITOR
18 #include "map_editor/global.h"
19 #include "map_editor/browser.h"
20 #include "map_editor/interface.h"
21 #include "load_gl_extensions.h"
22 #else
23 #include "achievements.h"
24 #include "alphamap.h"
25 #include "bags.h"
26 #include "buddy.h"
27 #include "chat.h"
28 #include "console.h"
29 #include "context_menu.h"
30 #include "counters.h"
31 #include "cursors.h"
32 #include "dialogues.h"
33 #include "draw_scene.h"
34 #include "emotes.h"
35 #include "errors.h"
36 #include "elwindows.h"
37 #include "filter.h"
38 #include "gamewin.h"
39 #include "gl_init.h"
40 #include "hud.h"
41 #include "hud_statsbar_window.h"
42 #include "hud_indicators.h"
43 #include "hud_misc_window.h"
44 #include "hud_quickbar_window.h"
45 #include "hud_quickspells_window.h"
46 #include "icon_window.h"
47 #include "init.h"
48 #include "interface.h"
49 #include "items.h"
50 #include "item_info.h"
51 #ifdef JSON_FILES
52 #include "json_io.h"
53 #endif
54 #include "loginwin.h"
55 #include "manufacture.h"
56 #include "map.h"
57 #include "mapwin.h"
58 #include "missiles.h"
59 #include "multiplayer.h"
60 #include "new_character.h"
61 #include "openingwin.h"
62 #include "particles.h"
63 #include "password_manager.h"
64 #include "pm_log.h"
65 #include "questlog.h"
66 #include "reflection.h"
67 #include "serverpopup.h"
68 #include "session.h"
69 #include "shadows.h"
70 #include "sound.h"
71 #include "spells.h"
72 #include "stats.h"
73 #include "storage.h"
74 #include "tabs.h"
75 #include "trade.h"
76 #include "trade_log.h"
77 #include "weather.h"
78 #include "minimap.h"
79 #ifdef NEW_ALPHA
80 #include "3d_objects.h"
81 #endif
82 #include "io/elpathwrapper.h"
83 #include "notepad.h"
84 #include "sky.h"
85 #ifdef OSX
86 #include "events.h"
87 #endif // OSX
88 #endif
89
90 #include "asc.h"
91 #include "elconfig.h"
92 #ifndef MAP_EDITOR
93 #include "text.h"
94 #include "consolewin.h"
95 #endif // !MAP_EDITOR
96 #include "url.h"
97 #if !defined(MAP_EDITOR)
98 #include "widgets.h"
99 #endif
100
101 #include "eye_candy_wrapper.h"
102 #include "sendvideoinfo.h"
103 #ifndef MAP_EDITOR
104 #include "actor_init.h"
105 #endif
106 #include "io/elpathwrapper.h"
107 #include "textures.h"
108 #ifdef FSAA
109 #include "fsaa/fsaa.h"
110 #endif /* FSAA */
111 #ifdef CUSTOM_UPDATE
112 #include "custom_update.h"
113 #endif /* CUSTOM_UPDATE */
114
115 // Defines for config variables
116 #define CONTROLS 0
117 #define HUD 1
118 #define CHAT 2
119 #define FONT 3
120 #define SERVER 4
121 #define AUDIO 5
122 #define VIDEO 6
123 #define GFX 7
124 #define CAMERA 8
125 #define TROUBLESHOOT 9
126
127
128 #ifdef DEBUG
129 #define DEBUGTAB 10
130 #define MAX_TABS 11
131 #else
132 #define MAX_TABS 10
133 #endif
134
135 const char * ini_filename = "el.ini";
136
137 #ifdef ELC
138 static int CHECKBOX_SIZE = 0;
139 static int SPACING = 0; //Space between widgets and labels and lines
140 #define MAX_LONG_DESC_LINES 3 //How many lines of text we can fit in LONG_DESC_SPACE
141 static int LONG_DESC_SPACE = 0; //Space to give to the long descriptions
142 static int TAB_TAG_HEIGHT = 0; // the height of the tab at the top of the window
143 // The config window is special, it is scaled on creation then frozen.
144 // This is because its too complex to resize and cannot simpely be destroyed and re-created.
145 static float elconf_scale = 0;
146 static float elconf_custom_scale = 1.0f;
147 static float elconf_desc_size = 0.0f;
148 static float elconf_desc_max_height = 0.0f;
149 static int recheck_window_scale = 0;
150 #define ELCONFIG_SCALED_VALUE(BASE) ((int)(0.5 + ((BASE) * elconf_scale)))
151 #endif
152
153 #define MULTI_LINE_HEIGHT (ELCONFIG_SCALED_VALUE(22) + SPACING)
154
155 typedef char input_line[256];
156
157 /*!
158 * Structure describing an option in a multi-elect widget. It contains the
159 * label, which is the text on the button presented to the user, and optionally a
160 * value. Using the value, options can also be sought by value rather than
161 * by index alone, and hence we don't have to rely on a particular ordering
162 * of elements, or on all elements being present.
163 */
164 typedef struct
165 {
166 //! The user-visible lable for the option
167 const char* label;
168 //! The optional value associated with this option
169 const char* value;
170 } multi_element;
171
172 /*!
173 * var_struct stores the data for a single configuration entry.
174 */
175 typedef struct
176 {
177 option_type type; /*!< type of the variable */
178 char *name; /*!< name of the variable */
179 int nlen; /*!< length of the \a name */
180 char *shortname; /*!< shortname of the variable */
181 int snlen; /*!< length of the \a shortname */
182 void (*func)(); /*!< routine to execute when this variable is selected. */
183 void *var; /*!< data for this variable */
184 float default_val; /*!< the default value before the config file is read */
185 float config_file_val; /*!< the value after the config file is read */
186 int len; /*!< length of the variable */
187 int in_ini_file; /*!< true if the var was present when we read the ini file */
188 #ifdef JSON_FILES
189 int character_override; /*!< true if the var is in the list maintained per character, requires JSON file support */
190 #endif
191 int saved;
192 // char *message; /*!< In case you want a message to be written when a setting is changed */
193 dichar display;
194 struct {
195 int tab_id; /*!< The tab ID in which we find this option */
196 int label_id; /*!< The label ID associated with this option */
197 int widget_id; /*!< Widget ID for things like checkboxes */
198 } widgets;
199 union
200 {
201 struct { int min; int max; } imm;
202 struct { int (*min)(); int (*max)(); } immf;
203 struct { float min; float max; float interval; } fmmi;
204 struct { float (*min)(); float (*max)(); float interval; } fmmif;
205 struct { multi_element *elems; size_t count; } multi;
206 } args; /*!< The various versions of additional arguments used by configuration variables */
207 } var_struct;
208
209 /*!
210 * a list of variables of type \see var_struct
211 */
212 struct variables
213 {
214 int no; /*!< current number of allocated \see var_struct in \a var */
215 var_struct * * var; /*!< fixed array of \a no \see var_struct structures */
216 } our_vars= {0,NULL};
217
218 /*!
219 * For some multi-select widgets (currently only the font selections, it seems),
220 * not all possible options are available at the time the ini file is read. Rather than
221 * blindly assuming the option will later be added, setting these options is
222 * deferred to a later stage after the widget has been fully initialized.
223 */
224 typedef struct
225 {
226 //! The index of the multiselect variable in \see our_vars.
227 int var_idx;
228 //! The previously stored index of the selected option.
229 int opt_idx;
230 //! The previously stored value of the selected option, or NULL if not present.
231 char* value;
232 } deferred_option;
233
234 //! The list of multi-select options that still need to be set
235 static deferred_option *defers = NULL;
236 //! The size of the \see defers array
237 static int defers_size = 0;
238 //! The number of elements currently used in the \see defers array
239 static int defers_count = 0;
240 //! Whether to still defer options
241 static int do_defer = 1;
242
243
244 int write_ini_on_exit= 1;
245 // Window Handling
246 int force_elconfig_win_ontop = 0;
247 #ifdef ELC
248 static int elconfig_tab_collection_id= 1;
249 static int elconfig_free_widget_id= 2;
250 static unsigned char elconf_description_buffer[400]= {0};
251 static int last_description_idx = -1;
252 #endif
253 struct {
254 Sint32 tab;
255 Uint16 x;
256 Uint16 y;
257 } elconfig_tabs[MAX_TABS];
258
259 int windows_on_top= 0;
260 static int options_set= 0;
261 #ifdef ELC
262 static int delay_poor_man = 1;
263 static int shadow_map_size_multi= 0;
264 #endif
265 #ifdef FSAA
266 static int fsaa_index = 0;
267 #endif /* FSAA */
268
269 static float ui_scale = 1.0;
get_global_scale(void)270 float get_global_scale(void) { return ui_scale; }
271
272 int you_sit= 0;
273 int sit_lock= 0;
274 int use_keypress_dialogue_boxes = 0, use_full_dialogue_window = 0;
275 int use_alpha_banner = 0;
276 int render_skeleton= 0;
277 int render_mesh= 1;
278 int render_bones_id = 0;
279 int render_bones_orientation = 0;
280 #ifdef NEW_CURSOR
281 int big_cursors = 0;
282 int sdl_cursors = 0;
283 float pointer_size = 1.0;
284 #endif // NEW_CURSOR
285 float water_tiles_extension = 200.0;
286 int show_game_seconds = 0;
287 int skybox_update_delay = 10;
288 int skybox_local_weather = 0;
289
290 #ifdef OSX // for probelem with rounded buttons on Intel graphics
291 int emulate3buttonmouse=0;
292 int square_buttons = 0;
293 #endif
294
295 int buddy_log_notice=1;
296 int video_mode_set=0;
297 int video_info_sent = 0;
298 int no_adjust_shadows=0;
299 int clouds_shadows=1;
300 int item_window_on_drop=1;
301 int mouse_limit=15;
302 int isometric=1;
303 int poor_man=0;
304 int max_fps = 0, limit_fps=0;
305 int special_effects=0;
306 char lang[10] = "en";
307 int auto_update= 1;
308 int clear_mod_keys_on_focus=1;
309
310 #ifdef CUSTOM_UPDATE
311 int custom_update= 1;
312 int custom_clothing= 1;
313 #endif //CUSTOM_UPDATE
314
315 #ifdef DEBUG
316 int enable_client_aiming = 0;
317 #endif // DEBUG
318
319 #ifdef ANTI_ALIAS
320 int anti_alias=0;
321 #endif //ANTI_ALIAS
322
323 #ifdef ELC
324 static int small_actor_texture_cache = 0;
325 static void consolidate_rotate_chat_log_status(void);
326 static int elconfig_menu_x_len= 0;
327 static int elconfig_menu_y_len= 0;
328 static int is_mouse_over_option = 0;
329 static int is_mouse_over_option_label = 0;
330 static char multiselect_find_str[32] = { 0 };
331 static size_t multiselect_find_str_len = 0;
332 static int multiselect_find_show = 0;
333 static int multiselect_find_casesensitive = 0;
334 static int multiselect_find_not_found = 0;
335
336 static int disable_auto_highdpi_scale = 0;
337 static int delay_update_highdpi_auto_scaling = 0;
338 // the elconfig local version of the font sizes, so we can auto scale if needed
339 static float local_ui_scale = 1.0f;
340 static float local_minimap_size_coefficient = 0.7f;
341 static size_t local_encyclopedia_font = 0;
342 // the chat and name fonts are not scaled by ui_scale
343 // so we need to scale them individually for high dpi support
344 static float chat_font_local_scale = 1.0;
345 static float name_font_local_scale = 1.0;
346
347 // default fonts will be replaced if values present the ini file
348 static char def_ui_font_str[80] = "20(Ubuntu-R.ttf)";
349 static char def_name_font_str[80] = "20(Ubuntu-R.ttf)";
350 static char def_chat_font_str[80] = "20(Ubuntu-R.ttf)";
351 static char def_note_font_str[80] = "20(Ubuntu-R.ttf)";
352 static char def_book_font_str[80] = "7(MarckScript-Regular.ttf)";
353 static char def_rules_font_str[80] = "20(Ubuntu-R.ttf)";
354 static char def_encyclopedia_font_str[80] = "8(UbuntuMono-R.ttf)";
355
356 #ifdef JSON_FILES
357 // Most of this code can be removed when json file use if the default of the only supported client
358
359 static int find_var (const char *str, var_name_type type);
360 static int use_json_user_files = 0;
361 static int ready_for_user_files = 0;
362
363 // Set when we initially login, before calling load functions
set_ready_for_user_files(void)364 void set_ready_for_user_files(void)
365 {
366 ready_for_user_files = 1;
367 }
368
369 // Returns true if we are using json files.
get_use_json_user_files(void)370 int get_use_json_user_files(void)
371 {
372 static int first_time = 1;
373
374 // If this is the first time we have run with the use_json_user_files
375 // option then check if we should switch it on. The option is false
376 // by default.
377 if (first_time && !use_json_user_files)
378 {
379 int i = find_var("use_json_user_files_v1", INI_FILE_VAR);
380
381 first_time = 0;
382
383 // should always be fine as add_var() done earlier
384 if (i < 0)
385 return 0;
386
387 // if the option was not in the read ini file, check for previous use of json files or a clean install
388 if (!our_vars.var[i]->in_ini_file)
389 {
390 char filename[256];
391 struct stat json_file_stat, bin_file_stat;
392 int json_stat_ret, bin_stat_ret;
393
394 // Use the counters file as not replaceable and is always saved
395 safe_snprintf(filename, sizeof(filename), "%scounters_%s.json", get_path_config(), get_lowercase_username());
396 json_stat_ret = stat(filename, &json_file_stat);
397 safe_snprintf(filename, sizeof(filename), "%scounters_%s.dat", get_path_config(), get_lowercase_username());
398 bin_stat_ret = stat(filename, &bin_file_stat);
399
400 // No bin file so switch on json usage even if no json file either - prevous or clean install
401 if (bin_stat_ret == -1)
402 {
403 use_json_user_files = 1;
404 USE_JSON_DEBUG("Setting use_json_user_files as no binary file exists");
405 }
406 // If there are both json and binary files, pick the most recently modified or the json if the same
407 else if ((json_stat_ret == 0) && (json_file_stat.st_mtime >= bin_file_stat.st_mtime))
408 {
409 use_json_user_files = 1;
410 USE_JSON_DEBUG("Setting use_json_user_files as json file newer")
411 }
412 else // only binary files to leave option disabled
413 {
414 USE_JSON_DEBUG("Leaving set to binary");
415 }
416
417 // Make sure the option is saved to file, to avoid having to check again next run
418 set_var_unsaved("use_json_user_files_v1", INI_FILE_VAR);
419 }
420 }
421
422 return use_json_user_files;
423 }
424 #endif // JSON_FILES
425
426 #endif // ELC
427
options_loaded(void)428 void options_loaded(void)
429 {
430 size_t i;
431 // find any options not marked as saved, excluding ones we marked on purpose
432 for (i= 0; i < our_vars.no; i++)
433 if ((!our_vars.var[i]->saved) && (our_vars.var[i]->type!=OPT_BOOL_INI) && (our_vars.var[i]->type!=OPT_INT_INI))
434 our_vars.var[i]->saved = 1;
435 options_set = 1;
436 #ifdef ELC
437 get_rotate_chat_log();
438 consolidate_rotate_chat_log_status();
439 #endif
440 }
441
442 #ifdef ELC
int_zero_func(void)443 static int int_zero_func(void)
444 {
445 return 0;
446 }
447 #endif
448
449 static __inline__ void check_option_var(const char* name);
450
destroy_shadow_mapping(void)451 static __inline__ void destroy_shadow_mapping(void)
452 {
453 if (gl_extensions_loaded)
454 {
455 CHECK_GL_ERRORS();
456 if (have_extension(ext_framebuffer_object))
457 {
458 #ifndef MAP_EDITOR
459 free_shadow_framebuffer();
460 #endif //MAP_EDITOR
461 }
462 else
463 {
464 if (depth_map_id != 0)
465 {
466 glDeleteTextures(1, &depth_map_id);
467 depth_map_id = 0;
468 }
469 }
470 CHECK_GL_ERRORS();
471 }
472 }
473
destroy_fbos(void)474 static __inline__ void destroy_fbos(void)
475 {
476 if (gl_extensions_loaded)
477 {
478 CHECK_GL_ERRORS();
479 if (have_extension(ext_framebuffer_object))
480 {
481 #ifndef MAP_EDITOR
482 destroy_shadow_mapping();
483 free_reflection_framebuffer();
484 #endif //MAP_EDITOR
485 }
486 CHECK_GL_ERRORS();
487 }
488 }
489
build_fbos(void)490 static __inline__ void build_fbos(void)
491 {
492 if (gl_extensions_loaded)
493 {
494 #ifndef MAP_EDITOR
495 if (have_extension(ext_framebuffer_object) && use_frame_buffer)
496 {
497 if ((water_shader_quality > 0) && show_reflection)
498 {
499 make_reflection_framebuffer(window_width, window_height);
500 }
501 }
502 #endif // MAP_EDITOR
503 check_option_var("shadow_map_size");
504 }
505 }
506
update_fbos(void)507 static __inline__ void update_fbos(void)
508 {
509 destroy_fbos();
510 build_fbos();
511 }
512
change_var(int * var)513 static void change_var(int * var)
514 {
515 *var= !*var;
516 }
517
518 #ifndef MAP_EDITOR
change_cursor_scale_factor(int * var,int value)519 static void change_cursor_scale_factor(int * var, int value)
520 {
521 // check the range, an invalid setting in the ini file bypasses the bound checking of that var
522 if ((value > 0) && (value <= max_cursor_scale_factor))
523 {
524 *var= value;
525 if (current_cursor >= 0)
526 {
527 build_cursors();
528 change_cursor(current_cursor);
529 }
530 }
531 }
532
change_show_action_bar(int * var)533 static void change_show_action_bar(int * var)
534 {
535 *var= !*var;
536 if (stats_bar_win >= 0)
537 init_stats_display();
538 }
539
change_minimap_scale(float * var,float * value)540 static void change_minimap_scale(float * var, float * value)
541 {
542 int minimap_win = get_id_MW(MW_MINIMAP);
543 int shown = 0;
544 float last_minimap_size_coefficient = minimap_size_coefficient;
545 *var= *value;
546 minimap_size_coefficient = ((disable_auto_highdpi_scale)) ? *var : get_highdpi_scale() * *var;
547 if (last_minimap_size_coefficient != minimap_size_coefficient && minimap_win >= 0)
548 {
549 shown = get_show_window(minimap_win);
550 set_pos_MW(MW_MINIMAP, windows_list.window[minimap_win].cur_x, windows_list.window[minimap_win].cur_y);
551 destroy_window(minimap_win);
552 set_id_MW(MW_MINIMAP, -1);
553 }
554 if (shown)
555 display_minimap();
556 }
557
change_sky_var(int * var)558 static void change_sky_var(int * var)
559 {
560 *var= !*var;
561 skybox_update_colors();
562 }
563
change_use_animation_program(int * var)564 static void change_use_animation_program(int * var)
565 {
566 if (*var)
567 {
568 if (gl_extensions_loaded && have_extension(arb_vertex_buffer_object) &&
569 have_extension(arb_vertex_program))
570 {
571 unload_vertex_programs();
572 }
573 *var = 0;
574 }
575 else
576 {
577 if (!gl_extensions_loaded)
578 {
579 *var = 1;
580 }
581 else
582 {
583 if (have_extension(arb_vertex_buffer_object) &&
584 have_extension(arb_vertex_program))
585 {
586 *var = load_vertex_programs();
587 }
588 }
589 }
590 if (gl_extensions_loaded)
591 {
592 if (use_animation_program)
593 {
594 LOG_TO_CONSOLE(c_green2, "Using vertex program for actor animation.");
595 }
596 else
597 {
598 LOG_TO_CONSOLE(c_red1, "Not using vertex program for actor animation.");
599 }
600 }
601 }
602 #endif //MAP_EDITOR
603
604 #ifndef MAP_EDITOR
change_min_ec_framerate(float * var,float * value)605 static void change_min_ec_framerate(float * var, float * value)
606 {
607 if(*value >= 0) {
608 if (*value < max_ec_framerate) {
609 *var = *value;
610 } else if (!max_ec_framerate) {
611 *var = *value;
612 } else {
613 *var = max_ec_framerate - 1;
614 }
615 } else {
616 *var= 0;
617 }
618 }
619
change_max_ec_framerate(float * var,float * value)620 static void change_max_ec_framerate(float * var, float * value)
621 {
622 if(*value >= 1) {
623 if (*value > min_ec_framerate) {
624 *var = *value;
625 } else if (!min_ec_framerate) {
626 *var = *value;
627 } else {
628 *var = min_ec_framerate + 1;
629 }
630 } else {
631 *var= 1;
632 }
633 }
634 #endif //!MAP_EDITOR
635
change_int(int * var,int value)636 static void change_int(int * var, int value)
637 {
638 if(value>=0) *var= value;
639 }
640
change_float(float * var,float * value)641 static void change_float(float * var, float * value)
642 {
643 *var= *value;
644 }
645
646 #ifdef ELC
647
change_string(char * var,const char * str,int len)648 static void change_string(char* var, const char* str, int len)
649 {
650 safe_strncpy(var, str, len);
651 }
652
change_ui_scale(float * var,float * value)653 static void change_ui_scale(float *var, float *value)
654 {
655 *var= *value;
656 ui_scale = ((disable_auto_highdpi_scale)) ? *var : get_highdpi_scale() * *var;
657 HUD_MARGIN_X = (int)ceilf(ui_scale * 64.0);
658 if (hud_x != 0)
659 hud_x = HUD_MARGIN_X;
660 HUD_MARGIN_Y = (int)ceilf(ui_scale * 49.0);
661 if (hud_y != 0)
662 hud_y = HUD_MARGIN_Y;
663
664 update_windows_scale(ui_scale);
665 update_console_input_size_and_position();
666 }
667
change_elconf_win_scale_factor(float * var,float * value)668 static void change_elconf_win_scale_factor(float *var, float *value)
669 {
670 *var= *value;
671 if (get_id_MW(MW_CONFIG) >= 0)
672 recheck_window_scale = 1;
673 }
674
change_win_scale_factor(float * var,float * value)675 static void change_win_scale_factor(float *var, float *value)
676 {
677 *var= *value;
678 update_windows_custom_scale(var);
679 }
680
681 static const float win_scale_min = 0.25f;
682 static const float win_scale_max = 3.0f;
683 static const float win_scale_step = 0.01f;
684
find_win_scale_factor(float * changed_window_custom_scale)685 static var_struct * find_win_scale_factor(float *changed_window_custom_scale)
686 {
687 if (changed_window_custom_scale != NULL)
688 {
689 size_t i;
690 for (i = 0; i < our_vars.no; i++)
691 if (our_vars.var[i]->var == changed_window_custom_scale)
692 return our_vars.var[i];
693 }
694 return NULL;
695 }
696
step_win_scale_factor(int increase,float * changed_window_custom_scale)697 void step_win_scale_factor(int increase, float *changed_window_custom_scale)
698 {
699 if (changed_window_custom_scale != NULL)
700 {
701 var_struct * var = find_win_scale_factor(changed_window_custom_scale);
702 float new_value = *changed_window_custom_scale + ((increase) ? win_scale_step : -win_scale_step);
703 if (new_value >= win_scale_min && new_value <= win_scale_max)
704 {
705 *changed_window_custom_scale = new_value;
706 update_windows_custom_scale(changed_window_custom_scale);
707 }
708 if (var != NULL)
709 var->saved = 0;
710 }
711 }
712
limit_win_scale_to_default(float * changed_window_custom_scale)713 void limit_win_scale_to_default(float *changed_window_custom_scale)
714 {
715 var_struct * var = find_win_scale_factor(changed_window_custom_scale);
716 if ((var != NULL) && (*changed_window_custom_scale > var->default_val))
717 {
718 *changed_window_custom_scale = var->default_val;
719 update_windows_custom_scale(changed_window_custom_scale);
720 var->saved = 0;
721 }
722 }
723
reset_win_scale_factor(int set_default,float * changed_window_custom_scale)724 void reset_win_scale_factor(int set_default, float *changed_window_custom_scale)
725 {
726 var_struct * var = find_win_scale_factor(changed_window_custom_scale);
727 if (var != NULL)
728 {
729 *changed_window_custom_scale = (set_default) ? var->default_val : var->config_file_val;
730 update_windows_custom_scale(changed_window_custom_scale);
731 var->saved = 0;
732 }
733 }
734
735 /*
736 * The chat logs are created very early on in the client start up, before the
737 * ini file is read at least. Because of this, a simple ini file variable
738 * can not be used to enabled/disable rotating chat log files. I suppose the init
739 * code could be changed but rather do that and unleash all kinds of grief, use a
740 * simple flag file when the chat logs are opened. The ini file setting now
741 * becomes the creater/remover of that file.
742 */
743 static int rotate_chat_log_config_var = 0;
744 static int rotate_chat_log = -1;
745 static const char* rotate_chat_log_flag_file = "rotate_chat_log_enabled";
746
747 /* get the current value depending on if the flag file exists */
get_rotate_chat_log(void)748 int get_rotate_chat_log(void)
749 {
750 if (rotate_chat_log == -1)
751 rotate_chat_log = (file_exists_config(rotate_chat_log_flag_file)==1) ?1: 0;
752 return rotate_chat_log;
753 }
754
755 /* create or delete the flag file to reflect the ini file rotate chat log value */
change_rotate_chat_log(int * value)756 static void change_rotate_chat_log(int *value)
757 {
758 *value = !*value;
759
760 /* create the flag file if we are switching on chat log rotate */
761 if (*value && (file_exists_config(rotate_chat_log_flag_file)!=1))
762 {
763 FILE *fp = open_file_config(rotate_chat_log_flag_file,"w");
764 if ((fp == NULL) || (fclose(fp) != 0))
765 LOG_ERROR("%s: Failed to create [%s] [%s]\n", __FILE__, rotate_chat_log_flag_file, strerror(errno) );
766 else
767 LOG_TO_CONSOLE(c_green2, rotate_chat_log_restart_str);
768 }
769 /* remove the flag file if we are switching off chat log rotate */
770 else if (!(*value) && (file_exists_config(rotate_chat_log_flag_file)==1))
771 {
772 const char *config_dir = get_path_config();
773 char *name_buf = NULL;
774 if (config_dir != NULL)
775 {
776 size_t buf_len = strlen(config_dir) + strlen(rotate_chat_log_flag_file) + 1;
777 name_buf = (char *)malloc(buf_len);
778 if (name_buf != NULL)
779 {
780 safe_strncpy(name_buf, config_dir, buf_len);
781 safe_strcat(name_buf, rotate_chat_log_flag_file, buf_len);
782 if (unlink(name_buf) != 0)
783 LOG_ERROR("%s: Failed to remove [%s] [%s]\n", __FILE__, rotate_chat_log_flag_file, strerror(errno) );
784 else
785 LOG_TO_CONSOLE(c_green2, rotate_chat_log_restart_str);
786 free(name_buf);
787 }
788 }
789 }
790 }
791
792 /* called after the el.in file has been read, so we can consolidate the rotate chat log status */
consolidate_rotate_chat_log_status(void)793 static void consolidate_rotate_chat_log_status(void)
794 {
795 /* it is too late to use a newly set rotate log value, but we can set the ini flag if rotating is on */
796 if ((rotate_chat_log==1) && !rotate_chat_log_config_var)
797 {
798 rotate_chat_log_config_var = 1;
799 set_var_unsaved("rotate_chat_log", INI_FILE_VAR);
800 }
801 }
802
803 #ifdef NEW_SOUND
change_sound_level(float * var,float * value)804 static void change_sound_level(float *var, float * value)
805 {
806 if(*value >= 0.0f && *value <= 1.0f+0.00001) {
807 *var= (float)*value;
808 } else {
809 *var=0;
810 }
811 }
812 #endif
813
update_max_actor_texture_handles(void)814 static void update_max_actor_texture_handles(void)
815 {
816 if (poor_man == 1)
817 {
818 if (small_actor_texture_cache == 1)
819 {
820 max_actor_texture_handles = 1;
821 }
822 else
823 {
824 max_actor_texture_handles = 4;
825 }
826 }
827 else
828 {
829 if (small_actor_texture_cache == 1)
830 {
831 max_actor_texture_handles = 16;
832 }
833 else
834 {
835 max_actor_texture_handles = 32;
836 }
837 }
838 }
839
change_compiled_vertex_array(int * value)840 static void change_compiled_vertex_array(int *value)
841 {
842 if (*value) {
843 *value= 0;
844 }
845 else if (!gl_extensions_loaded || have_extension(ext_compiled_vertex_array))
846 {
847 // don't check if we have hardware support when OpenGL
848 // extensions are not initialized yet.
849 *value= 1;
850 }
851 else LOG_TO_CONSOLE(c_green2,disabled_compiled_vertex_arrays);
852 }
853
change_vertex_buffers(int * value)854 static void change_vertex_buffers(int *value)
855 {
856 if (*value) {
857 *value= 0;
858 }
859 else if (!gl_extensions_loaded || have_extension(arb_vertex_buffer_object))
860 {
861 // don't check if we have hardware support when OpenGL
862 // extensions are not initialized yet.
863 *value= 1;
864 }
865 // else LOG_TO_CONSOLE(c_green2,disabled_vertex_buffers);
866 }
867
change_clouds_shadows(int * value)868 static void change_clouds_shadows(int *value)
869 {
870 if (*value) {
871 *value= 0;
872 }
873 else if (!gl_extensions_loaded || (get_texture_units() >= 2))
874 {
875 // don't check if we have hardware support when OpenGL
876 // extensions are not initialized yet.
877 *value= 1;
878 }
879 // else LOG_TO_CONSOLE(c_green2,disabled_clouds_shadows);
880 }
881
change_small_actor_texture_cache(int * value)882 static void change_small_actor_texture_cache(int *value)
883 {
884 if (*value)
885 {
886 *value = 0;
887 }
888 else
889 {
890 *value = 1;
891 }
892
893 update_max_actor_texture_handles();
894 }
895
change_eye_candy(int * value)896 static void change_eye_candy(int *value)
897 {
898 if (*value)
899 {
900 *value = 0;
901 }
902 else if (!gl_extensions_loaded || ((get_texture_units() >= 2) &&
903 supports_gl_version(1, 5)))
904 {
905 // don't check if we have hardware support when OpenGL
906 // extensions are not initialized yet.
907 *value = 1;
908 }
909 }
910
change_point_particles(int * value)911 static void change_point_particles(int *value)
912 {
913 if (*value) {
914 *value= 0;
915 }
916 else if (!gl_extensions_loaded || have_extension(arb_point_sprite))
917 {
918 // don't check if we have hardware support when OpenGL
919 // extensions are not initialized yet.
920 *value= 1;
921 }
922 else
923 {
924 LOG_TO_CONSOLE(c_green2, disabled_point_particles);
925 }
926 }
927
change_fps(int * var,int value)928 static void change_fps(int * var, int value)
929 {
930 if(value>=0) max_fps = *var = value;
931 }
932
change_particles_percentage(int * pointer,int value)933 static void change_particles_percentage(int *pointer, int value)
934 {
935 if(value>0 && value <=100) {
936 particles_percentage= value;
937 }
938 else
939 {
940 particles_percentage= 0;
941 LOG_TO_CONSOLE(c_green2, disabled_particles_str);
942 }
943 }
944
change_new_selection(int * value)945 static void change_new_selection(int *value)
946 {
947 if (*value)
948 {
949 *value = 0;
950 }
951 else
952 {
953 if (gl_extensions_loaded)
954 {
955 if ((supports_gl_version(1, 3) ||
956 have_extension(arb_texture_env_combine)) &&
957 (get_texture_units() > 1) && (bpp == 32))
958 {
959 *value = 1;
960 }
961 }
962 else
963 {
964 *value = 1;
965 }
966 }
967 }
968
switch_vidmode(int * pointer,int mode)969 static void switch_vidmode(int *pointer, int mode)
970 {
971 if(!video_mode_set) {
972 /* Video isn't ready yet, just remember the mode */
973 video_mode= mode;
974 } else {
975 full_screen = 0;
976 switch_video(mode, full_screen);
977 }
978 }
979
toggle_full_screen_mode(int * fs)980 static void toggle_full_screen_mode(int * fs)
981 {
982 if(!video_mode_set) {
983 *fs= !*fs;
984 } else {
985 toggle_full_screen();
986 }
987 }
988
989 #ifdef NEW_CURSOR
change_sdl_cursor(int * fs)990 static void change_sdl_cursor(int * fs)
991 {
992 if(!*fs) {
993 SDL_ShowCursor(1);
994 } else {
995 SDL_ShowCursor(0);
996 }
997 *fs = !*fs;
998 }
999 #endif // NEW_CURSOR
1000
toggle_follow_cam(int * fc)1001 void toggle_follow_cam(int * fc)
1002 {
1003 last_kludge=camera_kludge;
1004 if (*fc)
1005 hold_camera=rz;
1006 else
1007 hold_camera+=camera_kludge;
1008 change_var(fc);
1009 }
1010
toggle_follow_cam_behind(int * fc)1011 static void toggle_follow_cam_behind(int * fc)
1012 {
1013 if (*fc)
1014 {
1015 last_kludge = camera_kludge;
1016 hold_camera += camera_kludge;
1017 }
1018 else
1019 {
1020 last_kludge = -rz;
1021 }
1022 change_var(fc);
1023 }
1024
toggle_ext_cam(int * ec)1025 void toggle_ext_cam(int * ec)
1026 {
1027 change_var(ec);
1028 if (*ec)
1029 {
1030 isometric = 0;
1031 if (video_mode_set)
1032 {
1033 resize_root_window();
1034 set_all_intersect_update_needed(main_bbox_tree);
1035 }
1036 }
1037 }
1038
change_tilt_float(float * var,float * value)1039 static void change_tilt_float(float * var, float * value)
1040 {
1041 *var= *value;
1042 if (rx > -min_tilt_angle) rx = -min_tilt_angle;
1043 else if (rx < -max_tilt_angle) rx = -max_tilt_angle;
1044 }
1045
change_shadow_map_size(int * pointer,int value)1046 static void change_shadow_map_size(int *pointer, int value)
1047 {
1048 const int array[10]= {256, 512, 768, 1024, 1280, 1536, 1792, 2048, 3072, 4096};
1049 int index, size, i, max_size, error;
1050 char error_str[1024];
1051
1052 if (value >= array[0])
1053 {
1054 index = 0;
1055 for (i = 0; i < 10; i++)
1056 {
1057 /* Check if we can set the multiselect widget to this */
1058 if (array[i] == value)
1059 {
1060 index = i;
1061 break;
1062 }
1063 }
1064 }
1065 else
1066 {
1067 index = min2i(max2i(0, value), 9);
1068 }
1069
1070 size = array[index];
1071
1072 if (gl_extensions_loaded && use_shadow_mapping)
1073 {
1074 error= 0;
1075
1076 if (use_frame_buffer)
1077 {
1078 glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &max_size);
1079 }
1080 else
1081 {
1082 max_size = min2i(window_width, window_height);
1083 }
1084
1085 if (size > max_size)
1086 {
1087 while ((size > max_size) && (index > 0))
1088 {
1089 index--;
1090 size = array[index];
1091 }
1092 error= 1;
1093 }
1094
1095 if (!(have_extension(arb_texture_non_power_of_two) || supports_gl_version(2, 0)))
1096 {
1097 switch (index)
1098 {
1099 case 2:
1100 index = 1;
1101 error = 1;
1102 break;
1103 case 4:
1104 case 5:
1105 case 6:
1106 index = 3;
1107 error = 1;
1108 break;
1109 case 8:
1110 index = 7;
1111 error = 1;
1112 break;
1113 }
1114 size = array[index];
1115 }
1116 if (error == 1)
1117 {
1118 memset(error_str, 0, sizeof(error_str));
1119 safe_snprintf(error_str, sizeof(error_str),
1120 shadow_map_size_not_supported_str, size);
1121 LOG_TO_CONSOLE(c_yellow2, error_str);
1122 }
1123 shadow_map_size = size;
1124
1125 destroy_shadow_mapping();
1126 if (have_extension(ext_framebuffer_object) && use_frame_buffer)
1127 {
1128 make_shadow_framebuffer();
1129 }
1130 }
1131 else
1132 {
1133 shadow_map_size = size;
1134 }
1135
1136 if (pointer != NULL)
1137 {
1138 *pointer= index;
1139 }
1140 }
1141
1142 #ifdef FSAA
change_fsaa(int * pointer,int value)1143 static void change_fsaa(int *pointer, int value)
1144 {
1145 unsigned int i, index, fsaa_value;
1146
1147 index = 0;
1148 fsaa_value = 0;
1149
1150 if (value > 0)
1151 {
1152 for (i = 1; i < get_fsaa_mode_count(); i++)
1153 {
1154 if (get_fsaa_mode(i))
1155 {
1156 index++;
1157 fsaa_value = i;
1158 }
1159 if (value == index)
1160 {
1161 break;
1162 }
1163 }
1164 }
1165
1166 fsaa = fsaa_value;
1167
1168 if (pointer != 0)
1169 {
1170 *pointer = index;
1171 }
1172 }
1173 #endif /* FSAA */
1174
1175 #ifdef CUSTOM_UPDATE
change_custom_update(int * var)1176 static void change_custom_update(int *var)
1177 {
1178 *var = !*var;
1179
1180 if (*var)
1181 {
1182 start_custom_update();
1183 }
1184 }
1185
change_custom_clothing(int * var)1186 static void change_custom_clothing(int *var)
1187 {
1188 *var = !*var;
1189 unload_actor_texture_cache();
1190 }
1191 #endif //CUSTOM_UPDATE
1192
1193 #ifndef MAP_EDITOR2
set_afk_time(int * pointer,int time)1194 static void set_afk_time(int *pointer, int time)
1195 {
1196 if(time > 0) {
1197 afk_time= time*60000;
1198 *pointer= time;
1199 } else {
1200 afk_time= 0;
1201 *pointer= 0;
1202 }
1203 }
1204
set_buff_icon_size(int * pointer,int value)1205 static void set_buff_icon_size(int *pointer, int value)
1206 {
1207 /* The value is actually set in the widget code so attempting so controlling the
1208 range here does not work. Instead, use the built in max/min code of the widget.
1209 We still need to set the value here for the initial read from the config file. */
1210 *pointer = value;
1211 /* Turn off icons when the size is zero (or at least low). */
1212 view_buffs = (value < 5) ?0: 1;
1213 }
1214
1215 #ifdef JSON_FILES
change_use_json_user_files(int * var)1216 static void change_use_json_user_files(int *var)
1217 {
1218 *var= !*var;
1219 if (ready_for_user_files)
1220 {
1221 if (*var) // character options have no non-json equlivavnt
1222 load_character_options();
1223 save_local_data();
1224 LOG_TO_CONSOLE(c_green1, local_only_save_str);
1225 }
1226 else
1227 USE_JSON_DEBUG("Not ready for user files");
1228 }
1229 #endif
1230
change_dark_channeltext(int * dct,int value)1231 static void change_dark_channeltext(int *dct, int value)
1232 {
1233 *dct = value;
1234 if (*dct == 1)
1235 set_text_message_color (&input_text_line, 0.6f, 0.6f, 0.6f);
1236 else if (*dct == 2)
1237 set_text_message_color (&input_text_line, 0.16f, 0.16f, 0.16f);
1238 else
1239 set_text_message_color (&input_text_line, 1.0f, 1.0f, 1.0f);
1240 }
1241
change_windowed_chat(int * wc,int val)1242 void change_windowed_chat (int *wc, int val)
1243 {
1244 int old_wc= *wc;
1245
1246 *wc= val;
1247 if (*wc == 1)
1248 {
1249 if (game_root_win >= 0)
1250 {
1251 display_tab_bar ();
1252 }
1253 }
1254 else if (tab_bar_win >= 0)
1255 {
1256 hide_window (tab_bar_win);
1257 }
1258
1259 if (*wc == 2)
1260 {
1261 if (game_root_win >= 0)
1262 {
1263 display_chat();
1264 }
1265 }
1266 else if (get_id_MW(MW_CHAT) >= 0)
1267 {
1268 hide_window (get_id_MW(MW_CHAT));
1269 }
1270
1271 if (old_wc != *wc && (old_wc == 1 || old_wc == 2) )
1272 {
1273 convert_tabs (*wc);
1274 }
1275
1276 enable_chat_shown();
1277 }
1278
change_enable_chat_show_hide(int * var)1279 static void change_enable_chat_show_hide(int * var)
1280 {
1281 *var= !*var;
1282 enable_chat_shown();
1283 }
1284
change_console_input_at_top(int * var)1285 static void change_console_input_at_top(int * var)
1286 {
1287 *var= !*var;
1288 update_console_input_size_and_position();
1289 }
1290
change_max_chat_lines(int * var,int value)1291 static void change_max_chat_lines(int * var, int value)
1292 {
1293 if(value>=0) *var= value;
1294 enable_chat_shown();
1295 }
1296
change_quickbar_relocatable(int * rel)1297 static void change_quickbar_relocatable (int *rel)
1298 {
1299 *rel= !*rel;
1300 if (get_id_MW(MW_QUICKBAR) >= 0)
1301 {
1302 init_quickbar ();
1303 }
1304 }
1305
change_quickspells_relocatable(int * rel)1306 static void change_quickspells_relocatable (int *rel)
1307 {
1308 *rel= !*rel;
1309 if (get_id_MW(MW_QUICKSPELLS) >= 0)
1310 {
1311 init_quickspell ();
1312 }
1313 }
1314
change_font(size_t * var,int value)1315 static void change_font(size_t *var, int value)
1316 {
1317 if (value >= 0)
1318 {
1319 *var = value;
1320 // Yes, this is ugly. I just really did not want to introduce a ton of
1321 // separate function for each font category.
1322 if (var >= font_idxs && var < font_idxs + NR_FONT_CATS)
1323 {
1324 font_cat cat = var - font_idxs;
1325 change_windows_font(cat);
1326 if (cat == UI_FONT)
1327 {
1328 // The mapmark font uses the same font as the user interface, but has its own
1329 /// scale factor
1330 font_idxs[MAPMARK_FONT] = value;
1331 change_windows_font(MAPMARK_FONT);
1332 }
1333 }
1334 else if (var == &local_encyclopedia_font)
1335 {
1336 font_idxs[ENCYCLOPEDIA_FONT] = get_fixed_width_font_number(value);
1337 change_windows_font(ENCYCLOPEDIA_FONT);
1338 }
1339 }
1340 }
1341
change_text_zoom(float * var,const float * value)1342 static void change_text_zoom(float *var, const float *value)
1343 {
1344 float val = *value;
1345 if (val > 0.0)
1346 {
1347 if (val < 0.1)
1348 val = 0.1;
1349
1350 // these fonts are scaled by ui_scale so we do not have to apply the high dpi scale
1351 *var = val;
1352 // Yes, this is ugly. I just really did not want to introduce a ton of
1353 // separate function for each font category.
1354 if (var >= font_scales && var < font_scales + NR_FONT_CATS)
1355 {
1356 font_cat cat = var - font_scales;
1357 change_windows_font(cat);
1358 }
1359 }
1360 }
1361
change_chat_zoom(float * var,float * value)1362 static void change_chat_zoom(float *var, float *value)
1363 {
1364 if (*value < 0.0f)
1365 return;
1366 *var = *value;
1367
1368 // this font is not scaled by ui_scale so we have to apply the high dpi scale
1369 font_scales[CHAT_FONT] = (disable_auto_highdpi_scale) ? *value : get_highdpi_scale() * *value;
1370 change_windows_font(CHAT_FONT);
1371 update_console_input_zoom();
1372 }
1373
change_name_zoom(float * var,float * value)1374 static void change_name_zoom(float *var, float *value)
1375 {
1376 if (*value < 0.0f)
1377 return;
1378 *var = *value;
1379
1380 // this font is not scaled by ui_scale so we have to apply the high dpi scale
1381 font_scales[NAME_FONT] = (disable_auto_highdpi_scale) ? *value : get_highdpi_scale() * *value;
1382 change_windows_font(NAME_FONT);
1383 }
1384
1385 #ifdef TTF
change_use_ttf(int * var)1386 static void change_use_ttf(int *var)
1387 {
1388 *var = !*var;
1389 if (*var)
1390 enable_ttf();
1391 else
1392 disable_ttf();
1393 }
1394 #endif
1395
update_highdpi_auto_scaling(void)1396 void update_highdpi_auto_scaling(void)
1397 {
1398 change_chat_zoom(&chat_font_local_scale, &chat_font_local_scale);
1399 change_name_zoom(&name_font_local_scale, &name_font_local_scale);
1400 change_ui_scale(&local_ui_scale, &local_ui_scale);
1401 change_minimap_scale(&local_minimap_size_coefficient, &local_minimap_size_coefficient);
1402 }
1403
change_disable_auto_highdpi_scale(int * var)1404 static void change_disable_auto_highdpi_scale(int * var)
1405 {
1406 *var= !*var;
1407 if (!delay_update_highdpi_auto_scaling)
1408 update_highdpi_auto_scaling();
1409 }
1410
1411 #endif // MAP_EDITOR2
1412 #endif // def ELC
1413
change_dir_name(char * var,const char * str,int len)1414 static void change_dir_name (char *var, const char *str, int len)
1415 {
1416 int idx;
1417
1418 for (idx= 0; idx < len && str[idx]; idx++) {
1419 var[idx]= str[idx];
1420 }
1421 if (var[idx-1] != '/') {
1422 var[idx++]= '/';
1423 }
1424 var[idx]= '\0';
1425 }
1426
1427 #ifdef ANTI_ALIAS
change_aa(int * pointer)1428 static void change_aa(int *pointer) {
1429 change_var(pointer);
1430 if (anti_alias) {
1431 glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
1432 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
1433 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
1434 glEnable(GL_POINT_SMOOTH);
1435 glEnable(GL_LINE_SMOOTH);
1436 glEnable(GL_POLYGON_SMOOTH);
1437 } else {
1438 glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
1439 glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
1440 glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
1441 glDisable(GL_POINT_SMOOTH);
1442 glDisable(GL_LINE_SMOOTH);
1443 glDisable(GL_POLYGON_SMOOTH);
1444 }
1445 #ifdef OPENGL_TRACE
1446 CHECK_GL_ERRORS();
1447 #endif //OPENGL_TRACE
1448 }
1449 #endif // ANTI_ALIAS
1450 #ifdef ELC
1451
change_projection_float(float * var,float * value)1452 static void change_projection_float(float * var, float * value) {
1453 change_float(var, value);
1454 if (video_mode_set)
1455 {
1456 resize_root_window ();
1457 set_all_intersect_update_needed (main_bbox_tree);
1458 }
1459 }
1460
change_projection_bool(int * pointer)1461 static void change_projection_bool(int *pointer) {
1462 change_var(pointer);
1463 if (video_mode_set)
1464 {
1465 resize_root_window ();
1466 if (pointer == &isometric && isometric) ext_cam = 0;
1467 set_all_intersect_update_needed (main_bbox_tree);
1468 }
1469 }
1470
change_gamma(float * pointer,float * value)1471 static void change_gamma(float *pointer, float *value)
1472 {
1473 *pointer= *value;
1474 if(video_mode_set && !disable_gamma_adjust) {
1475 SDL_SetWindowBrightness(el_gl_window, *value);
1476 }
1477 }
1478
1479 #ifndef MAP_EDITOR2
change_windows_on_top(int * var)1480 void change_windows_on_top(int *var)
1481 {
1482 enum managed_window_enum i;
1483 *var=!*var;
1484
1485 if (*var)
1486 {
1487 for (i = 0; i < MW_MAX; i++)
1488 {
1489 int win_id = get_id_MW(i);
1490 if (on_top_responsive_MW(i) && (win_id >= 0) && (win_id < windows_list.num_windows))
1491 {
1492 window_info *win = &windows_list.window[win_id];
1493 /* Change the root windows */
1494 move_window(win_id, -1, 0, win->pos_x, win->pos_y );
1495 /* Display any open windows */
1496 if (win->displayed != 0 || win->reinstate != 0)
1497 show_window(win_id);
1498 }
1499 }
1500 }
1501 else
1502 {
1503 // Change the root windows
1504 for (i = 0; i < MW_MAX; i++)
1505 {
1506 int win_id = get_id_MW(i);
1507 if (on_top_responsive_MW(i) && (win_id >= 0) && (win_id < windows_list.num_windows))
1508 {
1509 window_info *win = &windows_list.window[win_id];
1510 move_window(win_id, game_root_win, 0, win->pos_x, win->pos_y );
1511 }
1512 }
1513 // Hide all the windows if needed
1514 if (windows_list.window[game_root_win].displayed == 0)
1515 hide_window(game_root_win);
1516 }
1517 }
1518 #endif
1519
1520 #ifndef MAP_EDITOR2
change_separate_flag(int * pointer)1521 static void change_separate_flag(int * pointer) {
1522 change_var(pointer);
1523
1524 if (get_id_MW(MW_CHAT) >= 0) {
1525 update_chat_win_buffers();
1526 }
1527 }
1528 #endif
1529
change_shadow_mapping(int * sm)1530 static void change_shadow_mapping (int *sm)
1531 {
1532 if (*sm)
1533 {
1534 *sm= 0;
1535 }
1536 else
1537 {
1538 // don't check if we have hardware support when OpenGL
1539 // extensions are not initialized yet.
1540 if (!gl_extensions_loaded || ((get_texture_units() >= 3) &&
1541 have_extension(arb_shadow) && have_extension(arb_texture_env_combine)))
1542 {
1543 *sm= 1;
1544 }
1545 else
1546 {
1547 LOG_TO_CONSOLE (c_red1, disabled_shadow_mapping);
1548 }
1549 }
1550 update_fbos();
1551 }
1552
1553 #ifndef MAP_EDITOR2
change_global_filters(int * use)1554 static void change_global_filters (int *use)
1555 {
1556 *use= !*use;
1557 // load global filters when new value is true, but only when changed
1558 // in game, not on startup
1559 if (options_set && *use) {
1560 load_filters_list ("global_filters.txt", 0);
1561 }
1562 }
1563 #endif //ndef MAP_EDITOR2
1564
1565 #endif // ELC
1566
1567 #ifndef MAP_EDITOR
change_reflection(int * rf)1568 static void change_reflection(int *rf)
1569 {
1570 *rf= !*rf;
1571 update_fbos();
1572 }
1573
change_frame_buffer(int * fb)1574 static void change_frame_buffer(int *fb)
1575 {
1576 if (*fb)
1577 {
1578 *fb= 0;
1579 }
1580 else
1581 {
1582 if (!gl_extensions_loaded || have_extension(ext_framebuffer_object))
1583 {
1584 *fb= 1;
1585 }
1586 else
1587 {
1588 LOG_TO_CONSOLE (c_red1, disabled_framebuffer);
1589 }
1590 }
1591 update_fbos();
1592 }
1593 #endif
1594
1595 #ifndef MAP_EDITOR
change_shadows(int * sh)1596 static void change_shadows(int *sh)
1597 {
1598 *sh= !*sh;
1599 update_fbos();
1600 }
1601
int_max_water_shader_quality(void)1602 static int int_max_water_shader_quality(void)
1603 {
1604 if (gl_extensions_loaded)
1605 {
1606 return get_max_supported_water_shader_quality();
1607 }
1608 else
1609 {
1610 return 2;
1611 }
1612 }
1613
change_water_shader_quality(int * wsq,int value)1614 static void change_water_shader_quality(int *wsq, int value)
1615 {
1616 if (gl_extensions_loaded)
1617 {
1618 *wsq = min2i(max2i(value, 0), get_max_supported_water_shader_quality());
1619 }
1620 else
1621 {
1622 *wsq = value;
1623 }
1624 update_fbos();
1625 }
1626 #endif
1627
1628 #ifdef MAP_EDITOR
1629
set_auto_save_interval(int * save_time,int time)1630 static void set_auto_save_interval (int *save_time, int time)
1631 {
1632 if(time>0) {
1633 *save_time= time*60000;
1634 } else {
1635 *save_time= 0;
1636 }
1637 }
1638
switch_vidmode(int * pointer,int mode)1639 static void switch_vidmode(int *pointer, int mode)
1640 {
1641 switch(mode)
1642 {
1643 case 1: window_width=640;
1644 window_height=480;
1645 return;
1646 case 2: window_width=780;
1647 window_height=550;
1648 return;
1649 case 3:
1650 window_width=990;
1651 window_height=720;
1652 return;
1653 case 4:
1654 window_width=1070;
1655 window_height=785;
1656 return;
1657 case 5:
1658 window_width=1250;
1659 window_height=990;
1660 return;
1661 case 6:
1662 window_width=1600;
1663 window_height=1200;
1664 return;
1665 case 7:
1666 window_width=1280;
1667 window_height=800;
1668 default:
1669 return;
1670 }
1671 }
1672
1673 #endif
1674
find_var(const char * str,var_name_type type)1675 static int find_var(const char *str, var_name_type type)
1676 {
1677 size_t i, len_to_check;
1678
1679 len_to_check = strcspn(str, " =");
1680 for (i = 0; i < our_vars.no; i++)
1681 {
1682 const var_struct *var = our_vars.var[i];
1683 const char *var_name = (type != COMMAND_LINE_SHORT_VAR) ? var->name : var->shortname;
1684
1685 if (strncmp(str, var_name, len_to_check) == 0 && var_name[len_to_check] == '\0')
1686 return i;
1687 }
1688
1689 return -1;
1690 }
1691
get_option_description(const char * str,var_name_type type)1692 const char *get_option_description(const char *str, var_name_type type)
1693 {
1694 int var_index = find_var(str, type);
1695 if (var_index == -1)
1696 {
1697 LOG_ERROR("Can't find var '%s', type %d", str, type);
1698 return NULL;
1699 }
1700 return (const char *)our_vars.var[var_index]->display.desc;
1701 }
1702
set_var_unsaved(const char * str,var_name_type type)1703 int set_var_unsaved(const char *str, var_name_type type)
1704 {
1705 int var_index = find_var(str, type);
1706 if (var_index == -1)
1707 {
1708 LOG_ERROR("Can't find var '%s', type %d", str, type);
1709 return 0;
1710 }
1711 our_vars.var[var_index]->saved = 0;
1712 return 1;
1713 }
1714
toggle_OPT_BOOL_by_name(const char * str)1715 int toggle_OPT_BOOL_by_name(const char *str)
1716 {
1717 int var_index = find_var(str, INI_FILE_VAR);
1718 if ((var_index == -1) || (our_vars.var[var_index]->type != OPT_BOOL))
1719 {
1720 fprintf(stderr, "%s(): Invalid OPT_BOOL '%s'\n", __FUNCTION__, str);
1721 return 0;
1722 }
1723 our_vars.var[var_index]->func(our_vars.var[var_index]->var);
1724 our_vars.var[var_index]->saved= 0;
1725 return 1;
1726 }
1727
1728 #ifdef ELC
1729 // find an OPT_INT ot OPT_INT_F widget and set its's value
set_var_OPT_INT(const char * str,int new_value)1730 int set_var_OPT_INT(const char *str, int new_value)
1731 {
1732 int var_index = find_var(str, INI_FILE_VAR);
1733
1734 if ((var_index != -1) && ((our_vars.var[var_index]->type == OPT_INT) || (our_vars.var[var_index]->type == OPT_INT_F)))
1735 {
1736 int tab_win_id = elconfig_tabs[our_vars.var[var_index]->widgets.tab_id].tab;
1737 int widget_id = our_vars.var[var_index]->widgets.widget_id;
1738 // This bit belongs in the widgets module
1739 widget_list *widget = widget_find(tab_win_id, widget_id);
1740 our_vars.var[var_index]->func(our_vars.var[var_index]->var, new_value);
1741 our_vars.var[var_index]->saved = 0;
1742 if(widget != NULL && widget->widget_info != NULL)
1743 {
1744 spinbutton *button = widget->widget_info;
1745 *(int *)button->data = new_value;
1746 safe_snprintf(button->input_buffer, sizeof(button->input_buffer), "%i", *(int *)button->data);
1747 return 1;
1748 }
1749
1750 return 0;
1751 }
1752
1753 LOG_ERROR("Can't find var '%s', type 'OPT_INT'", str);
1754 return 0;
1755 }
1756
1757 // find an OPT_FLOAT widget and set its's value
set_var_OPT_FLOAT(const char * str,float new_value)1758 static int set_var_OPT_FLOAT(const char *str, float new_value)
1759 {
1760 int var_index = find_var(str, INI_FILE_VAR);
1761
1762 if ((var_index != -1) && (our_vars.var[var_index]->type == OPT_FLOAT))
1763 {
1764 int tab_win_id = elconfig_tabs[our_vars.var[var_index]->widgets.tab_id].tab;
1765 int widget_id = our_vars.var[var_index]->widgets.widget_id;
1766 // This bit belongs in the widgets module
1767 widget_list *widget = widget_find(tab_win_id, widget_id);
1768 our_vars.var[var_index]->func(our_vars.var[var_index]->var, &new_value);
1769 our_vars.var[var_index]->saved = 0;
1770 if(widget != NULL && widget->widget_info != NULL)
1771 {
1772 spinbutton *button = widget->widget_info;
1773 *(float *)button->data = new_value;
1774 safe_snprintf(button->input_buffer, sizeof(button->input_buffer), "%.2f", *(float *)button->data);
1775 return 1;
1776 }
1777
1778 return 0;
1779 }
1780
1781 LOG_ERROR("Can't find var '%s', type 'OPT_FLOAT'", str);
1782 return 0;
1783 }
1784
set_var_OPT_BOOL(const char * str,int new_value)1785 static int set_var_OPT_BOOL(const char *str, int new_value)
1786 {
1787 int var_index = find_var(str, INI_FILE_VAR);
1788
1789 if ((var_index != -1) && ((our_vars.var[var_index]->type == OPT_BOOL) || (our_vars.var[var_index]->type == OPT_BOOL_INI)))
1790 {
1791 var_struct *option = our_vars.var[var_index];
1792 if (*(int *)option->var != new_value)
1793 {
1794 *(int *)option->var = !new_value;
1795 option->func(option->var);
1796 option->saved = 0;
1797 }
1798 }
1799
1800 LOG_ERROR("Can't find var '%s', type 'OPT_BOOL'", str);
1801 return 0;
1802 }
1803
1804
set_var_OPT_MULTI(const char * str,size_t new_value)1805 static int set_var_OPT_MULTI(const char *str, size_t new_value)
1806 {
1807 int var_index = find_var(str, INI_FILE_VAR);
1808
1809 if ((var_index != -1) && ((our_vars.var[var_index]->type == OPT_MULTI) || (our_vars.var[var_index]->type == OPT_MULTI_H)))
1810 {
1811 var_struct *option = our_vars.var[var_index];
1812 int window_id = elconfig_tabs[option->widgets.tab_id].tab;
1813 int widget_id = option->widgets.widget_id;
1814 size_t max_sel = option->args.multi.count;
1815 if (new_value >= max_sel)
1816 {
1817 LOG_ERROR("Invalid value '%lu' for var '%s', type 'OPT_MULTI*' max '%lu'", new_value, str, max_sel);
1818 return 0;
1819 }
1820 option->func(option->var, new_value);
1821 option->saved = 0;
1822 if ((window_id > 0) && (widget_id > 0) && (widget_find(window_id, widget_id) != NULL))
1823 multiselect_set_selected(window_id, option->widgets.widget_id, new_value);
1824 return 1;
1825 }
1826
1827 LOG_ERROR("Can't find var '%s', type 'OPT_MULTI'", str);
1828 return 0;
1829 }
1830
get_option_initial_value(const char * longname)1831 static float get_option_initial_value(const char *longname)
1832 {
1833 int var_index = find_var(longname, COMMAND_LINE_LONG_VAR);
1834 if (var_index == -1)
1835 {
1836 LOG_ERROR("Can't find longname var '%s'", longname);
1837 return -1;
1838 }
1839 return our_vars.var[var_index]->config_file_val;
1840 }
1841
action_poor_man(int * poor_man)1842 static void action_poor_man(int *poor_man)
1843 {
1844 unload_texture_cache();
1845 update_max_actor_texture_handles();
1846 if(*poor_man) {
1847 show_reflection= 0;
1848 shadows_on= 0;
1849 clouds_shadows= 0;
1850 use_shadow_mapping= 0;
1851 #ifndef MAP_EDITOR2
1852 special_effects= 0;
1853 use_eye_candy = 0;
1854 use_fog= 0;
1855 show_weather = 0;
1856 #endif
1857 #ifndef MAP_EDITOR
1858 use_frame_buffer= 0;
1859 #endif
1860 skybox_show_clouds = 0;
1861 skybox_show_sun = 0;
1862 skybox_show_moons = 0;
1863 skybox_show_stars = 0;
1864 if (far_plane > 50)
1865 {
1866 far_plane = 50;
1867 change_projection_float(&far_plane, &far_plane);
1868 }
1869 }
1870 else
1871 {
1872 show_reflection = get_option_initial_value("show_reflection");
1873 shadows_on= get_option_initial_value("shadows_on");
1874 clouds_shadows= get_option_initial_value("clouds_shadows");
1875 use_shadow_mapping= get_option_initial_value("use_shadow_mapping");
1876 #ifndef MAP_EDITOR2
1877 special_effects= get_option_initial_value("special_effects");
1878 use_eye_candy = get_option_initial_value("use_eye_candy");
1879 use_fog= get_option_initial_value("render_fog");
1880 show_weather = get_option_initial_value("show_weather");
1881 #endif
1882 #ifndef MAP_EDITOR
1883 use_frame_buffer= get_option_initial_value("use_frame_buffer");
1884 #endif
1885 skybox_show_clouds = get_option_initial_value("skybox_show_clouds");
1886 skybox_show_sun = get_option_initial_value("skybox_show_sun");
1887 skybox_show_moons = get_option_initial_value("skybox_show_moons");
1888 skybox_show_stars = get_option_initial_value("skybox_show_stars");
1889 far_plane = get_option_initial_value("far_plane");
1890 change_projection_float(&far_plane, &far_plane);
1891 }
1892 update_fbos();
1893 }
1894
change_poor_man(int * poor_man)1895 static void change_poor_man(int *poor_man)
1896 {
1897 *poor_man= !*poor_man;
1898 if (!delay_poor_man)
1899 action_poor_man(poor_man);
1900 }
1901
1902 static size_t cm_id = CM_INIT_VALUE;
1903
1904 // set the new value form the label option's context menu
context_option_handler(window_info * win,int widget_id,int mx,int my,int menu_option)1905 static int context_option_handler(window_info *win, int widget_id, int mx, int my, int menu_option)
1906 {
1907 float new_value = 0;
1908 var_struct *option = (var_struct *)cm_get_data(cm_id);
1909 if (menu_option == 1)
1910 new_value = option->default_val;
1911 else if (menu_option == 2)
1912 new_value = option->config_file_val;
1913 else
1914 return 1;
1915
1916 switch (option->type)
1917 {
1918 case OPT_INT:
1919 case OPT_INT_F:
1920 set_var_OPT_INT(option->name, (int)new_value);
1921 break;
1922 case OPT_MULTI:
1923 case OPT_MULTI_H:
1924 set_var_OPT_MULTI(option->name, (size_t)new_value);
1925 break;
1926 case OPT_BOOL:
1927 set_var_OPT_BOOL(option->name, (int)new_value);
1928 break;
1929 case OPT_FLOAT:
1930 set_var_OPT_FLOAT(option->name, new_value);
1931 break;
1932 default:
1933 break;
1934 }
1935 return 1;
1936 }
1937
1938 // add a named preset value to the option's label context menu
add_cm_option_line(const char * prefix,var_struct * option,float value)1939 static int add_cm_option_line(const char *prefix, var_struct *option, float value)
1940 {
1941 char menu_text[256];
1942 switch (option->type)
1943 {
1944 case OPT_INT:
1945 case OPT_INT_F:
1946 case OPT_MULTI:
1947 case OPT_MULTI_H:
1948 safe_snprintf(menu_text, sizeof(menu_text), "\n%s: %d\n", prefix, (int)value);
1949 break;
1950 case OPT_BOOL:
1951 safe_snprintf(menu_text, sizeof(menu_text), "\n%s: %s\n", prefix, ((int)value) ?"true": "false");
1952 break;
1953 case OPT_FLOAT:
1954 safe_snprintf(menu_text, sizeof(menu_text), "\n%s: %g\n", prefix, value);
1955 break;
1956 default:
1957 return 0;
1958 }
1959 cm_add(cm_id, menu_text, NULL);
1960 return 1;
1961 }
1962
1963 // create the context menu when we right click an option label
call_option_menu(var_struct * option)1964 static void call_option_menu(var_struct *option)
1965 {
1966 if (cm_id == CM_INIT_VALUE)
1967 {
1968 cm_id = cm_create(NULL, NULL);
1969 }
1970 cm_set_colour(cm_id, CM_GREY, 201.0/256.0, 254.0/256.0, 203.0/256.0);
1971 cm_set(cm_id, option->name, context_option_handler);
1972 cm_grey_line(cm_id, 0, 1);
1973 if (add_cm_option_line(cm_options_default_str, option, option->default_val))
1974 {
1975 add_cm_option_line(cm_options_initial_str, option, option->config_file_val);
1976 #ifdef JSON_FILES
1977 if (get_use_json_user_files() && ready_for_user_files)
1978 {
1979 cm_add(cm_id, cm_options_per_character_str, NULL);
1980 cm_bool_line(cm_id, 3, &option->character_override, NULL);
1981 }
1982 #endif
1983 cm_set_data(cm_id, (void *)option);
1984 }
1985 cm_show_direct(cm_id, -1, -1);
1986 }
1987
restore_starting_video_mode(void)1988 void restore_starting_video_mode(void)
1989 {
1990 int var_index =find_var("video_width", INI_FILE_VAR);
1991 if (var_index != -1)
1992 set_var_OPT_INT(our_vars.var[var_index]->name, (size_t)our_vars.var[var_index]->config_file_val);
1993 if ((var_index = find_var("video_height", INI_FILE_VAR)) != -1)
1994 set_var_OPT_INT(our_vars.var[var_index]->name, (size_t)our_vars.var[var_index]->config_file_val);
1995 if ((var_index = find_var("video_mode", INI_FILE_VAR)) != -1)
1996 set_var_OPT_MULTI(our_vars.var[var_index]->name, (size_t)our_vars.var[var_index]->config_file_val);
1997 if ((var_index = find_var("full_screen", INI_FILE_VAR)) != -1)
1998 set_var_OPT_BOOL(our_vars.var[var_index]->name, (int)our_vars.var[var_index]->config_file_val);
1999 }
2000
set_user_defined_video_mode(void)2001 void set_user_defined_video_mode(void)
2002 {
2003 int var_index = find_var("video_width", INI_FILE_VAR);
2004 if (var_index != -1)
2005 {
2006 set_var_OPT_INT(our_vars.var[var_index]->name, window_width);
2007 if ((var_index = find_var("video_height", INI_FILE_VAR)) != -1)
2008 {
2009 set_var_OPT_INT(our_vars.var[var_index]->name, window_height);
2010 if ((var_index = find_var("video_mode", INI_FILE_VAR)) != -1)
2011 set_var_OPT_MULTI(our_vars.var[var_index]->name, 0);
2012 }
2013 }
2014 }
2015
2016 #endif
2017
change_language(const char * new_lang)2018 void change_language(const char *new_lang)
2019 {
2020 int var_index;
2021
2022 LOG_DEBUG("Language changed, was [%s] now [%s]\n", lang, new_lang);
2023 /* guard against being the same string */
2024 if (strcmp(lang, new_lang) != 0)
2025 safe_strncpy(lang, new_lang, sizeof(lang));
2026
2027 var_index = find_var("language", INI_FILE_VAR);
2028
2029 if (var_index != -1)
2030 {
2031 our_vars.var[var_index]->saved= 0;
2032 }
2033 else
2034 {
2035 LOG_ERROR("Can't find var '%s', type 'OPT_STRING'", "language");
2036 }
2037 }
2038
check_option_var(const char * name)2039 static __inline__ void check_option_var(const char* name)
2040 {
2041 int i;
2042 int value_i;
2043 float value_f;
2044 char* value_s;
2045
2046 i= find_var(name, IN_GAME_VAR);
2047 if (i < 0)
2048 {
2049 LOG_ERROR("Can't find var '%s', type 'IN_GAME_VAR'", name);
2050 return;
2051 }
2052
2053 switch (our_vars.var[i]->type)
2054 {
2055 case OPT_INT:
2056 case OPT_MULTI:
2057 case OPT_MULTI_H:
2058 case OPT_INT_F:
2059 case OPT_INT_INI:
2060 value_i= *((int*)our_vars.var[i]->var);
2061 our_vars.var[i]->func (our_vars.var[i]->var, value_i);
2062 break;
2063 case OPT_BOOL:
2064 case OPT_BOOL_INI:
2065 value_i= *((int*)our_vars.var[i]->var);
2066 if (value_i == 0) *((int*)our_vars.var[i]->var)= 1;
2067 else *((int*)our_vars.var[i]->var)= 0;
2068 our_vars.var[i]->func (our_vars.var[i]->var);
2069 break;
2070 case OPT_STRING:
2071 case OPT_STRING_INI:
2072 case OPT_PASSWORD:
2073 value_s= (char*)our_vars.var[i]->var;
2074 our_vars.var[i]->func (our_vars.var[i]->var, value_s, our_vars.var[i]->len);
2075 break;
2076 case OPT_FLOAT:
2077 case OPT_FLOAT_F:
2078 value_f= *((float*)our_vars.var[i]->var);
2079 our_vars.var[i]->func (our_vars.var[i]->var, value_f);
2080 break;
2081 }
2082 }
2083
check_options(void)2084 void check_options(void)
2085 {
2086 #ifndef OSX
2087 check_option_var("use_compiled_vertex_array");
2088 #endif
2089 check_option_var("use_vertex_buffers");
2090 check_option_var("clouds_shadows");
2091 check_option_var("small_actor_texture_cache");
2092 check_option_var("use_eye_candy");
2093 check_option_var("use_point_particles");
2094 check_option_var("use_frame_buffer");
2095 check_option_var("use_shadow_mapping");
2096 check_option_var("shadow_map_size");
2097 check_option_var("water_shader_quality");
2098 check_option_var("use_animation_program");
2099 }
2100
2101 /*!
2102 * Find a multiselect option in variable \a var by value.
2103 *
2104 * \return The index of the value, if found, otherwise -1.
2105 */
find_multi_option_by_value(const var_struct * var,const char * value)2106 static int find_multi_option_by_value(const var_struct *var, const char* value)
2107 {
2108 int i;
2109
2110 if (!value)
2111 return -1;
2112
2113 for (i = 0; i < var->args.multi.count; ++i)
2114 {
2115 const char *opt_value = var->args.multi.elems[i].value;
2116 if (opt_value && !strcmp(opt_value, value))
2117 return i;
2118 }
2119
2120 return -1;
2121 }
2122
check_deferred_options()2123 void check_deferred_options()
2124 {
2125 int i;
2126
2127 for (i = 0; i < defers_count; ++i)
2128 {
2129 deferred_option *option = &defers[i];
2130 var_struct *var = our_vars.var[option->var_idx];
2131 const char* opt_val = option->value;
2132 int opt_idx = option->opt_idx;
2133 int opt_idx_ok = (opt_idx < var->args.multi.count);
2134
2135 if (opt_val)
2136 {
2137 const char* value = opt_idx_ok ? var->args.multi.elems[opt_idx].value : NULL;
2138 if (!value || strcmp(value, opt_val))
2139 {
2140 int new_idx = find_multi_option_by_value(var, opt_val);
2141 if (new_idx >= 0)
2142 {
2143 opt_idx = new_idx;
2144 opt_idx_ok = 1;
2145 }
2146 else
2147 {
2148 opt_idx_ok = 0;
2149 }
2150 }
2151 }
2152
2153 if (opt_idx_ok)
2154 {
2155 var->func(var->var, opt_idx);
2156 var->config_file_val = (float)opt_idx;
2157 }
2158 else
2159 {
2160 if (opt_val)
2161 {
2162 LOG_ERROR("Failed to find value '%s' in multiselect option '%s'",
2163 opt_val, var->name);
2164 }
2165 else
2166 {
2167 LOG_ERROR("Failed to find index %d in multiselect option '%s'",
2168 opt_idx, var->name);
2169 }
2170 }
2171
2172 free(option->value);
2173 }
2174
2175 free(defers);
2176 defers = NULL;
2177 defers_size = 0;
2178 defers_count = 0;
2179
2180 // Stop further deferrals
2181 do_defer = 0;
2182 }
2183
2184 /*!
2185 * Defer setting multi-select variable.
2186 *
2187 * Some multi-select variables cannot be reliably set because they are not fully
2188 * initialized before the ini is read. This function stores the desired option
2189 * for setting later.
2190 * \param var_idx The index of the multi-select variable in \see our_vars.
2191 * \param opt_idx The index of the desired option in the options list of the variable
2192 * \param value The value associated with the desired option.
2193 * \return -1 if the option cannot be stored, 1 otherwise.
2194 * \sa check_deferred_options()
2195 */
add_deferred_option(int var_idx,int opt_idx,const char * value)2196 static int add_deferred_option(int var_idx, int opt_idx, const char* value)
2197 {
2198 deferred_option *option;
2199
2200 if (defers_count >= defers_size)
2201 {
2202 int new_size = defers_size + 8;
2203 deferred_option *new_defers = realloc(defers, new_size * sizeof(deferred_option));
2204 if (!new_defers)
2205 return -1;
2206
2207 defers = new_defers;
2208 defers_size = new_size;
2209 }
2210
2211 option = &defers[defers_count];
2212 option->var_idx = var_idx;
2213 option->opt_idx = opt_idx;
2214 option->value = value ? strdup(value) : NULL;
2215 ++defers_count;
2216
2217 return 1;
2218 }
2219
2220 /*!
2221 * Set a the value of a multi-select variable.
2222 *
2223 * Set the value of the multiselect valuable at index \a var_idx in \see our_vars,
2224 * to the option described by \a str (a line from the ini file). If the description
2225 * contains both an index and a value, the value is preferred over the index,
2226 * and the variable is set to the first option that has the same value. If the
2227 * value cannot be found, or no value is set and the index is out of range,
2228 * and \a do_defer is not zero, the desired option is stored for later
2229 * processing.
2230 *
2231 * \param str The description of the option to set
2232 * \param var_idx The index of the multi-select variable in which to select an option
2233 * \return -1 is setting the option fails, 1 on success
2234 *
2235 * \sa check_deferred_options().
2236 */
check_multi_select(const char * str,int var_idx)2237 static int check_multi_select(const char* str, int var_idx)
2238 {
2239 var_struct *var = our_vars.var[var_idx];
2240 int nr_conv, opt_idx, opt_idx_ok;
2241 char value_buf[256] = { 0 };
2242
2243 nr_conv = sscanf(str, "%d (%255[^\r\n)])", &opt_idx, value_buf);
2244 if (nr_conv == 0)
2245 {
2246 // Unable to parse the value at all
2247 return -1;
2248 }
2249
2250 if (opt_idx < 0)
2251 {
2252 LOG_ERROR("Invalid value %d for '%s'", opt_idx, var->name);
2253 return -1;
2254 }
2255 opt_idx_ok = opt_idx <= var->args.multi.count;
2256
2257 if (nr_conv == 2)
2258 {
2259 // Got a value. If it doesn't match the value at the index, find the option
2260 // with the correct value and update the index.
2261 const char* opt_value = (opt_idx < var->args.multi.count)
2262 ? var->args.multi.elems[opt_idx].value : NULL;
2263 opt_idx_ok = opt_value && !strcmp(opt_value, value_buf);
2264 if (!opt_idx_ok)
2265 {
2266 int new_idx = find_multi_option_by_value(var, value_buf);
2267 if (new_idx >= 0)
2268 {
2269 opt_idx = new_idx;
2270 opt_idx_ok = 1;
2271 }
2272 }
2273 }
2274
2275 if (opt_idx_ok)
2276 {
2277 var->func(var->var, opt_idx);
2278 var->config_file_val = (float)opt_idx;
2279 return 1;
2280 }
2281
2282 // The index stored does not fit in the current range of the multiselect,
2283 // or the value stored with the index does not match the value at this index.
2284 // If do_defer is true, store the index and value to check again at a later
2285 // point, otherwise give up.
2286 if (do_defer)
2287 {
2288 char* value = *value_buf ? value_buf : NULL;
2289 return add_deferred_option(var_idx, opt_idx, value);
2290 }
2291
2292 if (*value_buf)
2293 {
2294 LOG_ERROR("Failed to find value '%s' in multiselect option '%s'",
2295 value_buf, var->name);
2296 }
2297 else
2298 {
2299 LOG_ERROR("Failed to find index %d in multiselect option '%s'",
2300 opt_idx, var->name);
2301 }
2302 return -1;
2303 }
2304
check_var(char * str,var_name_type type)2305 int check_var(char *str, var_name_type type)
2306 {
2307 int i, *p;
2308 char *ptr= str;
2309 float foo;
2310
2311 i = find_var(str, type);
2312 if (i < 0)
2313 {
2314 LOG_WARNING("Can't find var '%s', type %d", str, type);
2315 return -1;
2316 }
2317 our_vars.var[i]->in_ini_file = 1;
2318
2319 ptr += (type != COMMAND_LINE_SHORT_VAR) ? our_vars.var[i]->nlen : our_vars.var[i]->snlen;
2320
2321 while (*ptr && (*ptr== ' ' || *ptr == '='))
2322 ptr++; // go to the string occurence
2323 if (!*ptr || *ptr == 0x0d || *ptr == 0x0a)
2324 return -1; // hmm, why would you do such a stupid thing?
2325
2326 if (*ptr == '"')
2327 {
2328 //Accurate quoting
2329 char *tptr= ++ptr;
2330 while (*tptr && *tptr != '"')
2331 {
2332 if (*tptr == 0x0a || *tptr == 0x0d)
2333 {
2334 #ifdef ELC
2335 char str[200];
2336 safe_snprintf (str, sizeof(str), "Reached newline without an ending \" in %s", our_vars.var[i]->name);
2337 LOG_TO_CONSOLE(c_red2,str);
2338 #endif // ELC
2339 break;
2340 }
2341 tptr++;
2342 }
2343 *tptr= 0;
2344 }
2345 else
2346 {
2347 size_t len;
2348
2349 // Strip spaces from front and back of the string, but not internal spaces
2350 while (*ptr == ' ')
2351 ++ptr;
2352 len = strcspn(ptr, "\r\n");
2353 while (len > 0 && ptr[len-1] == ' ')
2354 --len;
2355 ptr[len] = '\0';
2356 }
2357
2358 if (type == INI_FILE_VAR)
2359 our_vars.var[i]->saved= 1;
2360 else if (type == IN_GAME_VAR)
2361 // make sure in-game changes are stored in the ini file
2362 our_vars.var[i]->saved= 0;
2363 switch (our_vars.var[i]->type)
2364 {
2365 case OPT_INT_INI:
2366 // Needed, because var is never changed through widget
2367 our_vars.var[i]->saved= 0;
2368 // fallthrough
2369 case OPT_INT:
2370 case OPT_INT_F:
2371 {
2372 int new_val = atoi (ptr);
2373 our_vars.var[i]->func(our_vars.var[i]->var, new_val);
2374 our_vars.var[i]->config_file_val = (float)new_val;
2375 return 1;
2376 }
2377 case OPT_MULTI:
2378 case OPT_MULTI_H:
2379 return check_multi_select(ptr, i);
2380 case OPT_BOOL_INI:
2381 // Needed, because var is never changed through widget
2382 our_vars.var[i]->saved= 0;
2383 // fallthrough
2384 case OPT_BOOL:
2385 {
2386 int new_val;
2387 p= our_vars.var[i]->var;
2388 if (ptr && ptr[0]=='!')
2389 new_val = !*p;
2390 else
2391 new_val = atoi (ptr);
2392 if ((new_val>0) != *p)
2393 our_vars.var[i]->func (our_vars.var[i]->var); //only call if value has changed
2394 our_vars.var[i]->config_file_val = (float)new_val;
2395 return 1;
2396 }
2397 case OPT_STRING_INI:
2398 // Needed, because var is never changed through widget
2399 our_vars.var[i]->saved= 0;
2400 // fallthrough
2401 case OPT_STRING:
2402 case OPT_PASSWORD:
2403 our_vars.var[i]->func (our_vars.var[i]->var, ptr, our_vars.var[i]->len);
2404 return 1;
2405 case OPT_FLOAT:
2406 case OPT_FLOAT_F:
2407 foo= atof (ptr);
2408 our_vars.var[i]->func (our_vars.var[i]->var, &foo);
2409 our_vars.var[i]->config_file_val = foo;
2410 return 1;
2411 }
2412 return -1;
2413 }
2414
free_vars(void)2415 void free_vars(void)
2416 {
2417 int i;
2418 for(i= 0; i < our_vars.no; i++)
2419 {
2420 switch(our_vars.var[i]->type) {
2421 case OPT_MULTI:
2422 case OPT_MULTI_H:
2423 if (our_vars.var[i]->args.multi.count > 0)
2424 {
2425 free(our_vars.var[i]->args.multi.elems);
2426 our_vars.var[i]->args.multi.count = 0;
2427 }
2428 break;
2429 default:
2430 /* do nothing */ ;
2431 }
2432 free(our_vars.var[i]);
2433 }
2434 if (our_vars.var != NULL)
2435 {
2436 free(our_vars.var);
2437 our_vars.var = NULL;
2438 }
2439 our_vars.no=0;
2440 }
2441
add_multi_option_to_var(var_struct * var,const char * label,const char * value)2442 static void add_multi_option_to_var(var_struct *var, const char* label, const char* value)
2443 {
2444 // FIXME? reallocating on every addition
2445 multi_element *new_elems = realloc(var->args.multi.elems, sizeof(multi_element) * (var->args.multi.count + 1));
2446 if (!new_elems)
2447 {
2448 LOG_ERROR("Failed to reallocate elements for variable '%s'", var->name);
2449 return;
2450 }
2451
2452 new_elems[var->args.multi.count].label = label;
2453 new_elems[var->args.multi.count].value = value;
2454 var->args.multi.elems = new_elems;
2455 var->args.multi.count++;
2456 }
2457
add_var(option_type type,char * name,char * shortname,void * var,void * func,float def,char * short_desc,char * long_desc,int tab_id,...)2458 static void add_var(option_type type, char * name, char * shortname, void * var, void * func, float def, char * short_desc, char * long_desc, int tab_id, ...)
2459 {
2460 int *integer=var;
2461 float *f=var;
2462 int no=our_vars.no;
2463 const char *pointer;
2464 va_list ap;
2465
2466 our_vars.var = realloc(our_vars.var, ++our_vars.no * sizeof(var_struct *));
2467
2468 our_vars.var[no]=(var_struct*)calloc(1,sizeof(var_struct));
2469 switch(our_vars.var[no]->type=type)
2470 {
2471 case OPT_MULTI:
2472 case OPT_MULTI_H:
2473 our_vars.var[no]->args.multi.elems = NULL;
2474 our_vars.var[no]->args.multi.count = 0;
2475 va_start(ap, tab_id);
2476 while ((pointer = va_arg(ap, const char *)) != NULL)
2477 {
2478 add_multi_option_to_var(our_vars.var[no], pointer, NULL);
2479 }
2480 va_end(ap);
2481 *integer= (int)def;
2482 break;
2483 case OPT_INT:
2484 case OPT_INT_INI:
2485 va_start(ap, tab_id);
2486 our_vars.var[no]->args.imm.min = va_arg(ap, uintptr_t);
2487 our_vars.var[no]->args.imm.max = va_arg(ap, uintptr_t);
2488 va_end(ap);
2489 *integer= (int)def;
2490 break;
2491 case OPT_BOOL:
2492 case OPT_BOOL_INI:
2493 *integer=(int)def;
2494 break;
2495 case OPT_STRING:
2496 case OPT_STRING_INI:
2497 case OPT_PASSWORD:
2498 our_vars.var[no]->len=(int)def;
2499 break;
2500 case OPT_FLOAT:
2501 va_start(ap, tab_id);
2502 our_vars.var[no]->args.fmmi.min = va_arg(ap, double);
2503 our_vars.var[no]->args.fmmi.max = va_arg(ap, double);
2504 our_vars.var[no]->args.fmmi.interval = va_arg(ap, double);
2505 va_end(ap);
2506 *f=def;
2507 break;
2508 case OPT_FLOAT_F:
2509 va_start(ap, tab_id);
2510 our_vars.var[no]->args.fmmif.min = va_arg(ap, float (*)());
2511 our_vars.var[no]->args.fmmif.max = va_arg(ap, float (*)());
2512 our_vars.var[no]->args.fmmif.interval = va_arg(ap, double);
2513 va_end(ap);
2514 *f = def;
2515 break;
2516 case OPT_INT_F:
2517 va_start(ap, tab_id);
2518 our_vars.var[no]->args.immf.min = va_arg(ap, int (*)());
2519 our_vars.var[no]->args.immf.max = va_arg(ap, int (*)());
2520 va_end(ap);
2521 *integer = (int)def;
2522 break;
2523 }
2524 our_vars.var[no]->var=var;
2525 our_vars.var[no]->config_file_val = our_vars.var[no]->default_val = def;
2526 our_vars.var[no]->func=func;
2527 our_vars.var[no]->name=name;
2528 our_vars.var[no]->shortname=shortname;
2529 our_vars.var[no]->nlen=strlen(our_vars.var[no]->name);
2530 our_vars.var[no]->snlen=strlen(our_vars.var[no]->shortname);
2531 our_vars.var[no]->saved= 0;
2532 our_vars.var[no]->in_ini_file = 0;
2533 #ifdef JSON_FILES
2534 our_vars.var[no]->character_override = 0;
2535 #endif
2536 #ifdef ELC
2537 add_options_distringid(name, &our_vars.var[no]->display, short_desc, long_desc);
2538 #endif //ELC
2539 our_vars.var[no]->widgets.tab_id= tab_id;
2540 }
2541
2542 #ifndef MAP_EDITOR
2543 // set default fonts names and sizes
command_set_default_fonts(char * text,int len)2544 int command_set_default_fonts(char *text, int len)
2545 {
2546 char const * font_vars[] = { "ui_font", "name_font", "chat_font", "note_font",
2547 "book_font", "rules_font", "encyclopedia_font" };
2548 char const * size_vars[] = { "ui_text_size", "name_text_size", "chat_text_size",
2549 "note_text_size", "book_text_size", "rules_text_size", "encyclopedia_text_size" };
2550 char const * font_names[] = { def_ui_font_str, def_name_font_str, def_chat_font_str,
2551 def_note_font_str, def_book_font_str, def_rules_font_str, def_encyclopedia_font_str };
2552 int elconfig_win = get_id_MW(MW_CONFIG);
2553 int var_idx;
2554 size_t i;
2555
2556 for (i = 0; i < sizeof(font_vars)/sizeof(char *); i++)
2557 {
2558 if (strlen(font_names[i]) == 0)
2559 continue;
2560 var_idx = find_var(font_vars[i], INI_FILE_VAR);
2561 if (var_idx >= 0)
2562 {
2563 var_struct *var = our_vars.var[var_idx];
2564 if (check_multi_select(font_names[i], var_idx) != 1)
2565 continue;
2566 var->saved = 0;
2567 if (elconfig_win >= 0)
2568 {
2569 int window_id = elconfig_tabs[var->widgets.tab_id].tab;
2570 int widget_id = var->widgets.widget_id;
2571 if ((window_id >= 0) && (widget_id >= 0))
2572 multiselect_set_selected(window_id, widget_id, *((const int*)var->var));
2573 }
2574 }
2575 var_idx = find_var(size_vars[i], INI_FILE_VAR);
2576 if (var_idx >= 0)
2577 {
2578 float new_val = 1.0f;
2579 var_struct *var = our_vars.var[var_idx];
2580 var->func(var->var, &new_val);
2581 var->saved = 0;
2582 }
2583 }
2584 var_idx = find_var("mapmark_text_size", INI_FILE_VAR);
2585 if (var_idx >= 0)
2586 {
2587 float new_val = 1.0f;
2588 var_struct *var = our_vars.var[var_idx];
2589 var->func(var->var, &new_val);
2590 var->saved = 0;
2591 }
2592 return 1;
2593 }
2594
add_multi_option_with_id(const char * name,const char * str,const char * id,int add_button)2595 void add_multi_option_with_id(const char* name, const char* str, const char* id,
2596 int add_button)
2597 {
2598 int var_index = find_var(name, INI_FILE_VAR);
2599 if (var_index == -1)
2600 {
2601 LOG_ERROR("Can't find var '%s', type 'INI_FILE_VAR'", name);
2602 return;
2603 }
2604
2605 add_multi_option_to_var(our_vars.var[var_index], str, id);
2606
2607 if (add_button)
2608 {
2609 int window_id = elconfig_tabs[our_vars.var[var_index]->widgets.tab_id].tab;
2610 int widget_id = our_vars.var[var_index]->widgets.widget_id;
2611 if (window_id > 0)
2612 {
2613 int n = our_vars.var[var_index]->args.multi.count - 1;
2614 multiselect_button_add_extended(window_id, widget_id,
2615 0, n * MULTI_LINE_HEIGHT, 0, str,
2616 elconf_scale * DEFAULT_SMALL_RATIO, 0);
2617 }
2618 }
2619 }
2620
clear_multiselect_var(const char * name)2621 void clear_multiselect_var(const char* name)
2622 {
2623 int var_index = find_var(name, INI_FILE_VAR);
2624 int window_id, widget_id;
2625
2626 if (var_index == -1)
2627 {
2628 LOG_ERROR("Can't find var '%s', type 'INI_FILE_VAR'", name);
2629 return;
2630 }
2631
2632 if (our_vars.var[var_index]->args.multi.count == 0)
2633 // Not yet initialized. Or already empty, at least.
2634 return;
2635
2636 our_vars.var[var_index]->args.multi.count = 0;
2637
2638 window_id = elconfig_tabs[our_vars.var[var_index]->widgets.tab_id].tab;
2639 widget_id = our_vars.var[var_index]->widgets.widget_id;
2640 multiselect_clear(window_id, widget_id);
2641 }
2642
set_multiselect_var(const char * name,int idx,int change_button)2643 void set_multiselect_var(const char* name, int idx, int change_button)
2644 {
2645 int var_index = find_var(name, INI_FILE_VAR);
2646 if (var_index == -1)
2647 {
2648 LOG_ERROR("Can't find var '%s', type 'INI_FILE_VAR'", name);
2649 return;
2650 }
2651
2652 if (our_vars.var[var_index]->args.multi.count <= idx)
2653 return;
2654
2655 our_vars.var[var_index]->func(our_vars.var[var_index]->var, idx);
2656 our_vars.var[var_index]->saved = 0;
2657
2658 if (change_button)
2659 {
2660 int window_id = elconfig_tabs[our_vars.var[var_index]->widgets.tab_id].tab;
2661 int widget_id = our_vars.var[var_index]->widgets.widget_id;
2662 if (window_id > 0)
2663 multiselect_set_selected(window_id, widget_id, idx);
2664 }
2665 }
2666 #endif // !MAP_EDITOR
2667
2668 //ELC specific variables
2669 #ifdef ELC
init_ELC_vars(void)2670 static void init_ELC_vars(void)
2671 {
2672 int i;
2673 char * win_scale_description = "Multiplied by the user interface scaling factor. With the mouse over the window: change ctrl+mousewheel up/down or ctrl+cursor up/down, set default ctrl+HOME, set initial ctrl+END.";
2674
2675 // CONTROLS TAB
2676 add_var(OPT_BOOL,"sit_lock","sl",&sit_lock,change_var,0,"Sit Lock","Enable this to prevent your character from moving by accident when you are sitting.",CONTROLS);
2677 add_var(OPT_BOOL,"always_pathfinding", "alwayspathfinding", &always_pathfinding, change_var, 0, "Extend the range of the walk cursor", "Extends the range of the walk cursor to as far as you can see. Using this option, movement may be slightly less responsive on larger maps.", CONTROLS);
2678 add_var(OPT_BOOL,"target_close_clicked_creature", "targetcloseclickedcreature", &target_close_clicked_creature, change_var, 1, "Target creature if you click close to it", "When enabled, if you click close to a creature that is in range, you will attack it or select it as the target for an active spell.", CONTROLS);
2679 add_var(OPT_BOOL,"open_close_clicked_bag", "openupcloseclickedbag", &open_close_clicked_bag, change_var, 1, "Open a bag if you click close to it", "When enabled, if you click close to a bag that is in range, you will open it.", CONTROLS);
2680 add_var(OPT_BOOL,"use_floating_messages", "floating", &floatingmessages_enabled, change_var, 1, "Floating Messages", "Toggles the use of floating experience messages and other graphical enhancements", CONTROLS);
2681 add_var(OPT_BOOL,"floating_session_counters", "floatingsessioncounters", &floating_session_counters, change_var, 0, "Floating Session Counters", "Toggles the display of floating session counters. Configure each type using the context menu of the counter category.", CONTROLS);
2682 add_var(OPT_BOOL,"enable_used_item_counter", "enable_used_item_counter", &enable_used_item_counter, change_var, 0, "Enable Used Item Counter", "WARNING: If enabled, saved counters will not be compatible with previous versions of the client. Previous versions of the client may crash when loading counters. If disabled, Used Item counts will not be saved.", CONTROLS);
2683 add_var(OPT_BOOL,"use_keypress_dialog_boxes", "keypressdialogues", &use_keypress_dialogue_boxes, change_var, 0, "Keypresses in dialogue boxes", "Toggles the ability to press a key to select a menu option in dialogue boxes (eg The Wraith)", CONTROLS);
2684 add_var(OPT_BOOL,"use_full_dialogue_window", "keypressdialoguesfullwindow", &use_full_dialogue_window, change_var, 0, "Keypresses allowed anywhere in dialogue boxes", "If set, the above will work anywhere in the Dialogue Window, if unset only on the NPC's face", CONTROLS);
2685 add_var(OPT_BOOL,"use_cursor_on_animal", "useanimal", &include_use_cursor_on_animals, change_var, 0, "For animals, right click includes use cursor", "Toggles inclusion of the use cursor when right clicking on animals, useful for your summoned creatures. Even when this option is off, you can still click the use icon.", CONTROLS);
2686 add_var(OPT_BOOL,"disable_double_click", "disabledoubleclick", &disable_double_click, change_var, 0, "Disable double-click button safety", "Some buttons are protected from mis-click by requiring you to double-click them. This option disables that protection.", CONTROLS);
2687 add_var(OPT_BOOL,"auto_disable_ranging_lock", "adrl", &auto_disable_ranging_lock, change_var, 1, "Auto-disable Ranging Lock when under attack", "Automatically disable Ranging Lock when the char is under attack and Ranging Lock is enabled", CONTROLS);
2688 add_var(OPT_BOOL,"achievements_ctrl_click", "achievementsctrlclick", &achievements_ctrl_click, change_var, 0, "Control click required to view achievements", "To view a players achievements, you click on them with the eye cursor. With this option enabled, you must use Ctrl+click.", CONTROLS);
2689 add_var(OPT_BOOL,"independant_quickbar_action_modes", "iqbam", &independant_quickbar_action_modes, change_var, 0, "Use independant action modes for quick-bar window", "When enabled, actions set from the icon window or action keys will not change the mode for the quick-bar window.", CONTROLS);
2690 add_var(OPT_BOOL,"independant_inventory_action_modes", "iiwam", &independant_inventory_action_modes, change_var, 0, "Use independant action modes for inventory window", "When enabled, actions set from the icon window or action keys will not change the mode for the invenory window.", CONTROLS);
2691 add_var(OPT_INT,"mouse_limit","lmouse",&mouse_limit,change_int,15,"Mouse Limit","You can increase the mouse sensitivity and cursor changing by adjusting this number to lower numbers, but usually the FPS will drop as well!",CONTROLS,1,INT_MAX);
2692 #ifdef OSX
2693 add_var(OPT_BOOL,"osx_right_mouse_cam","osxrightmousecam", &osx_right_mouse_cam, change_var,0,"Rotate Camera with right mouse button", "Allows to rotate the camera by pressing the right mouse button and dragging the cursor", CONTROLS);
2694 add_var(OPT_BOOL,"emulate_3_button_mouse","emulate3buttonmouse", &emulate3buttonmouse, change_var,0,"Emulate a 3 Button Mouse", "If you have a 1 Button Mouse you can use <apple> click to emulate a rightclick. Needs client restart.", CONTROLS);
2695 #endif // OSX
2696 add_var(OPT_MULTI,"trade_log_mode","tradelogmode",&trade_log_mode,change_int, TRADE_LOG_NONE,"Trade log","Set how successful trades are logged.",CONTROLS,"Do not log trades", "Log only to console", "Log only to file", "Log to console and file", NULL);
2697 // CONTROLS TAB
2698
2699
2700 // HUD TAB
2701 add_var(OPT_BOOL,"show_fps","fps",&show_fps,change_var,1,"Show FPS","Show the current frames per second in the corner of the window",HUD);
2702 add_var(OPT_BOOL,"view_analog_clock","analog",&view_analog_clock,change_var,1,"Analog Clock","Toggle the analog clock",HUD);
2703 add_var(OPT_BOOL,"view_digital_clock","digit",&view_digital_clock,change_var,1,"Digital Clock","Toggle the digital clock",HUD);
2704 add_var(OPT_BOOL,"view_knowledge_bar","knowledge_bar",&view_knowledge_bar,change_var,1,"Knowledge Bar","Toggle the knowledge bar",HUD);
2705 add_var(OPT_BOOL,"view_hud_timer","timer",&view_hud_timer,change_var,1,"Countdown/Stopwatch Timer","Timer controls: Right-click for menu. Shift-left-click to toggle mode. Left-click to start/stop. Mouse wheel to reset, up/down to change countdown start time (+ctrl/alt to change step).",HUD);
2706 add_var(OPT_BOOL,"show_game_seconds","show_game_seconds",&show_game_seconds,change_var,0,"Show Game Seconds","Show seconds on the digital clock. Note: the seconds displayed are computed on client side and synchronized with the server at each new minute.",HUD);
2707 add_var(OPT_BOOL,"show_stats_in_hud","sstats",&show_stats_in_hud,change_var,0,"Stats In HUD","Toggle showing stats in the HUD",HUD);
2708 add_var(OPT_BOOL,"show_statbars_in_hud","sstatbars",&show_statbars_in_hud,change_var,0,"StatBars In HUD","Toggle showing statbars in the HUD. Needs Stats in HUD",HUD);
2709 add_var(OPT_BOOL,"show_action_bar","ssactionbar",&show_action_bar,change_show_action_bar,0,"Action Points Bar in HUD","Show the current action points level in a stats bar on the bottom HUD.",HUD);
2710 add_var(OPT_BOOL,"show_last_health_change_always","slhca",&show_last_health_change_always,change_var,0,"Always Show The Last Health Change", "Enable to always show the last health change. Otherwise, it is only shown when the mouse is over the health bar.",HUD);
2711 add_var(OPT_BOOL,"show_indicators","showindicators",&show_hud_indicators,toggle_hud_indicators_window,1,"Show Status Indicators in HUD","Show status indicators for special day (left click to show day), harvesting, poision and message count (left click to zero). Right-click the window for settings.",HUD);
2712 add_var(OPT_BOOL,"logo_click_to_url","logoclick",&logo_click_to_url,change_var,0,"Logo Click To URL","Toggle clicking the LOGO opening a browser window",HUD);
2713 add_var(OPT_STRING,"logo_link", "logolink", LOGO_URL_LINK, change_string, sizeof(LOGO_URL_LINK),
2714 "Logo Link", "URL when clicking the logo", HUD);
2715 add_var(OPT_BOOL,"show_help_text","shelp",&show_help_text,change_var,1,"Help Text","Enable tooltips.",HUD);
2716 add_var(OPT_BOOL,"always_enlarge_text","aetext",&always_enlarge_text,change_var,1,"Always Enlarge Text","Some text can be enlarged by pressing ALT or CTRL, often only while the mouse is over it. Setting this option effectively locks the ALT/CTRL state to on.",HUD);
2717 add_var(OPT_BOOL,"show_item_desc_text","showitemdesctext",&show_item_desc_text,change_var,1,"Item Description Text","Enable item description tooltips. Needs item_info.txt file.",HUD);
2718 add_var(OPT_BOOL,"use_alpha_border", "aborder", &use_alpha_border, change_var, 1,"Alpha Border","Toggle the use of alpha borders",HUD); //ADVVID);
2719 add_var(OPT_BOOL,"use_alpha_banner", "abanner", &use_alpha_banner, change_var, 0,"Alpha Behind Name/Health Text","Toggle the use of an alpha background to name/health banners",HUD);
2720 add_var(OPT_BOOL,"cm_banner_disabled", "cmbanner", &cm_banner_disabled, change_var, 0,"Disable Name/Health Text Context Menu","Disable the context menu on your players name/health banner.",HUD);
2721 add_var(OPT_BOOL, "windows_on_top", "wot", &windows_on_top, change_windows_on_top, 0, "Windows On Top","Allows the Manufacture, Storage and Inventory windows to appear above the map and console.", HUD);
2722 add_var(OPT_BOOL,"opaque_window_backgrounds", "opaquewin", &opaque_window_backgrounds, change_var, 0,"Use Opaque Window Backgrounds","Toggle the current state of all windows between transparent and opaque background. Use CTRL+D to toggle the current state of an individual window.",HUD);
2723 add_var(OPT_SPECINT, "buff_icon_size","bufficonsize", &buff_icon_size, set_buff_icon_size, 32, "Buff Icon Size","The size of the icons of the active buffs. Icons are not displayed when size set to zero.",HUD,0,48);
2724 add_var(OPT_BOOL,"relocate_quickbar", "requick", &quickbar_relocatable, change_quickbar_relocatable, 0,"Relocate Quickbar","Set whether you can move the quickbar",HUD);
2725 add_var(OPT_BOOL,"relocate_quickspells", "requickspells", &quickspells_relocatable, change_quickspells_relocatable, 0,"Relocate Quick Spells","Set whether you can move the quick spells window",HUD);
2726 add_var(OPT_INT,"num_quickbar_slots","numqbslots",&num_quickbar_slots,change_int,6,"Number Of Quickbar Item Slots","Set the number of quick slots for inventory items. May be automatically reduced for low resolutions",HUD,1,MAX_QUICKBAR_SLOTS);
2727 add_var(OPT_INT,"num_quickspell_slots","numqsslots",&num_quickspell_slots,change_int,6,"Number Of Quickbar Spell Slots","Set the number of quickbar slots for spells. May be automatically reduced for low resolutions",HUD,1,MAX_QUICKSPELL_SLOTS);
2728 add_var(OPT_INT,"max_food_level","maxfoodlevel",&max_food_level,change_int,45,"Maximum Food Level", "Set the maximum value displayed by the food level bar.",HUD,10,200);
2729 add_var(OPT_INT,"wanted_num_recipe_entries","wantednumrecipeentries",&wanted_num_recipe_entries,change_num_recipe_entries,10,"Number of recipe entries", "Sets the number of entries available for the manufacturing window stored recipes.",HUD,4,max_num_recipe_entries);
2730 add_var(OPT_INT,"exp_log_threshold","explogthreshold",&exp_log_threshold,change_int,5000,"Log exp gain to console", "If you gain experience of this value or over, then a console message will be written. Set the value to zero to disable completely.",HUD,0,INT_MAX);
2731 add_var(OPT_STRING, "npc_mark_template", "npcmarktemplate", npc_mark_str, change_string,
2732 sizeof(npc_mark_str), "NPC map mark template",
2733 "The template used when setting a map mark from the NPC dialogue (right click name). The %s is substituted for the NPC name.",
2734 HUD);
2735 add_var(OPT_BOOL,"3d_map_markers","3dmarks",&marks_3d,change_3d_marks,1,"Enable 3D Map Markers","Shows user map markers in the game window",HUD);
2736 add_var(OPT_BOOL,"item_window_on_drop","itemdrop",&item_window_on_drop,change_var,1,"Item Window On Drop","Toggle whether the item window shows when you drop items",HUD);
2737 add_var(OPT_FLOAT,"minimap_scale", "minimapscale", &local_minimap_size_coefficient, change_minimap_scale, 0.7, "Minimap Scale", "Adjust the overall size of the minimap", HUD, 0.5, 1.5, 0.1);
2738 add_var(OPT_BOOL,"rotate_minimap","rotateminimap",&rotate_minimap,change_var,1,"Rotate Minimap","Toggle whether the minimap should rotate.",HUD);
2739 add_var(OPT_BOOL,"pin_minimap","pinminimap",&pin_minimap,change_var,0,"Pin Minimap","Toggle whether the minimap ignores close-all-windows.",HUD);
2740 add_var(OPT_BOOL, "continent_map_boundaries", "cmb", &show_continent_map_boundaries, change_var, 1, "Map Boundaries On Continent Map", "Show map boundaries on the continent map", HUD);
2741 add_var(OPT_BOOL,"enable_user_menus", "user_menus", &enable_user_menus, toggle_user_menus, 0, "Enable User Menus","Create .menu files in your config directory. First line is the menu name. After that, each line is a command using the format \"Menus Text || command || command\". Prompt for input using \"command text <prompt text>\".",HUD);
2742 add_var(OPT_BOOL,"console_scrollbar_enabled", "console_scrollbar", &console_scrollbar_enabled, toggle_console_scrollbar, 1, "Show Console Scrollbar","If enabled, a scrollbar will be shown in the console window.",HUD);
2743 #if !defined(WINDOWS) && !defined(OSX)
2744 add_var(OPT_BOOL,"use_clipboard","uclb",&use_clipboard, change_var, 1, "Use Clipboard For Pasting", "Use CLIPBOARD for pasting (as e.g. GNOME does) or use PRIMARY cutbuffer (as xterm does)",HUD);
2745 #endif
2746
2747 add_var(OPT_BOOL,"show_poison_count", "poison_count", &show_poison_count, change_var, 0, "Show Food Poison Count", "Displays on the poison drop icon, the number of times you have been food poisoned since last being free of poison.",HUD);
2748
2749 add_var(OPT_BOOL,"your_dynamic_banner_colour", "ydbc", &dynamic_banner_colour.yourself, change_var, 1, "Dynamic Health and Mana Banner Colours", "Dynamically change the colour of your health and mana banner. For example, the health banner changes from green to red as you loose health.",HUD);
2750 add_var(OPT_BOOL,"player_dynamic_banner_colour", "pdbc", &dynamic_banner_colour.other_players, change_var, 1, "Dynamic Other Players Health Banner Colour", "Dynamically change the colour of the health banner for other players. It changes from green to red as they loose health.",HUD);
2751 add_var(OPT_BOOL,"creature_dynamic_banner_colour", "cdbc", &dynamic_banner_colour.creatures, change_var, 1, "Dynamic Creatures Health Banner Colour", "Dynamically change the colour of the health banner for creatures. It changes from green to red as they loose health.",HUD);
2752
2753 // instance mode options
2754 add_var(OPT_BOOL,"use_view_mode_instance","instance_mode",&view_mode_instance, change_var, 0, "Use instance mode banners", "Shows only your and mobs banners, adds mana bar to your banner.",HUD);
2755 add_var(OPT_FLOAT,"instance_mode_banner_height","instance_mode_bheight",&view_mode_instance_banner_height,change_float,5.0f,"Your instance banner height","Sets how high the banner is located above your character",HUD,1.0,12.0,0.2);
2756 add_var(OPT_FLOAT,"instance_mode_damage_height","instance_mode_dheight",&view_mode_instance_damage_height,change_float,5.0f,"Your instance heal/damage height","Sets how high the heal/damage are located above your character",HUD,-12.0,12.0,0.2);
2757 add_var(OPT_BOOL,"im_creature_view_names","im_cnm",&im_creature_view_names, change_var, 0, "Creatures instance banners - names", "Show creature names when using instance mode",HUD);
2758 add_var(OPT_BOOL,"im_creature_view_hp","im_chp",&im_creature_view_hp, change_var, 0, "Creatures instance banners - health numbers", "Show creature health numbers when using instance mode",HUD);
2759 add_var(OPT_BOOL,"im_creature_view_hp_bar","im_chpbar",&im_creature_view_hp_bar, change_var, 0, "Creatures instance banners - health bars", "Show creature health bars when using instance mode",HUD);
2760 add_var(OPT_BOOL,"im_creature_banner_bg","im_cbbg",&im_creature_banner_bg, change_var, 0, "Creatures instance banners - background", "Show creatures banners background when using instance mode",HUD);
2761 add_var(OPT_BOOL,"im_other_player_view_names","im_opnm",&im_other_player_view_names, change_var, 0, "Other players instance banners - names", "Show other players names when using instance mode",HUD);
2762 add_var(OPT_BOOL,"im_other_player_view_hp","im_ophp",&im_other_player_view_hp, change_var, 0, "Other players instance banners - health numbers", "Show other players health numbers when using instance mode",HUD);
2763 add_var(OPT_BOOL,"im_other_player_view_hp_bar","im_ophpbar",&im_other_player_view_hp_bar, change_var, 0, "Other players instance banners - health bars", "Show other players health bars when using instance mode",HUD);
2764 add_var(OPT_BOOL,"im_other_player_banner_bg","im_opbbg",&im_other_player_banner_bg, change_var, 0, "Other players instance banners - background", "Show other players banners background when using instance mode",HUD);
2765 add_var(OPT_BOOL,"im_other_player_show_banner_on_damage","im_opbdmg",&im_other_player_show_banner_on_damage, change_var, 0, "Other players instance banners - show hp on damage", "Show other players banners for a while if player gets hit when using instance mode",HUD);
2766 // HUD TAB
2767
2768
2769 // CHAT TAB
2770 add_var(OPT_MULTI,"windowed_chat", "winchat", &use_windowed_chat, change_windowed_chat, 1, "Chat Display Style", "How do you want your chat to be displayed?", CHAT, "Old behavior", "Tabbed chat", "Chat window", NULL);
2771 add_var(OPT_BOOL, "enable_chat_show_hide", "ecsh", &enable_chat_show_hide, change_enable_chat_show_hide, 0, "Enable Show/Hide For Chat", "If enabled, you can show or hide chat either using the #K_CHAT key (usually ALT+c) or using the optional icon-bar icon.", CHAT);
2772 add_var(OPT_BOOL, "console_input_at_top", "ciat", &console_input_at_top, change_console_input_at_top, 0, "Console Input At Top Of Window", "If set, console input will be located at the top of the window.", CHAT);
2773 add_var(OPT_INT,"max_chat_lines","mcl",&max_chat_lines.value,change_max_chat_lines,10,"Maximum Number Of Chat Lines","For Tabbed and Old behaviour chat modes, this value sets the maximium number of lines of chat displayed.",CHAT, max_chat_lines.lower, max_chat_lines.upper);
2774 add_var(OPT_BOOL,"local_chat_separate", "locsep", &local_chat_separate, change_separate_flag, 0, "Separate Local Chat", "Should local chat be separate?", CHAT);
2775 // The forces that be want PMs always global, so that they're less likely to be ignored
2776 //add_var (OPT_BOOL, "personal_chat_separate", "pmsep", &personal_chat_separate, change_separate_flag, 0, "Separate Personal Chat", "Should personal chat be separate?", CHAT);
2777 add_var(OPT_BOOL,"guild_chat_separate", "gmsep", &guild_chat_separate, change_separate_flag, 1, "Separate Guild Chat", "Should guild chat be separate?", CHAT);
2778 add_var(OPT_BOOL,"server_chat_separate", "scsep", &server_chat_separate, change_separate_flag, 0, "Separate Server Messages", "Should the messages from the server be separate?", CHAT);
2779 add_var(OPT_BOOL,"mod_chat_separate", "modsep", &mod_chat_separate, change_separate_flag, 0, "Separate Moderator Chat", "Should moderator chat be separated from the rest?", CHAT);
2780 // No longer supported, the code is just missing!
2781 //add_var(OPT_BOOL,"highlight_tab_on_nick", "highlight", &highlight_tab_on_nick, change_var, 1, "Highlight Tabs On Name", "Should tabs be highlighted when someone mentions your name?", CHAT);
2782 add_var(OPT_BOOL,"emote_filter", "emote_filter", &emote_filter, change_var, 1, "Emotes filter", "Do not display lines of text in local chat containing emotes only", CHAT);
2783 add_var(OPT_BOOL,"summoning_filter", "summ_filter", &summoning_filter, change_var, 0, "Summoning filter", "Do not display lines of text in local chat containing summoning messages", CHAT);
2784 add_var(OPT_BOOL,"mixed_message_filter", "mixedmessagefilter", &mixed_message_filter, change_var, 0, "Mixed item filter", "Do not display console messages for mixed items when other windows are closed", CHAT);
2785 add_var(OPT_INT,"time_warning_hour","warn_h",&time_warn_h,change_int,-1,"Time warning for new hour","If set to -1, there will be no warning given. Otherwise, you will get a notification in console this many minutes before the new hour",CHAT, -1, 30);
2786 add_var(OPT_INT,"time_warning_sun","warn_s",&time_warn_s,change_int,-1,"Time warning for dawn/dusk","If set to -1, there will be no warning given. Otherwise, you will get a notification in console this many minutes before sunrise/sunset",CHAT, -1, 30);
2787 add_var(OPT_INT,"time_warning_day","warn_d",&time_warn_d,change_int,-1,"Time warning for new #day","If set to -1, there will be no warning given. Otherwise, you will get a notification in console this many minutes before the new day",CHAT, -1, 30);
2788 add_var(OPT_SPECINT,"auto_afk_time","afkt",&afk_time_conf,set_afk_time,5,"AFK Time","The idle time in minutes before the AFK auto message",CHAT,0,INT_MAX);
2789 add_var(OPT_STRING, "afk_message", "afkm", afk_message, change_string, sizeof(afk_message),
2790 "AFK Message", "Set the AFK message", CHAT);
2791 add_var(OPT_BOOL, "afk_local", "afkl", &afk_local, change_var, 0, "Save Local Chat Messages When AFK", "When you go AFK, local chat messages are counted and saved as well as PMs", CHAT);
2792 #ifdef NEW_SOUND
2793 add_var(OPT_BOOL, "afk_snd_warning", "afks", &afk_snd_warning, change_var, 0, "Play AFK Message Sound", "When you go AFK, a sound is played when you receive a message or trade request", CHAT);
2794 #endif //NEW_SOUND
2795 add_var(OPT_BOOL,"use_global_ignores","gign",&use_global_ignores,change_var,1,"Global Ignores","Global ignores is a list with people that are well known for being nasty, so we put them into a list (global_ignores.txt). Enable this to load that list on startup.",CHAT);
2796 add_var(OPT_BOOL,"save_ignores","sign",&save_ignores,change_var,1,"Save Ignores","Toggle saving of the local ignores list on exit.",CHAT);
2797 add_var(OPT_BOOL, "use_global_filters", "gfil", &use_global_filters, change_global_filters, 1, "Global Filter", "Toggle the use of global text filters.", CHAT);
2798 add_var(OPT_BOOL,"caps_filter","caps",&caps_filter,change_var,1,"Caps Filter","Toggle the caps filter",CHAT);
2799 add_var(OPT_BOOL,"show_timestamp","timestamp",&show_timestamp,change_var,0,"Show Time Stamps","Toggle time stamps for chat messages",CHAT);
2800 add_var(OPT_MULTI_H,"dark_channeltext","dark_channeltext",&dark_channeltext,change_dark_channeltext,0,"Channel Text Color","Display the channel text in a darker color for better reading on bright maps ('Dark' may be unreadable in F1 screen)",CHAT, "Normal", "Medium", "Dark", NULL);
2801 // CHAT TAB
2802
2803
2804 // FONT TAB
2805 add_var(OPT_BOOL,"disable_auto_highdpi_scale", "disautohighdpi", &disable_auto_highdpi_scale, change_disable_auto_highdpi_scale, 0, "Disable High-DPI auto scaling", "For systems with high-dpi support (e.g. OS X): When enabled, name, chat and notepad font values, and the user interface scaling factor are all automatically scaled using the system's scale factor.", FONT);
2806 #ifdef TTF
2807 add_var(OPT_BOOL, "use_ttf", "ttf", &use_ttf, change_use_ttf, 1, "Use TTF",
2808 "Toggle the use of True Type fonts for text rendering", FONT);
2809 add_var(OPT_STRING, "ttf_directory", "ttfdir", ttf_directory, change_string, sizeof(ttf_directory),
2810 "TTF directory", "Scan this directory and its direct subdirectories for True Type fonts. This is only used when 'Use TTF' is enabled. Changes to this option only take effect after a restart of the client.", FONT);
2811 #endif
2812 add_var(OPT_FLOAT,"ui_text_size","uisize",&font_scales[UI_FONT],change_text_zoom,1,"UI Text Size","Set the size of the text in the user interface",FONT,0.8,1.2,0.01);
2813 add_var(OPT_MULTI,"ui_font","uifont",&font_idxs[UI_FONT],change_font,0,"UI Font","Change the type of font used in the user interface",FONT, NULL);
2814 add_var(OPT_FLOAT,"name_text_size","nsize",&name_font_local_scale,change_name_zoom,1,"Name Text Size","Set the size of the players name text",FONT,0.1,3.0,0.01);
2815 add_var(OPT_MULTI,"name_font","nfont",&font_idxs[NAME_FONT],change_font,0,"Name Font","Change the type of font used for the name",FONT, NULL);
2816 add_var(OPT_FLOAT,"chat_text_size","csize",&chat_font_local_scale,change_chat_zoom,1,"Chat Text Size","Sets the size of the normal text",FONT,0.1,3.0,0.01);
2817 add_var(OPT_MULTI,"chat_font","cfont",&font_idxs[CHAT_FONT],change_font,0,"Chat Font","Set the type of font used for normal text",FONT, NULL);
2818 add_var(OPT_FLOAT,"book_text_size","bsize",&font_scales[BOOK_FONT],change_text_zoom,1,"Book Text Size","Set the size of the text in in-game books",FONT,0.1,2.0,0.01);
2819 add_var(OPT_MULTI,"book_font","bfont",&font_idxs[BOOK_FONT],change_font,0,"Book Font","Set the type of font used for text in in-game books",FONT, NULL);
2820 add_var(OPT_FLOAT,"note_text_size", "notesize", &font_scales[NOTE_FONT], change_text_zoom, 0.8, "Notepad Text Size","Sets the size of the text in the notepad", FONT, 0.1, 2.0, 0.01);
2821 add_var(OPT_MULTI,"note_font","notefont",&font_idxs[NOTE_FONT],change_font,0,"Note Font","Set the type of font used for text in user notes",FONT, NULL);
2822 add_var(OPT_FLOAT,"rules_text_size","rsize",&font_scales[RULES_FONT],change_text_zoom,1,"Rules Text Size","Set the size of the rules text",FONT,0.1,2.0,0.01);
2823 add_var(OPT_MULTI,"rules_font","rfont",&font_idxs[RULES_FONT],change_font,0,"Rules Font","Set the type of font used for drawing the game rules",FONT, NULL);
2824 add_var(OPT_FLOAT, "encyclopedia_text_size", "esize", &font_scales[ENCYCLOPEDIA_FONT],
2825 change_text_zoom, 1, "Encyclopedia Text Size",
2826 "Set the size of the encyclopedia and help text", FONT, 0.1, 2.0, 0.01);
2827 add_var(OPT_MULTI, "encyclopedia_font", "efont", &local_encyclopedia_font,
2828 change_font, 0, "Encyclopedia Font",
2829 "Set the type of font used for drawing the encycloepdia and ingame help",
2830 FONT, NULL);
2831 // the tab map scale changes mean previous setting are too small, make sure all users are reset to the new scaling by using a different variable
2832 add_var(OPT_FLOAT,"mapmark_text_size_1", "marksize", &font_scales[MAPMARK_FONT], change_text_zoom, 1.0, "Mapmark Text Size","Sets the size of the mapmark text", FONT, 0.1, 2.0, 0.01);
2833 add_var(OPT_FLOAT,"ui_scale","ui_scale",&local_ui_scale,change_ui_scale,1,"User interface scaling factor","Scale user interface by this factor, useful for high DPI displays. Note: the options window will be rescaled after reopening.",FONT,0.75,3.0,0.01);
2834 add_var(OPT_INT,"cursor_scale_factor","cursor_scale_factor",&cursor_scale_factor ,change_cursor_scale_factor,cursor_scale_factor,"Mouse pointer scaling factor","The size of the mouse pointer is scaled by this factor",FONT, 1, max_cursor_scale_factor);
2835 add_var(OPT_BOOL,"disable_window_scaling_controls","disablewindowscalingcontrols", get_scale_flag_MW(), change_var, 0, "Disable Window Scaling Controls", "If you do not want to use keys or mouse+scrollwheel to scale individual windows, set this option.", FONT);
2836 add_var(OPT_FLOAT,"trade_win_scale","tradewinscale",get_scale_WM(MW_TRADE),change_win_scale_factor,1.0f,"Trade window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2837 add_var(OPT_FLOAT,"item_win_scale","itemwinscale",get_scale_WM(MW_ITEMS),change_win_scale_factor,1.0f,"Inventory window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2838 add_var(OPT_FLOAT,"bags_win_scale","bagswinscale",get_scale_WM(MW_BAGS),change_win_scale_factor,1.0f,"Ground bag window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2839 add_var(OPT_FLOAT,"spells_win_scale","spellswinscale",get_scale_WM(MW_SPELLS),change_win_scale_factor,1.0f,"Spells window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2840 add_var(OPT_FLOAT,"storage_win_scale","storagewinscale",get_scale_WM(MW_STORAGE),change_win_scale_factor,1.0f,"Storage window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2841 add_var(OPT_FLOAT,"manu_win_scale","manuwinscale",get_scale_WM(MW_MANU),change_win_scale_factor,1.0f,"Manufacturing window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2842 add_var(OPT_FLOAT,"emote_win_scale","emotewinscale",get_scale_WM(MW_EMOTE),change_win_scale_factor,1.0f,"Emote window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2843 add_var(OPT_FLOAT,"questlog_win_scale","questlogwinscale",get_scale_WM(MW_QUESTLOG),change_win_scale_factor,1.0f,"Quest log window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2844 add_var(OPT_FLOAT,"note_url_win_scale","noteurlwinscale",get_scale_WM(MW_INFO),change_win_scale_factor,1.0f,"Notepad/URL window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2845 add_var(OPT_FLOAT,"buddy_win_scale","buddywinscale",get_scale_WM(MW_BUDDY),change_win_scale_factor,1.0f,"Buddy window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2846 add_var(OPT_FLOAT,"stats_win_scale","statswinscale",get_scale_WM(MW_STATS),change_win_scale_factor,1.0f,"Stats window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2847 add_var(OPT_FLOAT,"help_win_scale","helpwinscale",get_scale_WM(MW_HELP),change_win_scale_factor,1.0f,"Help window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2848 add_var(OPT_FLOAT,"ranging_win_scale","rangingwinscale",get_scale_WM(MW_RANGING),change_win_scale_factor,1.0f,"Ranging window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2849 add_var(OPT_FLOAT,"achievements_win_scale","achievementswinscale",get_scale_WM(MW_ACHIEVE),change_win_scale_factor,1.0f,"Achievements window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2850 add_var(OPT_FLOAT,"dialogue_win_scale","dialoguewinscale",get_scale_WM(MW_DIALOGUE),change_win_scale_factor,1.0f,"Dialogue window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2851 add_var(OPT_FLOAT,"quickbar_win_scale","quickbarwinscale",get_scale_WM(MW_QUICKBAR),change_win_scale_factor,1.0f,"Quickbar window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2852 add_var(OPT_FLOAT,"quickspells_win_scale","quickspellswinscale",get_scale_WM(MW_QUICKSPELLS),change_win_scale_factor,1.0f,"Quickspells window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2853 add_var(OPT_FLOAT,"chat_win_scale","chatwinscale",get_scale_WM(MW_CHAT),change_win_scale_factor,1.0f,"Chat window scaling factor",win_scale_description,FONT,win_scale_min,win_scale_max,win_scale_step);
2854 add_var(OPT_FLOAT,"options_win_scale","optionswinscale",&elconf_custom_scale,change_elconf_win_scale_factor,1.0f,"Options window scaling factor","Multiplied by the user interface scaling factor. Change will take effect after closing then reopening the window.",FONT,win_scale_min,win_scale_max,win_scale_step);
2855 #ifdef NEW_CURSOR
2856 add_var(OPT_BOOL,"sdl_cursors","sdl_cursors", &sdl_cursors, change_sdl_cursor,1,"Use Standard Black/White Mouse Pointers", "When disabled, use the experimental coloured mouse pointers. Needs the texture from Git dev-data-files/cursor2.dss.", FONT);
2857 add_var(OPT_BOOL,"big_cursors","big_cursors", &big_cursors, change_var,0,"Use Large Pointers", "When using the experiment coloured mouse pointers, use the large pointer set.", FONT);
2858 add_var(OPT_FLOAT,"pointer_size","pointer_size", &pointer_size, change_float,1.0,"Coloured Pointer Size", "When using the experiment coloured mouse pointers, set the scale of the pointer. 1.0 is 1:1 scale.", FONT,0.25,4.0,0.05);
2859 #endif // NEW_CURSOR
2860 // default fonts
2861 add_var(OPT_STRING_INI,"def_ui_font", "def_ui_font", def_ui_font_str, change_string, sizeof(def_ui_font_str), "def_ui_font", "Default font for UI", FONT);
2862 add_var(OPT_STRING_INI,"def_name_font", "def_name_font", def_name_font_str, change_string, sizeof(def_name_font_str), "def_name_font", "Default UI font", FONT);
2863 add_var(OPT_STRING_INI,"def_chat_font", "def_chat_font", def_chat_font_str, change_string, sizeof(def_chat_font_str), "def_chat_font", "Default Chat font", FONT);
2864 add_var(OPT_STRING_INI,"def_note_font", "def_note_font", def_note_font_str, change_string, sizeof(def_note_font_str), "def_note_font", "Default Note font", FONT);
2865 add_var(OPT_STRING_INI,"def_book_font", "def_book_font", def_book_font_str, change_string, sizeof(def_book_font_str), "def_book_font", "Default Book font", FONT);
2866 add_var(OPT_STRING_INI,"def_rules_font", "def_rules_font", def_rules_font_str, change_string, sizeof(def_rules_font_str), "def_rules_font", "Default Rules font", FONT);
2867 add_var(OPT_STRING_INI,"def_encyclopedia_font", "def_encyclopedia_font", def_encyclopedia_font_str, change_string, sizeof(def_encyclopedia_font_str), "def_encyclopedia_font", "Default Encyclopedia font", FONT);
2868 // FONT TAB
2869
2870
2871 // SERVER TAB
2872 add_var(OPT_STRING,"username", "u", active_username_str, change_string, sizeof(active_username_str),
2873 "Username", "Your user name here", SERVER);
2874 add_var(OPT_PASSWORD, "password", "p", active_password_str, change_string, sizeof(active_password_str),
2875 "Password", "Put your password here", SERVER);
2876 add_var(OPT_BOOL,"passmngr_enabled","pme",&passmngr_enabled,change_var,0,"Enable Password Manager", "If enabled, user names and passwords are saved locally by the built-in password manager. Multiple sets of details can be saved. You can choose which details to use at the login screen.",SERVER);
2877 add_var(OPT_MULTI,"log_chat","log",&log_chat,change_int,LOG_SERVER,"Log Messages","Log messages from the server (chat, harvesting events, GMs, etc)",SERVER,"Do not log chat", "Log chat only", "Log server messages", "Log server to srv_log.txt", NULL);
2878 add_var(OPT_BOOL,"rotate_chat_log","rclog",&rotate_chat_log_config_var,change_rotate_chat_log,0,"Rotate Chat Log File","Tag the chat/server message log files with year and month. You will still need to manage deletion of the old files. Requires a client restart.",SERVER);
2879 add_var(OPT_BOOL,"buddy_log_notice", "buddy_log_notice", &buddy_log_notice, change_var, 1, "Log Buddy Sign On/Off", "Toggle whether to display notices when people on your buddy list log on or off", SERVER);
2880 add_var(OPT_STRING,"language", "lang", lang, change_string, sizeof(lang), "Language", "Wah?", SERVER);
2881 add_var(OPT_STRING, "browser", "b", browser_name, change_string, sizeof(browser_name), "Browser",
2882 "Location of your web browser (Windows users leave blank to use default browser)",
2883 SERVER);
2884 add_var(OPT_BOOL,"write_ini_on_exit", "wini", &write_ini_on_exit, change_var, 1,"Save INI","Save options when you quit",SERVER);
2885 add_var(OPT_STRING,"data_dir","dir",datadir,change_dir_name,90,"Data Directory","Place were we keep our data. Can only be changed with a Client restart.",SERVER);
2886 add_var(OPT_BOOL,"serverpopup","spu",&use_server_pop_win,change_var,1,"Use Special Text Window","Toggles whether server messages from channel 255 are displayed in a pop up window.",SERVER);
2887 /* Note: We don't take any action on the already-running thread, as that wouldn't necessarily be good. */
2888 add_var(OPT_BOOL,"autoupdate","aup",&auto_update,change_var,1,"Automatic Updates","Toggles whether updates are automatically downloaded.",SERVER);
2889 #ifdef CUSTOM_UPDATE
2890 add_var(OPT_BOOL,"customupdate","cup",&custom_update,change_custom_update,1,"Custom Looks Updates","Toggles whether custom look updates are automatically downloaded.",SERVER);
2891 add_var(OPT_BOOL,"showcustomclothing","scc",&custom_clothing,change_custom_clothing,1,"Show Custom clothing","Toggles whether custom clothing is shown.",SERVER);
2892 #endif //CUSTOM_UPDATE
2893 #ifdef JSON_FILES
2894 add_var(OPT_BOOL, "use_json_user_files_v1", "usejsonuserfiles_v1", &use_json_user_files, change_use_json_user_files, 0, "Use New Format To Save User Files (.json)",
2895 "NOTE: Use this option to enable the new format for saving user data. If you change this option, data is automatically saved using the chosen format. Disable this option before switching back to 1.9.5p8 or older clients.", SERVER);
2896 #endif
2897 // SERVER TAB
2898
2899
2900 // AUDIO TAB
2901 #ifdef NEW_SOUND
2902 add_var(OPT_BOOL,"disable_sound", "nosound", &no_sound, disable_sound, 0, "Disable Sound & Music System", "Disable all of the sound effects and music processing", AUDIO);
2903 add_var(OPT_STRING, "sound_device", "snddev", sound_device, change_string, sizeof(sound_device),
2904 "Sound Device", "Device used for playing sounds & music", AUDIO);
2905 add_var(OPT_BOOL,"enable_sound", "sound", &sound_on, toggle_sounds, 0, "Enable Sound Effects", "Turn sound effects on/off", AUDIO);
2906 add_var(OPT_FLOAT,"sound_gain", "sgain", &sound_gain, change_sound_level, 1, "Overall Sound Effects Volume", "Adjust the overall sound effects volume", AUDIO, 0.0, 1.0, 0.1);
2907 add_var(OPT_FLOAT,"crowd_gain", "crgain", &crowd_gain, change_sound_level, 1, "Crowd Sounds Volume", "Adjust the crowd sound effects volume", AUDIO, 0.0, 1.0, 0.1);
2908 add_var(OPT_FLOAT,"enviro_gain", "envgain", &enviro_gain, change_sound_level, 1, "Environmental Sounds Volume", "Adjust the environmental sound effects volume", AUDIO, 0.0, 1.0, 0.1);
2909 add_var(OPT_FLOAT,"actor_gain", "again", &actor_gain, change_sound_level, 1, "Character Sounds Volume", "Adjust the sound effects volume for fighting, magic and other character sounds", AUDIO, 0.0, 1.0, 0.1);
2910 add_var(OPT_FLOAT,"walking_gain", "wgain", &walking_gain, change_sound_level, 1, "Walking Sounds Volume", "Adjust the walking sound effects volume", AUDIO, 0.0, 1.0, 0.1);
2911 add_var(OPT_FLOAT,"gamewin_gain", "gwgain", &gamewin_gain, change_sound_level, 1, "Item and Inventory Sounds Volume", "Adjust the item and inventory sound effects volume", AUDIO, 0.0, 1.0, 0.1);
2912 add_var(OPT_FLOAT,"client_gain", "clgain", &client_gain, change_sound_level, 1, "Misc Client Sounds Volume", "Adjust the client sound effects volume (warnings, hud/button clicks)", AUDIO, 0.0, 1.0, 0.1);
2913 add_var(OPT_FLOAT,"warn_gain", "wrngain", &warnings_gain, change_sound_level, 1, "Text Warning Sounds Volume", "Adjust the user configured text warning sound effects volume", AUDIO, 0.0, 1.0, 0.1);
2914 add_var(OPT_BOOL,"enable_music","music",&music_on,toggle_music,0,"Enable Music","Turn music on/off",AUDIO);
2915 add_var(OPT_FLOAT,"music_gain","mgain",&music_gain,change_sound_level,1,"Music Volume","Adjust the music volume",AUDIO,0.0,1.0,0.1);
2916 #endif //NEW_SOUND
2917 // AUDIO TAB
2918
2919
2920 // VIDEO TAB
2921 add_var(OPT_BOOL,"full_screen","fs",&full_screen,toggle_full_screen_mode,0,"Full Screen","Changes between full screen and windowed mode",VIDEO);
2922 add_var(OPT_MULTI,"video_mode","vid",&video_mode,switch_vidmode,4,"Video Mode","The video mode you wish to use",VIDEO, "Userdefined", NULL);
2923 for (i = 0; i < video_modes_count; i++)
2924 {
2925 static char str[100];
2926 safe_snprintf(str, sizeof(str), "%dx%dx%d", video_modes[i].width, video_modes[i].height, video_modes[i].bpp);
2927 if (video_modes[i].name)
2928 free(video_modes[i].name);
2929 video_modes[i].name = strdup(str);
2930 add_multi_option("video_mode", video_modes[i].name);
2931 }
2932 add_var(OPT_INT,"video_width","width",&video_user_width,change_int, 640,"Userdefined width","Userdefined window width",VIDEO, 640,INT_MAX);
2933 add_var(OPT_INT,"video_height","height",&video_user_height,change_int, 480,"Userdefined height","Userdefined window height",VIDEO, 480,INT_MAX);
2934 add_var(OPT_INT,"limit_fps","lfps",&limit_fps,change_fps,0,"Limit FPS","Limit the frame rate to reduce load on the system",VIDEO,0,INT_MAX);
2935 add_var(OPT_FLOAT,"gamma","g",&gamma_var,change_gamma,1,"Gamma","How bright your display should be.",VIDEO,0.10,3.00,0.05);
2936 add_var(OPT_BOOL,"disable_gamma_adjust","dga",&disable_gamma_adjust,change_var,0,"Disable Gamma Adjustment","Stop the client from adjusting the display gamma.",VIDEO);
2937 #ifdef ANTI_ALIAS
2938 add_var(OPT_BOOL,"anti_alias", "aa", &anti_alias, change_aa, 0, "Toggle Anti-Aliasing", "Anti-aliasing makes edges look smoother", VIDEO);
2939 #endif //ANTI_ALIAS
2940 #ifdef FSAA
2941 add_var(OPT_MULTI_H, "anti_aliasing", "fsaa", &fsaa_index, change_fsaa, 0, "Anti-Aliasing", "Full Scene Anti-Aliasing", VIDEO, get_fsaa_mode_str(0), NULL);
2942 for (i = 1; i < get_fsaa_mode_count(); i++)
2943 {
2944 if (get_fsaa_mode(i) == 1)
2945 {
2946 add_multi_option("anti_aliasing", get_fsaa_mode_str(i));
2947 }
2948 }
2949 #endif /* FSAA */
2950 add_var (OPT_BOOL, "use_frame_buffer", "fb", &use_frame_buffer, change_frame_buffer, 0, "Toggle Frame Buffer Support", "Toggle frame buffer support. Used for reflection and shadow mapping.", VIDEO);
2951 add_var(OPT_INT_F,"water_shader_quality","water_shader_quality",&water_shader_quality,change_water_shader_quality,1," water shader quality","Defines what shader is used for water rendering. Higher values are slower but look better. Needs \"toggle frame buffer support\" to be turned on.",VIDEO, int_zero_func, int_max_water_shader_quality);
2952 add_var(OPT_BOOL,"small_actor_texture_cache","small_actor_tc",&small_actor_texture_cache,change_small_actor_texture_cache,0,"Small actor texture cache","A small Actor texture cache uses less video memory, but actor loading can be slower.",VIDEO);
2953 add_var(OPT_BOOL,"use_vertex_buffers","vbo",&use_vertex_buffers,change_vertex_buffers,0,"Vertex Buffer Objects","Toggle the use of the vertex buffer objects, restart required to activate it",VIDEO);
2954 add_var(OPT_BOOL, "use_animation_program", "uap", &use_animation_program, change_use_animation_program, 1, "Use animation program", "Use GL_ARB_vertex_program for actor animation", VIDEO);
2955 add_var(OPT_BOOL_INI, "video_info_sent", "svi", &video_info_sent, change_var, 0, "Video info sent", "Video information are sent to the server (like OpenGL version and OpenGL extentions)", VIDEO);
2956 // VIDEO TAB
2957
2958
2959 // GFX TAB
2960 add_var(OPT_BOOL,"shadows_on","shad",&shadows_on,change_shadows,0,"Shadows","Toggles the shadows", GFX);
2961 add_var(OPT_BOOL,"use_shadow_mapping", "sm", &use_shadow_mapping, change_shadow_mapping, 0, "Shadow Mapping", "If you want to use some better quality shadows, enable this. It will use more resources, but look prettier.", GFX);
2962 add_var(OPT_MULTI,"shadow_map_size","smsize",&shadow_map_size_multi,change_shadow_map_size,3,"Shadow Map Size","This parameter determines the quality of the shadow maps. You should as minimum set it to 512.",GFX,"256","512","768","1024","1280","1536","1792","2048","3072","4096",NULL);
2963 add_var(OPT_BOOL,"no_adjust_shadows","noadj",&no_adjust_shadows,change_var,0,"Don't Adjust Shadows","If enabled, tell the engine not to disable the shadows if the frame rate is too low.",GFX);
2964 add_var(OPT_BOOL,"clouds_shadows","cshad",&clouds_shadows,change_clouds_shadows,1,"Cloud Shadows","The clouds shadows are projected on the ground, and the game looks nicer with them on.",GFX);
2965 add_var(OPT_BOOL,"show_reflection","refl",&show_reflection,change_reflection,1,"Show Reflections","Toggle the reflections",GFX);
2966 add_var(OPT_BOOL,"render_fog","fog",&use_fog,change_var,1,"Render Fog","Toggles fog rendering.",GFX);
2967 add_var(OPT_BOOL,"show_weather","weather",&show_weather,change_var,1,"Show Weather Effects","Toggles thunder, lightning and rain effects.",GFX);
2968 add_var(OPT_BOOL,"skybox_show_sky","sky", &skybox_show_sky, change_sky_var,1,"Show Sky", "Enable the sky box.", GFX);
2969 /* add_var(OPT_BOOL,"reflect_sky","reflect_sky", &reflect_sky, change_var,1,"Reflect Sky", "Sky Performance Option. Disable these from top to bottom until you're happy", GFX); */
2970 add_var(OPT_BOOL,"skybox_show_clouds","sky_clouds", &skybox_show_clouds, change_sky_var,1,"Show Clouds", "Sky Performance Option. Disable these from top to bottom until you're happy", GFX);
2971 add_var(OPT_BOOL,"skybox_show_sun","sky_sun", &skybox_show_sun, change_sky_var,1,"Show Sun", "Sky Performance Option. Disable these from top to bottom until you're happy", GFX);
2972 add_var(OPT_BOOL,"skybox_show_moons","sky_moons", &skybox_show_moons, change_sky_var,1,"Show Moons", "Sky Performance Option. Disable these from top to bottom until you're happy", GFX);
2973 add_var(OPT_BOOL,"skybox_show_stars","sky_stars", &skybox_show_stars, change_sky_var,1,"Show Stars", "Sky Performance Option. Disable these from top to bottom until you're happy", GFX);
2974 add_var(OPT_INT,"skybox_update_delay","skybox_update_delay", &skybox_update_delay, change_int, skybox_update_delay, "Sky Update Delay", "Specifies the delay in seconds between 2 updates of the sky and the environment. A value of 0 corresponds to an update at every frame.", GFX, 0, 60);
2975 add_var(OPT_INT,"particles_percentage","pp",&particles_percentage,change_particles_percentage,100,"Particle Percentage","If you experience a significant slowdown when particles are nearby, you should consider lowering this number.",GFX,0,100);
2976 add_var(OPT_BOOL,"special_effects", "sfx", &special_effects, change_var, 1, "Toggle Special Effects", "Special spell effects", GFX);
2977 add_var(OPT_BOOL,"use_eye_candy", "ec", &use_eye_candy, change_eye_candy, 1, "Enable Eye Candy", "Toggles most visual effects, like spells' and harvesting events'. Needs OpenGL 1.5", GFX);
2978 add_var(OPT_BOOL,"enable_blood","eb",&enable_blood,change_var,0,"Enable Blood","Enable blood special effects during combat.",GFX);
2979 add_var(OPT_BOOL,"use_harvesting_eye_candy","uharvec",&use_harvesting_eye_candy,change_var,0,"Enable harvesting effect","This effect shows that you're harvesting. Only you can see it!",GFX);
2980 add_var(OPT_BOOL,"use_lamp_halo","ulh",&use_lamp_halo,change_var,0,"Use Lamp Halos","Enable halos for torches, candles, etc.",GFX);
2981 add_var(OPT_BOOL,"use_fancy_smoke","ufs",&use_fancy_smoke,change_var,0,"Use Fancy Smoke","If your system has performance problems around chimney smoke, turn this option off.",GFX);
2982 add_var(OPT_FLOAT,"max_ec_framerate","ecmaxf",&max_ec_framerate,change_max_ec_framerate,45,"Max Effects Framerate","If your framerate is above this amount, eye candy will use maximum detail.",GFX,2.0,FLT_MAX,1.0);
2983 add_var(OPT_FLOAT,"min_ec_framerate","ecminf",&min_ec_framerate,change_min_ec_framerate,15,"Min Effects Framerate","If your framerate is below this amount, eye candy will use minimum detail.",GFX,1.0,FLT_MAX,1.0);
2984 add_var(OPT_INT,"light_columns_threshold","lct",&light_columns_threshold,change_int,5,"Light columns threshold","If your framerate is below this amount, you will not get columns of light around teleportation effects (useful for slow systems).",GFX, 0, INT_MAX);
2985 add_var(OPT_INT,"max_idle_cycles_per_second","micps",&max_idle_cycles_per_second,change_int,40,"Max Idle Cycles Per Second","The eye candy 'idle' function, which moves particles around, will run no more than this often. If your CPU is your limiting factor, lowering this can give you a higher framerate. Raising it gives smoother particle motion (up to the limit of your framerate).",GFX, 1, INT_MAX);
2986 #ifdef NEW_ALPHA
2987 add_var(OPT_BOOL,"use_3d_alpha_blend","3dalpha",&use_3d_alpha_blend,change_var,1,"3D Alpha Blending","Toggle the use of the alpha blending on 3D objects",GFX);
2988 #endif //NEW_ALPHA
2989 // GFX TAB
2990
2991
2992 // CAMERA TAB
2993 add_var(OPT_FLOAT,"far_plane", "far_plane", &far_plane, change_projection_float, 100.0, "Maximum Viewing Distance", "Adjusts how far you can see.", CAMERA, 40.0, 200.0, 1.0);
2994 add_var(OPT_FLOAT,"far_reflection_plane", "far_reflection_plane", &far_reflection_plane, change_projection_float, 100.0, "Maximum Reflection Distance", "Adjusts how far the reflections are displayed.", CAMERA, 0.0, 200.0, 1.0);
2995 add_var(OPT_FLOAT,"max_zoom_level","maxzoomlevel",&max_zoom_level,change_float,max_zoom_level,"Maximum Camera Zoom Out","Sets the maxiumum value that the camera can zoom out",CAMERA,4.0,8.0,0.5);
2996 add_var(OPT_FLOAT,"perspective", "perspective", &perspective, change_projection_float, 0.15f, "Perspective", "The degree of perspective distortion. Change if your view looks odd.", CAMERA, 0.01, 0.80, 0.01);
2997 add_var(OPT_BOOL,"isometric" ,"isometric", &isometric, change_projection_bool, 1, "Use Isometric View", "Toggle the use of isometric (instead of perspective) view", CAMERA);
2998 add_var(OPT_BOOL,"follow_cam","folcam", &fol_cam, toggle_follow_cam,0,"Follow Camera", "Causes the camera to stay fixed relative to YOU and not the world", CAMERA);
2999 add_var(OPT_BOOL,"fol_cam_behind","fol_cam_behind", &fol_cam_behind, toggle_follow_cam_behind,0,"Keep the camera behind the char", "Causes the camera to stay behind you while walking (works only in follow camera mode)", CAMERA);
3000 add_var(OPT_BOOL,"extended_cam","extcam", &ext_cam, toggle_ext_cam,0,"Extended Camera", "Camera range of motion extended and adjusted to allow overhead and first person style camera.", CAMERA);
3001 add_var(OPT_BOOL,"ext_cam_auto_zoom","autozoom", &ext_cam_auto_zoom, change_var,0,"Auto zoom", "Allows the camera to zoom automatically when getting close to the ground (works only in extended camera mode and with a max tilt angle over 90.0).", CAMERA);
3002 add_var(OPT_FLOAT,"normal_camera_rotation_speed","nrot",&normal_camera_rotation_speed,change_float,15,"Camera Rotation Speed","Set the speed the camera rotates",CAMERA,1.0,FLT_MAX,0.5);
3003 add_var(OPT_FLOAT,"fine_camera_rotation_speed","frot",&fine_camera_rotation_speed,change_float,1,"Fine Rotation Speed","Set the fine camera rotation speed (when holding shift+arrow key)",CAMERA,1.0,FLT_MAX,0.5);
3004 add_var(OPT_FLOAT,"normal_camera_deceleration","ncd",&normal_camera_deceleration,change_float,normal_camera_deceleration,"Camera Rotation Deceleration","Set the camera rotation deceleration",CAMERA,0.01,1.0,0.01);
3005 add_var(OPT_FLOAT,"min_tilt_angle","min_tilt_angle", &min_tilt_angle, change_tilt_float,30.0,"Minimum tilt angle", "Minimum angle that the camera can reach when raising it (works only in extended camera mode).", CAMERA, 20.0, 45.0, 1.0);
3006 add_var(OPT_FLOAT,"max_tilt_angle","max_tilt_angle", &max_tilt_angle, change_tilt_float,90.0,"Maximum tilt angle", "Maximum angle that the camera can reach when lowering it (works only in extended camera mode).", CAMERA, 60.0, 150.0, 1.0);
3007 add_var(OPT_FLOAT,"follow_strength","f_strn",&fol_strn,change_float,0.1,"Follow Camera Snapiness","Adjust how responsive the follow camera is. 0 is stopped, 1 is fastest. Use the three numbers below to tweak the feel. Try them one at a time, then mix them to find a ratio you like.",CAMERA,0.0,1.00,0.01);
3008 add_var(OPT_FLOAT,"const_speed","f_con",&fol_con,change_float,7,"Constant Speed","The basic rate that the camera rotates at to keep up with you.",CAMERA,0.0,10.00,1.0);
3009 add_var(OPT_FLOAT,"lin_speed","f_lin",&fol_lin,change_float,1,"Linear Decel.","A hit of speed that drops off as the camera gets near its set point.",CAMERA,0.0,10.00,1.0);
3010 add_var(OPT_FLOAT,"quad_speed","f_quad",&fol_quad,change_float,1,"Quadratic Decel.","A hit of speed that drops off faster as it nears the set point.",CAMERA,0.0,10.00,1.0);
3011 // CAMERA TAB
3012
3013
3014 // TROUBLESHOOT TAB
3015 add_var(OPT_BOOL,"shadows_on","shad",&shadows_on,change_shadows,0,"Shadow Bug","Some video cards have trouble with the shadows. Uncheck this if everything you see is white.", TROUBLESHOOT);
3016 // Grum: attempt to work around bug in Ati linux drivers.
3017 add_var(OPT_BOOL,"ati_click_workaround", "atibug", &ati_click_workaround, change_var, 0, "ATI Bug", "If you are using an ATI graphics card and don't move when you click, try this option to work around a bug in their drivers.", TROUBLESHOOT);
3018 add_var (OPT_BOOL,"use_old_clicker", "oldmclick", &use_old_clicker, change_var, 0, "Mouse Bug", "Unrelated to ATI graphics cards, if clicking to walk doesn't move you, try toggling this option.", TROUBLESHOOT);
3019 add_var(OPT_BOOL,"use_new_selection", "uns", &use_new_selection, change_new_selection, 1, "New selection", "Using new selection can give you a higher framerate. However, if your cursor does not change when over characters or items, try disabling this option.", TROUBLESHOOT);
3020 add_var(OPT_BOOL,"clear_mod_keys_on_focus", "clear_mod_keys_on_focus", &clear_mod_keys_on_focus, change_var, 0, "Clear modifier keys when window focused","If you have trouble with modifier keys (shift/ctrl/alt etc) when keyboard focus returns, enable this option to force all modifier keys up.", TROUBLESHOOT);
3021 #ifndef OSX
3022 add_var(OPT_BOOL,"use_compiled_vertex_array","cva",&use_compiled_vertex_array,change_compiled_vertex_array,1,"Compiled Vertex Array","Some systems will not support the new compiled vertex array in EL. Disable this if some 3D objects do not display correctly.",TROUBLESHOOT);
3023 #endif
3024 add_var(OPT_BOOL,"use_draw_range_elements","dre",&use_draw_range_elements,change_var,1,"Draw Range Elements","Disable this if objects appear partially stretched.",TROUBLESHOOT);
3025 add_var(OPT_BOOL,"use_point_particles","upp",&use_point_particles,change_point_particles,1,"Point Particles","Some systems will not support the new point based particles in EL. Disable this if your client complains about not having the point based particles extension.",TROUBLESHOOT);
3026 #ifdef OSX
3027 add_var(OPT_BOOL, "square_buttons", "sqbutt",&square_buttons,change_var,1,"Square Buttons","Use square buttons rather than rounded",TROUBLESHOOT);
3028 #endif
3029 add_var(OPT_BOOL, "use_animation_program", "uap", &use_animation_program, change_use_animation_program, 1, "Use animation program", "Use GL_ARB_vertex_program for actor animation", TROUBLESHOOT);
3030 add_var(OPT_BOOL,"poor_man","poor",&poor_man,change_poor_man,0,"Poor Man","If the game is running very slow for you, toggle this setting.",TROUBLESHOOT);
3031 // TROUBLESHOOT TAB
3032
3033 // DEBUGTAB TAB
3034 #ifdef DEBUG
3035 add_var(OPT_FLOAT,"sunny_sky_bias","sunny_sky_bias", &skybox_sunny_sky_bias, change_float,0.0,"Sunny sky bias", "Change the radius of the sun effect on the sky.", DEBUGTAB, -1.0, 1.0, 0.01);
3036 add_var(OPT_FLOAT,"sunny_clouds_bias","sunny_clouds_bias", &skybox_sunny_clouds_bias, change_float,-0.1,"Sunny clouds bias", "Change the radius of the sun effect on the clouds.", DEBUGTAB, -1.0, 1.0, 0.01);
3037 add_var(OPT_FLOAT,"sunny_fog_bias","sunny_fog_bias", &skybox_sunny_fog_bias, change_float,0.0,"Sunny fog bias", "Change the radius of the sun effect on the fog.", DEBUGTAB, -1.0, 1.0, 0.01);
3038 add_var(OPT_FLOAT,"water_tiles_extension","wt_ext", &water_tiles_extension, change_float,200.0,"Water tiles extension", "Extends the water tiles upto the specified distance.", DEBUGTAB, 0.0, 1000.0, 1.0);
3039 add_var(OPT_BOOL,"enable_client_aiming","eca",&enable_client_aiming,change_var,0,"Enable client aiming","Allow to aim at something by holding CTRL key. This aim is only done on client side and is used only for debugging purposes. Warning: enabling this code can produce server resyncs or locks when playing with missiles...",DEBUGTAB);
3040 add_var(OPT_BOOL,"render_skeleton","rskel",&render_skeleton,change_var,0,"Render Skeleton", "Render the Cal3d skeletons.", DEBUGTAB);
3041 add_var(OPT_BOOL,"render_mesh","rmesh",&render_mesh,change_var,1,"Render Mesh", "Render the meshes", DEBUGTAB);
3042 add_var(OPT_BOOL,"render_bones_id","rbid",&render_bones_id,change_var,0,"Render bones ID", "Render the bones ID", DEBUGTAB);
3043 add_var(OPT_BOOL,"render_bones_orientation","rbor",&render_bones_orientation,change_var,0,"Render bones orientation", "Render the bones orientation", DEBUGTAB);
3044 add_var(OPT_FLOAT,"near_plane", "near_plane", &near_plane, change_projection_float, 0.1, "Minimum Viewing Distance", "Adjusts how near you can see.", DEBUGTAB, 0.1, 10.0, 0.1);
3045 add_var(OPT_BOOL,"skybox_local_weather","skybox_local_weather", &skybox_local_weather, change_var,0,"Local Weather", "Show local weather areas on the sky. It allows to see distant weather but can reduce performance.", DEBUGTAB);
3046 #endif // DEBUG
3047 // DEBUGTAB TAB
3048
3049 } /* end init_ELC_vars() */
3050 #endif // def ELC
3051
3052
3053
init_vars(void)3054 void init_vars(void)
3055 {
3056 #ifdef ELC
3057 init_ELC_vars();
3058
3059 #else
3060 // NOTE !!!!
3061 // some repeated in init_ELC_vars() so that we can control the order showin in the tabs
3062
3063 //Global vars...
3064 // Only possible to do at startup - this could of course be changed by using a special function for this purpose. I just don't see why you'd want to change the directory whilst running the game...
3065 add_var(OPT_STRING,"data_dir","dir",datadir,change_dir_name,90,"Data Directory","Place were we keep our data. Can only be changed with a Client restart.",SERVER);
3066 add_var(OPT_INT,"limit_fps","lfps",&limit_fps,change_int,0,"Limit FPS","Limit the frame rate to reduce load on the system",VIDEO,0,INT_MAX);
3067 #ifdef MAP_EDITOR
3068 add_var(OPT_INT,"video_mode","vid",&video_mode,switch_vidmode,4,"Video Mode","The video mode you wish to use",VIDEO,1,7);
3069 add_var(OPT_BOOL,"close_browser_on_select","cbos",&close_browser_on_select, change_var, 0,"Close Browser","Close the browser on select",HUD);
3070 add_var(OPT_BOOL,"show_position_on_minimap","spos",&show_position_on_minimap, change_var, 0,"Show Pos","Show position on the minimap",HUD);
3071 add_var(OPT_SPECINT,"auto_save","asv",&auto_save_time, set_auto_save_interval, 0,"Auto Save","Auto Save",HUD,0,INT_MAX);
3072 add_var(OPT_BOOL,"show_grid","sgrid",&view_grid, change_var, 0, "Show Grid", "Show grid",HUD);
3073 add_var(OPT_BOOL,"show_tooltips","stooltips",&view_tooltips, change_var, 0, "Show Tooltips", "Show tooltips",HUD);
3074 add_var(OPT_BOOL,"show_reflections","srefl",&show_mapeditor_reflections, change_var, 0, "Show reflections", "Show reflections, disabling improves editor performance",HUD);
3075 add_var(OPT_FLOAT, "ui_scale", "ui_scale", &ui_scale, change_float, 1, "User interface scaling factor",
3076 "Scale user interface by this factor, useful for high DPI displays.", FONT, 0.75, 3.0, 0.01);
3077 #endif
3078 #ifndef MAP_EDITOR
3079 add_var (OPT_BOOL, "use_frame_buffer", "fb", &use_frame_buffer, change_frame_buffer, 0, "Toggle Frame Buffer Support", "Toggle frame buffer support. Used for reflection and shadow mapping.", VIDEO);
3080 add_var(OPT_BOOL, "use_animation_program", "uap", &use_animation_program, change_use_animation_program, 1, "Use animation program", "Use GL_ARB_vertex_program for actor animation", VIDEO);
3081 #endif //MAP_EDITOR
3082 #ifdef OSX
3083 add_var(OPT_BOOL, "square_buttons", "sqbutt",&square_buttons,change_var,1,"Square Buttons","Use square buttons rather than rounded",HUD);
3084 #endif
3085
3086 #endif // ELC
3087
3088 }
3089
write_var(FILE * fout,int ivar)3090 static void write_var(FILE *fout, int ivar)
3091 {
3092 var_struct *var;
3093
3094 if (fout == NULL) return;
3095
3096 var = our_vars.var[ivar];
3097 switch (var->type)
3098 {
3099 case OPT_INT:
3100 case OPT_BOOL:
3101 case OPT_INT_F:
3102 case OPT_BOOL_INI:
3103 case OPT_INT_INI:
3104 {
3105 int *p = var->var;
3106 fprintf(fout, "#%s= %d\n", var->name, *p);
3107 break;
3108 }
3109 case OPT_MULTI:
3110 case OPT_MULTI_H:
3111 {
3112 int idx = *(const int*)var->var;
3113 const char* value = var->args.multi.elems[idx].value;
3114 if (value)
3115 fprintf(fout, "#%s= %d(%s)\n", var->name, idx, value);
3116 else
3117 fprintf(fout, "#%s= %d\n", var->name, idx);
3118 break;
3119 }
3120 case OPT_STRING:
3121 case OPT_STRING_INI:
3122 if (strcmp(var->name, "password") == 0)
3123 // Do not write the password to the file. If the user really wants it
3124 // s/he should edit the file.
3125 fprintf(fout, "#%s= \"\"\n", var->name);
3126 else
3127 fprintf(fout, "#%s= \"%s\"\n", var->name, (const char *)var->var);
3128 break;
3129 case OPT_PASSWORD:
3130 // Do not write the password to the file. If the user really wants it
3131 // s/he should edit the file.
3132 fprintf(fout, "#%s= \"\"\n", var->name);
3133 break;
3134 case OPT_FLOAT:
3135 case OPT_FLOAT_F:
3136 {
3137 float *g = var->var;
3138 fprintf(fout, "#%s= %g\n", var->name, *g);
3139 break;
3140 }
3141 }
3142 var->saved= 1; // keep only one copy of this setting
3143 }
3144
3145
read_el_ini(void)3146 int read_el_ini (void)
3147 {
3148 input_line line;
3149 #ifdef MAP_EDITOR
3150 FILE *fin= open_file_config("mapedit.ini", "r");
3151 #else
3152 FILE *fin= open_file_config(ini_filename, "r");
3153 #endif //MAP_EDITOR
3154
3155 if (fin == NULL){
3156 LOG_ERROR("%s: %s \"%s\": %s\n", reg_error_str, cant_open_file, ini_filename, strerror(errno));
3157 return 0;
3158 }
3159
3160 #ifdef ELC
3161 delay_poor_man = delay_update_highdpi_auto_scaling = 1;
3162 #endif
3163 while ( fgets (line, sizeof (input_line), fin) )
3164 {
3165 if (line[0] == '#')
3166 check_var(&(line[1]), INI_FILE_VAR);
3167 }
3168 #ifdef ELC
3169 delay_poor_man = delay_update_highdpi_auto_scaling = 0;
3170 action_poor_man(&poor_man);
3171 #endif
3172
3173 fclose (fin);
3174 return 1;
3175 }
3176
write_el_ini(void)3177 int write_el_ini (void)
3178 {
3179 #if !defined(WINDOWS)
3180 int fd;
3181 struct stat statbuff;
3182 #endif // !WINDOWS
3183 int nlines= 0, maxlines= 0, iline, ivar;
3184 input_line *cont= NULL;
3185 const char *last_line;
3186 FILE *file;
3187 short *written;
3188
3189 // first check if we need to change anything
3190 //
3191 // The advantage of skipping this check is that a new ini file would be
3192 // created in the users $HOME/.elc for Unix users, even if nothing
3193 // changed. However, most of the time it's pointless to update an
3194 // unchanged file.
3195 for (ivar= 0; ivar < our_vars.no; ivar++)
3196 {
3197 if (!our_vars.var[ivar]->saved)
3198 break;
3199 }
3200 if (ivar >= our_vars.no)
3201 return 1; // nothing changed, no need to write
3202
3203 // Consolidate changes for any items featured more than once - on different tabs for example.
3204 for (ivar= 0; ivar < our_vars.no; ivar++)
3205 {
3206 if (!our_vars.var[ivar]->saved)
3207 {
3208 size_t i;
3209 for (i=0; i<our_vars.no; i++)
3210 if (strcmp(our_vars.var[ivar]->name, our_vars.var[i]->name) == 0)
3211 if (our_vars.var[i]->saved)
3212 our_vars.var[i]->saved = 0;
3213 }
3214 }
3215
3216 // read the ini file
3217 file = open_file_config(ini_filename, "r");
3218 if(file == NULL){
3219 LOG_ERROR("%s: %s \"%s\": %s\n", reg_error_str, cant_open_file, ini_filename, strerror(errno));
3220 } else {
3221 maxlines= 300;
3222 cont= malloc (maxlines * sizeof (input_line));
3223 while (fgets (cont[nlines], sizeof (input_line), file) != NULL)
3224 {
3225 if (++nlines >= maxlines)
3226 {
3227 maxlines *= 2;
3228 cont= realloc (cont, maxlines * sizeof (input_line));
3229 }
3230 }
3231 fclose(file);
3232 }
3233
3234 // Now write the contents of the file, updating those variables that have been changed
3235 file = open_file_config(ini_filename, "w");
3236 if(file == NULL){
3237 LOG_ERROR("%s: %s \"%s\": %s\n", reg_error_str, cant_open_file, ini_filename, strerror(errno));
3238 free(cont);
3239 return 0;
3240 }
3241
3242 // Prevent duplicate entries by remembering which we have written
3243 written = calloc(our_vars.no, sizeof(short));
3244
3245 last_line = "";
3246 for (iline= 0; iline < nlines; iline++)
3247 {
3248 if (cont[iline][0] != '#')
3249 {
3250 if (strcmp(cont[iline], last_line) != 0)
3251 fprintf (file, "%s", cont[iline]);
3252 }
3253 else
3254 {
3255 ivar= find_var (&(cont[iline][1]), INI_FILE_VAR);
3256 if (ivar >= 0 && written[ivar])
3257 continue;
3258 if (ivar < 0 || our_vars.var[ivar]->saved)
3259 fprintf (file, "%s", cont[iline]);
3260 else
3261 write_var (file, ivar);
3262 if (ivar >= 0)
3263 written[ivar] = 1;
3264 }
3265 last_line = cont[iline];
3266 }
3267
3268 // now write all variables that still haven't been saved yet
3269 for (ivar= 0; ivar < our_vars.no; ivar++)
3270 {
3271 // check if we already wrote a var with the same name
3272 int check_var= find_var (our_vars.var[ivar]->name, INI_FILE_VAR);
3273 if (check_var >= 0 && written[check_var])
3274 continue;
3275 if (!our_vars.var[ivar]->saved)
3276 {
3277 fprintf (file, "\n");
3278 write_var (file, ivar);
3279 written[ivar] = 1;
3280 }
3281 }
3282 #if !defined(WINDOWS)
3283 fd = fileno (file);
3284 fstat (fd, &statbuff);
3285 /* Set perms to 600 on el_ini if they are anything else */
3286 if (statbuff.st_mode != (S_IRUSR|S_IWUSR)){
3287 fchmod (fd, S_IRUSR|S_IWUSR);
3288 }
3289 #endif // !WINDOWS
3290
3291 fclose (file);
3292 free (written);
3293 free (cont);
3294 return 1;
3295 }
3296
3297 /* ------ ELConfig Window functions start here ------ */
3298 #ifdef ELC
display_elconfig_handler(window_info * win)3299 static int display_elconfig_handler(window_info *win)
3300 {
3301 int help_y = win->len_y + 10;
3302
3303 // Draw the long description of an option
3304 draw_string_zoomed_width_font(TAB_MARGIN, elconfig_menu_y_len-LONG_DESC_SPACE,
3305 elconf_description_buffer, window_width, MAX_LONG_DESC_LINES, win->font_category,
3306 elconf_desc_size);
3307
3308 // Show the context menu help message
3309 if (is_mouse_over_option)
3310 {
3311 show_help(cm_help_options_str, 0, help_y, win->current_scale);
3312 help_y += win->small_font_len_y;
3313 is_mouse_over_option = 0;
3314 }
3315
3316 // Show current find string for multi-select widgets
3317 if (multiselect_find_show)
3318 {
3319 if (multiselect_find_str_len > 0)
3320 {
3321 float col[2][3] = {{1.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}};
3322 int ic = (multiselect_find_not_found) ?1: 0;
3323 static char text[128];
3324 safe_snprintf(text, sizeof(text), "%s%s: %s\n", multiselect_find_prompt_str,
3325 (multiselect_find_casesensitive) ?"[Aa]" :"", multiselect_find_str);
3326 draw_text(0, help_y, (const unsigned char*)text, strlen(text), UI_FONT,
3327 TDO_MAX_WIDTH, window_width - 80, TDO_HELP, 1, TDO_FOREGROUND, col[ic][0], col[ic][1], col[ic][2],
3328 TDO_ZOOM, win->current_scale * DEFAULT_SMALL_RATIO, TDO_END);
3329 }
3330 else
3331 show_help(multiselect_find_help_str, 0, help_y, win->current_scale);
3332 multiselect_find_show = 0;
3333 }
3334
3335 is_mouse_over_option_label = 0;
3336
3337 return 1;
3338 }
3339
spinbutton_onkey_handler(widget_list * widget,int mx,int my,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)3340 static int spinbutton_onkey_handler(widget_list *widget, int mx, int my, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
3341 {
3342 if(widget != NULL) {
3343 int i;
3344 spinbutton *button;
3345
3346 if (!(key_mod & KMOD_ALT) && !(key_mod & KMOD_CTRL)) {
3347 for(i= 0; i < our_vars.no; i++) {
3348 if(our_vars.var[i]->widgets.widget_id == widget->id) {
3349 button= widget->widget_info;
3350 switch(button->type) {
3351 case SPIN_FLOAT:
3352 our_vars.var[i]->func(our_vars.var[i]->var, (float *)button->data);
3353 break;
3354 case SPIN_INT:
3355 our_vars.var[i]->func(our_vars.var[i]->var, *(int *)button->data);
3356 break;
3357 }
3358 our_vars.var[i]->saved= 0;
3359 return 0;
3360 }
3361 }
3362 }
3363 }
3364 return 0;
3365 }
3366
spinbutton_onclick_handler(widget_list * widget,int mx,int my,Uint32 flags)3367 static int spinbutton_onclick_handler(widget_list *widget, int mx, int my, Uint32 flags)
3368 {
3369 if(widget != NULL) {
3370 int i;
3371 spinbutton *button;
3372
3373 for(i= 0; i < our_vars.no; i++) {
3374 if(our_vars.var[i]->widgets.widget_id == widget->id) {
3375 button= widget->widget_info;
3376 switch(button->type) {
3377 case SPIN_FLOAT:
3378 our_vars.var[i]->func(our_vars.var[i]->var, (float *)button->data);
3379 break;
3380 case SPIN_INT:
3381 our_vars.var[i]->func(our_vars.var[i]->var, *(int *)button->data);
3382 break;
3383 }
3384 our_vars.var[i]->saved= 0;
3385 return 0;
3386 }
3387 }
3388 }
3389 return 0;
3390 }
3391
multiselect_click_handler(widget_list * widget,int mx,int my,Uint32 flags)3392 static int multiselect_click_handler(widget_list *widget, int mx, int my, Uint32 flags)
3393 {
3394 int i;
3395 if(flags&ELW_LEFT_MOUSE || flags&ELW_RIGHT_MOUSE) {
3396 for(i= 0; i < our_vars.no; i++) {
3397 if(our_vars.var[i]->widgets.widget_id == widget->id) {
3398 our_vars.var[i]->func ( our_vars.var[i]->var, multiselect_get_selected(elconfig_tabs[our_vars.var[i]->widgets.tab_id].tab, our_vars.var[i]->widgets.widget_id) );
3399 our_vars.var[i]->saved= 0;
3400 return 1;
3401 }
3402 }
3403 }
3404 return 0;
3405 }
3406
multiselect_keypress_handler(widget_list * widget,int mx,int my,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)3407 static int multiselect_keypress_handler(widget_list *widget, int mx, int my, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
3408 {
3409 size_t search_offset = 0;
3410 var_struct *var = NULL;
3411 size_t i;
3412 static Uint32 last_widget_id = (Uint32)-1;
3413 static size_t last_entry = 0;
3414
3415 // exit if this widget does not have a scrollbar - no need for search
3416 if ((widget == NULL) || (multiselect_get_scrollbar_pos(widget->window_id, widget->id) == -1))
3417 return 0;
3418
3419 // reset if ESCAPE pressed
3420 if (key_code == SDLK_ESCAPE)
3421 {
3422 multiselect_find_not_found = 0;
3423 multiselect_find_str_len = 0;
3424 multiselect_find_str[multiselect_find_str_len] = '\0';
3425 last_widget_id = (Uint32)-1;
3426 last_entry = 0;
3427 return 1;
3428 }
3429
3430 // start from the first entry if we change widgets
3431 if (last_widget_id != widget->id)
3432 {
3433 last_widget_id = widget->id;
3434 last_entry = 0;
3435 }
3436
3437 // find the var with the widget id, exit if its not a multiselect
3438 for (i = 0; i < our_vars.no; i++)
3439 {
3440 if (our_vars.var[i]->widgets.widget_id == widget->id)
3441 {
3442 if (our_vars.var[i]->type != OPT_MULTI)
3443 return 0;
3444 var = our_vars.var[i];
3445 break;
3446 }
3447 }
3448
3449 // didn't find the option var so return
3450 if (var == NULL)
3451 return 0;
3452
3453 // if we press return, search for the next match after the current
3454 if ((key_code == SDLK_RETURN) || (key_code == SDLK_KP_ENTER))
3455 search_offset = 1;
3456
3457 // toggle wether we match case, the need to search for TAB is unlikely
3458 else if (key_code == SDLK_TAB)
3459 {
3460 if (multiselect_find_str_len) // only action if we are showing a string
3461 multiselect_find_casesensitive ^= 1;
3462 }
3463
3464 // if we use the key modifying the string, search again starting from current entry
3465 else if (string_input(multiselect_find_str, sizeof(multiselect_find_str), key_code, key_unicode, key_mod))
3466 multiselect_find_str_len = strlen(multiselect_find_str);
3467
3468 // else we are not using the key so return now
3469 else
3470 return 0;
3471
3472 // reset to the first entry if the string is empty then return
3473 if (multiselect_find_str_len == 0)
3474 {
3475 last_entry = 0;
3476 return 1;
3477 }
3478
3479 // find the entry matching the text, starting from the current unless return was pressed
3480 for (i = 0; i < var->args.multi.count; i++)
3481 {
3482 size_t entry = (i + last_entry + search_offset) % var->args.multi.count;
3483 if (multiselect_find_casesensitive)
3484 multiselect_find_not_found = (strstr(var->args.multi.elems[entry].label, multiselect_find_str) == NULL);
3485 else
3486 multiselect_find_not_found = (safe_strcasestr(var->args.multi.elems[entry].label,
3487 strlen(var->args.multi.elems[entry].label), multiselect_find_str, multiselect_find_str_len) == NULL);
3488 if (!multiselect_find_not_found)
3489 {
3490 multiselect_set_scrollbar_pos(widget->window_id, widget->id, entry);
3491 last_entry = entry;
3492 break;
3493 }
3494 }
3495
3496 return 1;
3497 }
3498
mouseover_option_handler(widget_list * widget,int mx,int my)3499 static int mouseover_option_handler(widget_list *widget, int mx, int my)
3500 {
3501 int i, nr_lines;
3502
3503 //Find the label in our_vars
3504 for (i = 0; i < our_vars.no; i++)
3505 {
3506 if (widget->id == our_vars.var[i]->widgets.label_id
3507 || widget->id == our_vars.var[i]->widgets.widget_id)
3508 break;
3509 }
3510 if (i == our_vars.no)
3511 //We didn't find anything, abort
3512 return 0;
3513
3514 if (!is_mouse_over_option_label && (our_vars.var[i]->type == OPT_MULTI) &&
3515 (multiselect_get_scrollbar_pos(widget->window_id, widget->id) != -1))
3516 // If we are over a OPT_MULTI with a scrollbar, show the search text
3517 multiselect_find_show = 1;
3518
3519 if (i == last_description_idx)
3520 // We're still on the same variable
3521 return 1;
3522
3523 elconf_desc_size = elconf_scale * DEFAULT_SMALL_RATIO;
3524 safe_strncpy((char*)elconf_description_buffer, (const char*)our_vars.var[i]->display.desc,
3525 sizeof(elconf_description_buffer));
3526 nr_lines = reset_soft_breaks(elconf_description_buffer, strlen((const char*)elconf_description_buffer),
3527 sizeof(elconf_description_buffer), CONFIG_FONT, elconf_desc_size,
3528 elconfig_menu_x_len - 2*TAB_MARGIN, NULL, NULL);
3529 if (nr_lines > MAX_LONG_DESC_LINES)
3530 {
3531 // Drats, the description does not fit. Try to reduce the font size, but not less than 75%
3532 // otherwise it becomes too small to read.
3533 float min_size = 0.75 * elconf_desc_size, max_size = elconf_desc_size;
3534 for (int itry = 0; itry < 3; ++itry)
3535 {
3536 float size = 0.5 * (min_size + max_size);
3537 int height;
3538 int nr_lines = reset_soft_breaks(elconf_description_buffer,
3539 strlen((const char*)elconf_description_buffer), sizeof(elconf_description_buffer),
3540 CONFIG_FONT, size, elconfig_menu_x_len - 2*TAB_MARGIN, NULL, NULL);
3541 height = get_text_height(nr_lines, CONFIG_FONT, size);
3542 if (height > elconf_desc_max_height)
3543 max_size = size;
3544 else
3545 min_size = size;
3546 }
3547 elconf_desc_size = min_size;
3548 reset_soft_breaks(elconf_description_buffer,
3549 strlen((const char*)elconf_description_buffer), sizeof(elconf_description_buffer),
3550 CONFIG_FONT, elconf_desc_size, elconfig_menu_x_len - 2*TAB_MARGIN, NULL, NULL);
3551 }
3552
3553 last_description_idx = i;
3554
3555 return 1;
3556 }
3557
mouseover_option_label_handler(widget_list * widget,int mx,int my)3558 static int mouseover_option_label_handler(widget_list *widget, int mx, int my)
3559 {
3560 is_mouse_over_option = is_mouse_over_option_label = 1;
3561 return mouseover_option_handler(widget, mx, my);
3562 }
3563
onclick_label_handler(widget_list * widget,int mx,int my,Uint32 flags)3564 static int onclick_label_handler(widget_list *widget, int mx, int my, Uint32 flags)
3565 {
3566 int i;
3567 var_struct *option= NULL;
3568
3569 if(!(flags&(ELW_LEFT_MOUSE|ELW_RIGHT_MOUSE)))
3570 return 0;
3571
3572 for(i= 0; i < our_vars.no; i++) {
3573 if(our_vars.var[i]->widgets.label_id == widget->id) {
3574 option= our_vars.var[i];
3575 break;
3576 }
3577 }
3578
3579 if (option == NULL)
3580 {
3581 // option not found, not supposed to happen
3582 return 0;
3583 }
3584
3585 if (flags&ELW_RIGHT_MOUSE)
3586 {
3587 call_option_menu(option);
3588 return 1;
3589 }
3590
3591 if (option->type == OPT_BOOL)
3592 {
3593 option->func(option->var);
3594 option->saved= 0;
3595 do_click_sound();
3596 }
3597
3598 return 1;
3599 }
3600
onclick_checkbox_handler(widget_list * widget,int mx,int my,Uint32 flags)3601 static int onclick_checkbox_handler(widget_list *widget, int mx, int my, Uint32 flags)
3602 {
3603 int i;
3604 var_struct *option= NULL;
3605
3606 for(i= 0; i < our_vars.no; i++) {
3607 if(our_vars.var[i]->widgets.widget_id == widget->id) {
3608 option= our_vars.var[i];
3609 break;
3610 }
3611 }
3612
3613 if (!option)
3614 // shouldn't happen
3615 return 0;
3616
3617 if (option->type == OPT_BOOL)
3618 {
3619 int *var= option->var;
3620 *var= !*var;
3621 option->func(var);
3622 option->saved= 0;
3623 }
3624
3625 return 1;
3626 }
3627
string_onkey_handler(widget_list * widget)3628 static int string_onkey_handler(widget_list *widget)
3629 {
3630 // dummy key handler that marks the appropriate variable as changed
3631 if(widget != NULL)
3632 {
3633 int i;
3634
3635 for(i= 0; i < our_vars.no; i++)
3636 {
3637 if(our_vars.var[i]->widgets.widget_id == widget->id)
3638 {
3639 our_vars.var[i]->saved= 0;
3640 return 0;
3641 }
3642 }
3643 }
3644
3645 return 0;
3646 }
3647
get_elconfig_content_width(void)3648 static int get_elconfig_content_width(void)
3649 {
3650 int i, iopt, line_width, max_line_width = 0;
3651 var_struct *var;
3652 int spin_button_width = max2i(ELCONFIG_SCALED_VALUE(100),
3653 4 * get_max_digit_width_zoom(CONFIG_FONT, elconf_scale) + 4 * (int)(0.5 + 5 * elconf_scale));
3654
3655 for (i = 0; i < our_vars.no; i++)
3656 {
3657 var = our_vars.var[i];
3658
3659 switch (var->type)
3660 {
3661 case OPT_BOOL_INI:
3662 case OPT_INT_INI:
3663 case OPT_PASSWORD:
3664 // not shown
3665 line_width = 0;
3666 break;
3667 case OPT_BOOL:
3668 line_width = CHECKBOX_SIZE + SPACING
3669 + get_string_width_zoom(var->display.str, CONFIG_FONT, elconf_scale);
3670 break;
3671 case OPT_INT:
3672 case OPT_FLOAT:
3673 case OPT_INT_F:
3674 case OPT_FLOAT_F:
3675 line_width = get_string_width_zoom(var->display.str, CONFIG_FONT, elconf_scale)
3676 + SPACING + spin_button_width;
3677 break;
3678 case OPT_STRING:
3679 // don't display the username, if it is changed after login, any name tagged files will be saved using the new name
3680 if (strcmp(our_vars.var[i]->name, "username") == 0)
3681 {
3682 line_width = 0;
3683 }
3684 else
3685 {
3686 line_width = get_string_width_zoom(var->display.str, CONFIG_FONT, elconf_scale)
3687 + SPACING + ELCONFIG_SCALED_VALUE(332);
3688 }
3689 break;
3690 case OPT_MULTI:
3691 line_width = get_string_width_zoom(var->display.str, CONFIG_FONT, elconf_scale)
3692 + SPACING + ELCONFIG_SCALED_VALUE(250);
3693 break;
3694 case OPT_MULTI_H:
3695 line_width = get_string_width_zoom(var->display.str, CONFIG_FONT, elconf_scale);
3696 for (iopt = 0; iopt < our_vars.var[i]->args.multi.count; ++iopt)
3697 {
3698 int radius = elconf_scale * BUTTONRADIUS;
3699 const char *label= var->args.multi.elems[iopt].label;
3700 if (!*label)
3701 label = "??";
3702
3703 line_width += SPACING + 2 * radius
3704 + get_string_width_zoom((const unsigned char*)label, CONFIG_FONT, elconf_scale);
3705 }
3706 break;
3707 default:
3708 line_width = 0;
3709 }
3710
3711 max_line_width = max2i(max_line_width, line_width);
3712 }
3713
3714 return max_line_width + 2 * TAB_MARGIN + ELCONFIG_SCALED_VALUE(ELW_BOX_SIZE);
3715 }
3716
elconfig_populate_tabs(void)3717 static void elconfig_populate_tabs(void)
3718 {
3719 int i;
3720 int label_id=-1; //temporary storage for the label id
3721 int widget_id=-1; //temporary storage for the widget id
3722 int widget_width, widget_height, label_height; //Used to calculate the y pos of the next option
3723 int x;
3724 int line_height = get_line_height(CONFIG_FONT, elconf_scale);
3725 int y_label, y_widget, dx, dy, iopt;
3726 int spin_button_width = max2i(ELCONFIG_SCALED_VALUE(100),
3727 4 * get_max_digit_width_zoom(CONFIG_FONT, elconf_scale) + 4 * (int)(0.5 + 5 * elconf_scale));
3728 int right_margin = TAB_MARGIN;
3729 int multi_height = 3 * MULTI_LINE_HEIGHT;
3730
3731 for(i= 0; i < MAX_TABS; i++) {
3732 //Set default values
3733 elconfig_tabs[i].x= TAB_MARGIN;
3734 elconfig_tabs[i].y= TAB_MARGIN;
3735 }
3736
3737 for(i= 0; i < our_vars.no; i++)
3738 {
3739 var_struct *var = our_vars.var[i];
3740 int tab_id = var->widgets.tab_id;
3741 int window_id = elconfig_tabs[tab_id].tab;
3742 int window_width = get_window_content_width(window_id);
3743 int current_x = elconfig_tabs[tab_id].x;
3744 int current_y = elconfig_tabs[tab_id].y;
3745
3746 if (elconfig_tabs[tab_id].tab < 0)
3747 continue;
3748
3749 switch(var->type)
3750 {
3751 case OPT_BOOL_INI:
3752 case OPT_STRING_INI:
3753 case OPT_INT_INI:
3754 // This variable should not be settable
3755 // through the window, so don't try to add it,
3756 // and more importantly, don't try to compute
3757 // its height
3758 continue;
3759 case OPT_BOOL:
3760 //Add checkbox
3761 dy = line_height - CHECKBOX_SIZE;
3762 y_widget = current_y + max2i(dy / 2, 0);
3763 widget_id = checkbox_add_extended(window_id, elconfig_free_widget_id++, NULL,
3764 current_x, y_widget, CHECKBOX_SIZE, CHECKBOX_SIZE, 0, elconf_scale, var->var);
3765 //Add label for the checkbox
3766 y_label = current_y - min2i(dy / 2, 0);
3767 label_id = label_add_extended(window_id, elconfig_free_widget_id++, NULL,
3768 current_x+CHECKBOX_SIZE+SPACING, y_label, 0, elconf_scale, (char*)var->display.str);
3769 //Set handlers
3770 widget_set_OnClick(window_id, widget_id, onclick_checkbox_handler);
3771 break;
3772 case OPT_INT:
3773 /* interval is always 1 */
3774 label_id = label_add_extended(window_id, elconfig_free_widget_id++, NULL,
3775 current_x, current_y, 0, elconf_scale, (char*)var->display.str);
3776 widget_width = spin_button_width;
3777 widget_id = spinbutton_add_extended(window_id, elconfig_free_widget_id++, NULL,
3778 window_width - right_margin - widget_width, current_y, widget_width, line_height,
3779 SPIN_INT, var->var, var->args.imm.min,
3780 var->args.imm.max, 1.0, elconf_scale);
3781 widget_set_OnKey(window_id, widget_id, (int (*)())spinbutton_onkey_handler);
3782 widget_set_OnClick(window_id, widget_id, spinbutton_onclick_handler);
3783 break;
3784 case OPT_FLOAT:
3785 label_id = label_add_extended(window_id, elconfig_free_widget_id++, NULL,
3786 current_x, current_y, 0, elconf_scale, (char*)var->display.str);
3787 widget_width = spin_button_width;
3788 widget_id = spinbutton_add_extended(window_id, elconfig_free_widget_id++, NULL,
3789 window_width - right_margin - widget_width, current_y, widget_width, line_height,
3790 SPIN_FLOAT, var->var, var->args.fmmi.min, var->args.fmmi.max,
3791 var->args.fmmi.interval, elconf_scale);
3792 widget_set_OnKey(window_id, widget_id, (int (*)())spinbutton_onkey_handler);
3793 widget_set_OnClick(window_id, widget_id, spinbutton_onclick_handler);
3794 break;
3795 case OPT_STRING:
3796 // don't display the username, if it is changed after login, any name tagged files will be saved using the new name
3797 if (strcmp(var->name, "username") == 0)
3798 continue;
3799 widget_width = ELCONFIG_SCALED_VALUE(332);
3800 widget_id = pword_field_add_extended(window_id, elconfig_free_widget_id++, NULL,
3801 window_width - right_margin - widget_width, current_y, widget_width, 0,
3802 P_TEXT, elconf_scale, var->var, var->len);
3803 dy = widget_get_height(window_id, widget_id) - line_height;
3804 label_id = label_add_extended(window_id, elconfig_free_widget_id++, NULL,
3805 current_x, current_y + dy/2, 0, elconf_scale, (char*)var->display.str);
3806 widget_set_OnKey (window_id, widget_id, (int (*)())string_onkey_handler);
3807 break;
3808 case OPT_PASSWORD:
3809 // Grum: the client shouldn't store the password, so let's not add it to the configuration window
3810 //label_id= label_add_extended(window_id, elconfig_free_widget_id++, NULL, current_x, current_y, 0, 0, 0, 1.0, var->display.str);
3811 //widget_id= pword_field_add_extended(window_id, elconfig_free_widget_id++, NULL, elconfig_menu_x_len/2, current_y, 200, 20, P_NORMAL, 1.0f, var->var, var->len);
3812 //widget_set_OnKey (window_id, widget_id, string_onkey_handler);
3813 continue;
3814 case OPT_MULTI:
3815 label_id = label_add_extended(window_id, elconfig_free_widget_id++, NULL,
3816 current_x, current_y, 0, elconf_scale, (char*)var->display.str);
3817 widget_width = ELCONFIG_SCALED_VALUE(250);
3818 widget_id = multiselect_add_extended(window_id, elconfig_free_widget_id++, NULL,
3819 window_width - right_margin - widget_width, current_y, widget_width,
3820 multi_height, elconf_scale, gui_color[0], gui_color[1], gui_color[2],
3821 gui_invert_color[0], gui_invert_color[1], gui_invert_color[2], 0);
3822 for (iopt = 0; iopt < var->args.multi.count; ++iopt)
3823 {
3824 const char *label = var->args.multi.elems[iopt].label;
3825 if (!*label)
3826 label = "??";
3827 multiselect_button_add_extended(window_id, widget_id,
3828 0, iopt * MULTI_LINE_HEIGHT, 0, label,
3829 DEFAULT_SMALL_RATIO*elconf_scale, iopt == *(int *)var->var);
3830 }
3831 multiselect_set_selected(window_id, widget_id, *((const int*)var->var));
3832 widget_set_OnClick(window_id, widget_id, multiselect_click_handler);
3833 widget_set_OnKey(window_id, widget_id, (int (*)())multiselect_keypress_handler);
3834 break;
3835 case OPT_FLOAT_F:
3836 label_id = label_add_extended(window_id, elconfig_free_widget_id++, NULL,
3837 current_x, current_y, 0, elconf_scale, (char*)var->display.str);
3838 widget_width = spin_button_width;
3839 widget_id = spinbutton_add_extended(window_id, elconfig_free_widget_id++, NULL,
3840 window_width - right_margin - widget_width, current_y, widget_width, line_height,
3841 SPIN_FLOAT, var->var, var->args.fmmif.min(), var->args.fmmif.max(),
3842 var->args.fmmif.interval, elconf_scale);
3843 widget_set_OnKey(window_id, widget_id, (int (*)())spinbutton_onkey_handler);
3844 widget_set_OnClick(window_id, widget_id, spinbutton_onclick_handler);
3845 break;
3846 case OPT_INT_F:
3847 /* interval is always 1 */
3848 label_id = label_add_extended(window_id, elconfig_free_widget_id++, NULL,
3849 current_x, current_y, 0, elconf_scale, (char*)var->display.str);
3850 widget_width = spin_button_width;
3851 widget_id = spinbutton_add_extended(window_id, elconfig_free_widget_id++, NULL,
3852 window_width - right_margin - widget_width, current_y, widget_width, line_height,
3853 SPIN_INT, var->var, var->args.immf.min(), var->args.immf.max(), 1.0, elconf_scale);
3854 widget_set_OnKey(window_id, widget_id, (int (*)())spinbutton_onkey_handler);
3855 widget_set_OnClick(window_id, widget_id, spinbutton_onclick_handler);
3856 break;
3857 case OPT_MULTI_H:
3858 label_id= label_add_extended(window_id, elconfig_free_widget_id++, NULL,
3859 current_x, current_y, 0, elconf_scale, (const char*)var->display.str);
3860 x = current_x + widget_get_width(window_id, label_id) + SPACING;
3861 widget_id = multiselect_add_extended(window_id, elconfig_free_widget_id++,
3862 NULL, x, current_y, ELCONFIG_SCALED_VALUE(350), multi_height,
3863 elconf_scale, gui_color[0], gui_color[1], gui_color[2], gui_invert_color[0],
3864 gui_invert_color[1], gui_invert_color[2], 0);
3865 dx = 0;
3866 for (iopt = 0; iopt < var->args.multi.count; ++iopt)
3867 {
3868 int radius = elconf_scale*BUTTONRADIUS;
3869 int width=0;
3870 const char *label= var->args.multi.elems[iopt].label;
3871 if (!*label)
3872 label = "??";
3873
3874 width = 2 * radius
3875 + get_string_width_zoom((const unsigned char*)label, CONFIG_FONT, elconf_scale);
3876
3877 multiselect_button_add_extended(window_id, widget_id, dx, 0, width, label,
3878 DEFAULT_SMALL_RATIO * elconf_scale, iopt == *(int *)var->var);
3879
3880 dx += width + SPACING;
3881 }
3882
3883 widget_width = dx - SPACING;
3884 widget_height = widget_get_height(window_id, widget_id);
3885 dy = line_height - widget_height;
3886 if (dy < 0)
3887 {
3888 widget_move(window_id, label_id, current_x, current_y - dy / 2);
3889 widget_move(window_id, widget_id, window_width - right_margin - widget_width, current_y);
3890 }
3891 else
3892 {
3893 widget_move(window_id, widget_id,
3894 window_width - right_margin - widget_width, current_y + dy / 2);
3895 }
3896
3897 multiselect_set_selected(window_id, widget_id, *((const int*)var->var));
3898 widget_set_OnClick(window_id, widget_id, multiselect_click_handler);
3899 break;
3900 }
3901
3902 //Calculate y position of the next option.
3903 label_height = widget_get_height(window_id, label_id);
3904 widget_height = widget_get_height(window_id, widget_id);
3905 elconfig_tabs[tab_id].y += max2i(widget_height, label_height) + SPACING;
3906 //Set IDs
3907 our_vars.var[i]->widgets.label_id= label_id;
3908 our_vars.var[i]->widgets.widget_id= widget_id;
3909 //Make the description print when the mouse is over a widget
3910 widget_set_OnMouseover(window_id, label_id, mouseover_option_label_handler);
3911 widget_set_OnMouseover(window_id, widget_id, mouseover_option_handler);
3912 //left click used only to tolle BOOL, right click to open context menu
3913 widget_set_OnClick(window_id, label_id, onclick_label_handler);
3914 }
3915 }
3916
3917 // TODO: replace this hack by something clean.
show_elconfig_handler(window_info * win)3918 static int show_elconfig_handler(window_info * win) {
3919 int pwinx, pwiny; window_info *pwin;
3920
3921 if (win->pos_id != -1) {
3922 pwin= &windows_list.window[win->pos_id];
3923 pwinx= pwin->cur_x;
3924 pwiny= pwin->cur_y;
3925 } else {
3926 pwinx= 0;
3927 pwiny= 0;
3928 }
3929 #ifndef MAP_EDITOR2
3930 if (get_show_window(newchar_root_win)) {
3931 init_window(win->window_id, newchar_root_win, 0, win->pos_x - pwinx, win->pos_y - pwiny, win->len_x, win->len_y);
3932 } else {
3933 init_window(win->window_id, ((not_on_top_now(MW_CONFIG) && !force_elconfig_win_ontop) ?game_root_win : -1),
3934 0, win->pos_x - pwinx, win->pos_y - pwiny, win->len_x, win->len_y);
3935 }
3936 #else
3937 init_window(win->window_id, game_root_win, 0, win->pos_x - pwinx, win->pos_y - pwiny, win->len_x, win->len_y);
3938 #endif
3939
3940 return 1;
3941 }
3942
3943 // It would be messy to resize the window each time the scale option is changed so
3944 // keep the scale at the original value until we can recreate.
ui_scale_elconfig_handler(window_info * win)3945 static int ui_scale_elconfig_handler(window_info *win)
3946 {
3947 update_window_scale(win, elconf_scale); // stop scale change impacting immediately
3948 if (get_id_MW(MW_CONFIG) >= 0)
3949 recheck_window_scale = 1;
3950 return 1;
3951 }
3952
3953 // Similar to the UI scale handler, when changing the UI font we don't want to
3954 // disrupt the options window. So defer the font change here as well until
3955 // after the window is closed.
change_elconfig_font_handler(window_info * win,font_cat cat)3956 static int change_elconfig_font_handler(window_info *win, font_cat cat)
3957 {
3958 if (cat != UI_FONT)
3959 return 0;
3960 if (get_id_MW(MW_CONFIG) >= 0)
3961 recheck_window_scale = 1;
3962 return 1;
3963 }
3964
3965 // Called from the low freqency timer as we can't initiate distorying a window in from one of its call backs.
3966 // If the scale has changed and the window is hidden, destroy it, it will be re-create with the new scale
check_for_config_window_scale(void)3967 void check_for_config_window_scale(void)
3968 {
3969 int elconfig_win = get_id_MW(MW_CONFIG);
3970 if (recheck_window_scale && (elconfig_win >= 0) && !get_show_window(elconfig_win))
3971 {
3972 size_t i;
3973 set_pos_MW(MW_CONFIG, windows_list.window[elconfig_win].cur_x, windows_list.window[elconfig_win].cur_y);
3974 for (i=MAX_TABS; i>0; i--)
3975 tab_collection_close_tab(elconfig_win, elconfig_tab_collection_id, i-1);
3976 destroy_window(elconfig_win);
3977 set_id_MW(MW_CONFIG, -1);
3978 recheck_window_scale = 0;
3979 elconf_description_buffer[0] = '\0';
3980 }
3981 }
3982
display_elconfig_win(void)3983 void display_elconfig_win(void)
3984 {
3985 int elconfig_win = get_id_MW(MW_CONFIG);
3986
3987 if(elconfig_win < 0) {
3988 int i;
3989
3990 set_config_font();
3991
3992 elconf_scale = ui_scale * elconf_custom_scale;
3993 CHECKBOX_SIZE = ELCONFIG_SCALED_VALUE(15);
3994 SPACING = ELCONFIG_SCALED_VALUE(5);
3995 elconf_desc_max_height = MAX_LONG_DESC_LINES * get_line_height(CONFIG_FONT, elconf_scale * DEFAULT_SMALL_RATIO);
3996 LONG_DESC_SPACE = SPACING + elconf_desc_max_height;
3997 TAB_TAG_HEIGHT = tab_collection_calc_tab_height(CONFIG_FONT, elconf_scale);
3998 elconfig_menu_x_len = get_elconfig_content_width() + 2 * TAB_MARGIN;
3999 elconfig_menu_y_len = ELCONFIG_SCALED_VALUE(440);
4000
4001 /* Set up the window */
4002 elconfig_win = create_window(win_configuration, (not_on_top_now(MW_CONFIG) ?game_root_win : -1), 0, get_pos_x_MW(MW_CONFIG), get_pos_y_MW(MW_CONFIG),
4003 elconfig_menu_x_len, elconfig_menu_y_len, ELW_WIN_DEFAULT|ELW_USE_UISCALE);
4004 set_id_MW(MW_CONFIG, elconfig_win);
4005 if (elconfig_win >=0 && elconfig_win < windows_list.num_windows)
4006 update_window_scale(&windows_list.window[elconfig_win], elconf_scale);
4007 check_proportional_move(MW_CONFIG);
4008 set_window_color(elconfig_win, ELW_COLOR_BORDER, gui_color[0], gui_color[1], gui_color[2], 0.0f);
4009 set_window_font_category(elconfig_win, CONFIG_FONT);
4010 set_window_handler(elconfig_win, ELW_HANDLER_DISPLAY, &display_elconfig_handler );
4011 set_window_handler(elconfig_win, ELW_HANDLER_UI_SCALE, &ui_scale_elconfig_handler );
4012 set_window_handler(elconfig_win, ELW_HANDLER_FONT_CHANGE, &change_elconfig_font_handler);
4013 // TODO: replace this hack by something clean.
4014 set_window_handler(elconfig_win, ELW_HANDLER_SHOW, &show_elconfig_handler);
4015 /* Create tabs */
4016 elconfig_tab_collection_id= tab_collection_add_extended (elconfig_win, elconfig_tab_collection_id, NULL,
4017 TAB_MARGIN, TAB_MARGIN, elconfig_menu_x_len-TAB_MARGIN*2, elconfig_menu_y_len-TAB_MARGIN*2-LONG_DESC_SPACE,
4018 0, DEFAULT_SMALL_RATIO * elconf_scale, MAX_TABS, ELW_BOX_SIZE);
4019 /* Pass ELW_SCROLLABLE as the final argument to tab_add() if you want
4020 * to put more widgets in the tab than the size of the window allows.*/
4021 elconfig_tabs[CONTROLS].tab= tab_add(elconfig_win, elconfig_tab_collection_id, ttab_controls, 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4022 elconfig_tabs[HUD].tab= tab_add(elconfig_win, elconfig_tab_collection_id, ttab_hud, 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4023 elconfig_tabs[CHAT].tab= tab_add(elconfig_win, elconfig_tab_collection_id, ttab_chat, 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4024 elconfig_tabs[FONT].tab= tab_add(elconfig_win, elconfig_tab_collection_id, ttab_font, 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4025 elconfig_tabs[SERVER].tab= tab_add(elconfig_win, elconfig_tab_collection_id, ttab_server, 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4026 elconfig_tabs[AUDIO].tab= tab_add(elconfig_win, elconfig_tab_collection_id, ttab_audio, 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4027 elconfig_tabs[VIDEO].tab= tab_add(elconfig_win, elconfig_tab_collection_id, ttab_video, 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4028 elconfig_tabs[GFX].tab= tab_add(elconfig_win, elconfig_tab_collection_id, ttab_gfx, 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4029 elconfig_tabs[CAMERA].tab= tab_add(elconfig_win, elconfig_tab_collection_id, ttab_camera, 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4030 elconfig_tabs[TROUBLESHOOT].tab= tab_add(elconfig_win, elconfig_tab_collection_id, ttab_troubleshoot, 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4031 #ifdef DEBUG
4032 elconfig_tabs[DEBUGTAB].tab= tab_add(elconfig_win, elconfig_tab_collection_id, "Debug", 0, 0, ELW_SCROLLABLE|ELW_USE_UISCALE);
4033 #endif
4034 elconfig_populate_tabs();
4035
4036 /* configure scrolling for tabs */
4037 for (i=0; i<MAX_TABS; i++)
4038 {
4039 /* configure scrolling for any tabs that exceed the window length */
4040 int window_height = widget_get_height(elconfig_win, elconfig_tab_collection_id) -TAB_TAG_HEIGHT;
4041 if (elconfig_tabs[i].tab < 0)
4042 continue;
4043 if (elconfig_tabs[i].y > window_height)
4044 {
4045 set_window_scroll_len(elconfig_tabs[i].tab, elconfig_tabs[i].y - window_height);
4046 set_window_scroll_inc(elconfig_tabs[i].tab, TAB_TAG_HEIGHT);
4047 }
4048 /* otherwise disable scrolling */
4049 else
4050 {
4051 set_window_scroll_inc(elconfig_tabs[i].tab, 0);
4052 widget_set_flags(elconfig_tabs[i].tab, windows_list.window[elconfig_tabs[i].tab].scroll_id, WIDGET_DISABLED);
4053 }
4054 }
4055 }
4056 show_window(elconfig_win);
4057 select_window(elconfig_win);
4058 }
4059
4060 #ifdef JSON_FILES
4061 // If we have logged in to a character, save any override options.
4062 //
4063 // For each ini file option, check if we have the override value set:
4064 // If we do, save the value if it is new or changed.
4065 // If we do not, remove any existing override value.
4066 //
save_character_options(void)4067 void save_character_options(void)
4068 {
4069 size_t i;
4070
4071 if (!get_use_json_user_files() || !ready_for_user_files)
4072 return;
4073
4074 for(i = 0; i < our_vars.no; i++)
4075 {
4076 var_struct *option = our_vars.var[i];
4077 if (option->character_override)
4078 {
4079 int do_save = 1;
4080 switch (option->type)
4081 {
4082 case OPT_INT:
4083 case OPT_INT_F:
4084 case OPT_MULTI:
4085 case OPT_MULTI_H:
4086 {
4087 if (json_character_options_exists(option->name))
4088 if (*((int *)option->var) == json_character_options_get_int(option->name, *((int *)option->var)))
4089 do_save = 0;
4090 if (do_save)
4091 json_character_options_set_int(option->name, *((int *)option->var));
4092 break;
4093 }
4094 case OPT_BOOL:
4095 {
4096 if (json_character_options_exists(option->name))
4097 if (*((int *)option->var) == json_character_options_get_bool(option->name, *((int *)option->var)))
4098 do_save = 0;
4099 if (do_save)
4100 json_character_options_set_bool(option->name, *((int *)option->var));
4101 break;
4102 }
4103 case OPT_FLOAT:
4104 {
4105 if (json_character_options_exists(option->name))
4106 if (*((float *)option->var) == json_character_options_get_float(option->name, *((float *)option->var)))
4107 do_save = 0;
4108 if (do_save)
4109 json_character_options_set_float(option->name, *((float *)option->var));
4110 break;
4111 }
4112 default:
4113 break;
4114 }
4115 }
4116 else
4117 {
4118 if (json_character_options_exists(option->name))
4119 json_character_options_remove(option->name);
4120 }
4121 }
4122
4123 json_character_options_save_file();
4124
4125 }
4126
4127
4128 // If we have logged in to a character, check for any override options.
4129 //
4130 // The character_options_<name>.json file can contain character specific
4131 // values for options. For each ini file option, check if we have an
4132 // override value to use. Also set the character_override flag for the var
4133 // so that we can show the option as checked when using the context menu
4134 // for the option. We do not set the unsaved state for vars that have
4135 // been overridden so that they are not saved in the ini file.
4136 //
load_character_options(void)4137 void load_character_options(void)
4138 {
4139 size_t i;
4140 char json_fname[128];
4141 static int already_loaded = 0;
4142 int have_user_video_mode = 0;
4143
4144 if (!get_use_json_user_files() || !ready_for_user_files || already_loaded)
4145 return;
4146
4147 safe_snprintf(json_fname, sizeof(json_fname), "%scharacter_options_%s.json", get_path_config(), get_lowercase_username());
4148 json_character_options_set_file_name(json_fname);
4149 json_character_options_load_file();
4150
4151 already_loaded = 1;
4152
4153 for(i = 0; i < our_vars.no; i++)
4154 {
4155 if (json_character_options_exists(our_vars.var[i]->name))
4156 {
4157 var_struct *option = our_vars.var[i];
4158 int last_save = option->saved;
4159 option->character_override = 1;
4160 switch (option->type)
4161 {
4162 case OPT_INT:
4163 case OPT_INT_F:
4164 {
4165 int new_value = json_character_options_get_int(option->name, *((int *)option->var));
4166 set_var_OPT_INT(option->name, new_value);
4167 break;
4168 }
4169 case OPT_MULTI:
4170 case OPT_MULTI_H:
4171 {
4172 int new_value = json_character_options_get_int(option->name, *((int *)option->var));
4173 option->func(option->var, new_value);
4174 // user defined video mode is a special case as we may set the mode before we have the width/height
4175 if ((strcmp(option->name, "video_mode") == 0) && (*((int *)option->var) == 0))
4176 have_user_video_mode = 1;
4177 break;
4178 }
4179 case OPT_BOOL:
4180 {
4181 int new_value = json_character_options_get_bool(option->name, *((int *)option->var));
4182 if (*(int *)option->var != new_value)
4183 {
4184 *(int *)option->var = !new_value;
4185 option->func(option->var);
4186 }
4187 break;
4188 }
4189 case OPT_FLOAT:
4190 {
4191 float new_value = json_character_options_get_float(option->name, *((float *)option->var));
4192 set_var_OPT_FLOAT(option->name, new_value);
4193 break;
4194 }
4195 default:
4196 break;
4197 }
4198 option->saved = last_save;
4199 }
4200 }
4201
4202 // if we have user defined video mode, try setting the mode again now as we will now have the width and height
4203 if (have_user_video_mode)
4204 switch_video(video_mode, full_screen);
4205 }
4206 #endif
4207
4208 #endif //ELC
4209