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