Divergent/mods/Enhanced Graphical User Int.../gamedata/scripts/ui_options.script

2733 lines
126 KiB
Plaintext

--[[
Tronex
2019/10/12
Anomlous Options Menu
Features:
- %100 customizable, support for all kind of xml elements
- Highlight pending changes
- Option description support
- Option presets support
- Reset button to clear pending changes
- Script callback on option changes
- Functors capability to excute on apply or init
- Precoditions capability to hide/show/execute functors
To get an option value inside other scripts, use: ui_options.get(parameter)
Check the options table here to see the values used
See the tutorial at the bottom for adding or modifying options table
--]]
local enable_debug = false
------------------------------------------------------------
-- Strings and LTX management
------------------------------------------------------------
local ini_pres = ini_file("presets\\includes.ltx")
local ini_loc = ini_file_ex("localization.ltx",true)
local _opt_ = "/" -- For axr_options.ltx, don't touch!
local opt_section = "options" -- For axr_options.ltx, don't touch!
local opt_str = "ui_mm_" -- Option name: "ui_mm_(option ID)" -- Option description: "ui_mm_(option ID)_desc"
local opt_str_menu = "ui_mm_menu_" -- Option menu: "ui_mm_menu_(option menu ID)"
local opt_str_prst = "ui_mm_prst_" -- Option preset: "ui_mm_prst_(option preset ID)"
local opt_str_lst = "ui_mm_lst_" -- List/Radio keys: "ui_mm_lst_(key)"
function cc(path,opt) return (path .. _opt_ .. opt) end
------------------------------------------------------------
-- Utilities
------------------------------------------------------------
local m_floor, m_ceil, s_find, s_gsub = math.floor, math.ceil, string.find, string.gsub
local clr, clamp, round_idp, str_explode = utils_xml.get_color, clamp, round_idp, str_explode
local precision = 6 -- allowed number of zeros
local width_factor = utils_xml.is_widescreen() and 0.8 or 1
local clr_o = GetARGB(255, 250, 150, 75)
local clr_g1 = GetARGB(255, 170, 170, 170)
local clr_g2 = GetARGB(255, 200, 200, 200)
local clr_w = GetARGB(255, 255, 255, 255)
local clr_tree = {
[1] = GetARGB(255, 180, 180, 180),
[2] = GetARGB(255, 180, 180, 180),
[3] = GetARGB(255, 180, 180, 180),
}
function print_dbg(...)
if enable_debug then
printf(...)
end
end
------------------------------------------------------------
-- Options
------------------------------------------------------------
options = {}
local opt_temp = {} -- stores pending changes
local opt_backup = {} -- do a backup of original changes for comparison with pendings
local opt_index = {} -- option index by path, so we can locate an option fast without iterating throw the whole options table
local opt_val = {} -- option value type by path, to execute proper functions on different type without iterating throw the whole options table
local allowed_type = { -- ignore tables from these types in temp tables
["check"] = true,
["list"] = true,
["input"] = true,
["radio_h"] = true,
["radio_v"] = true,
["track"] = true,
}
function init_opt_base()
options = {
{ id= "video" ,gr={
{ id= "basic" ,sh=true ,gr={
{ id= "slide_vid" ,type= "slide" ,link= "ui_options_slider_video" ,text= "ui_mm_title_video_basic" ,size= {512,50} },
{ id= "renderer" ,type= "list" ,val= 0 ,cmd= "renderer" ,curr= {curr_renderer} ,content= {cont_renderer} ,restart= true ,vid= true },
-- { id= "preset" ,type= "list" ,val= 0 ,cmd= "_preset" ,content= {{"Minimum","pres_minimum"} , {"Low","pres_low"} , {"Default","pres_default"} , {"High","pres_high"} , {"Extreme","pres_extreme"}} ,restart= true},
{ id= "resolution" ,type= "list" ,val= 0 ,cmd= "vid_mode" ,content= {cont_vid_mode} ,no_str= true ,restart= true ,vid= true },
{ id= "gamma" ,type= "track" ,val= 2 ,cmd= "rs_c_gamma" ,min= 0.5 ,max= 1.5 ,step= 0.1 ,precondition= {for_renderer,"renderer_r1"} },
{ id= "contrast" ,type= "track" ,val= 2 ,cmd= "rs_c_contrast" ,min= 0.5 ,max= 1.5 ,step= 0.1 ,precondition= {for_renderer,"renderer_r1"} },
{ id= "brightness" ,type= "track" ,val= 2 ,cmd= "rs_c_brightness" ,min= 0.5 ,max= 1.5 ,step= 0.1 ,precondition= {for_renderer,"renderer_r1"} },
{ id= "fov" ,type= "track" ,val= 2 ,cmd= "fov" ,min= 5 ,max= 180 ,step= 1 },
{ id= "hud_fov" ,type= "track" ,val= 2 ,cmd= "hud_fov" ,min= 0.1 ,max= 1 ,step= 0.01 },
{ id= "screen_mode" ,type= "radio_h" ,val= 2 ,curr= {curr_screen_mode} ,content= {{1,"fullscreen"} , {2,"borderless"} , {3,"windowed"}} ,functor = {func_screen_mode} },
{ id= "lighting" ,type= "button" ,functor_ui= {start_lighting_ui} ,precondition= {level_present} ,precondition_1= {for_renderer,"renderer_r2a","renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"} },
},},
{ id= "advanced" ,presets= {"video_extreme","video_high","video_default","video_low","video_minimum"} ,sh=true ,gr={
{ id= "slide_vid_adv" ,type= "slide" ,link= "ui_options_slider_video_advanced" ,text= "ui_mm_title_video_advanced" ,size= {512,50} },
{ id= "ai_torch" ,type= "check" ,val= 1 ,cmd= "ai_use_torch_dynamic_lights" },
{ id= "v_sync" ,type= "check" ,val= 1 ,cmd= "rs_v_sync" },
{ id= "framelimit" ,type= "track" ,val= 2 ,cmd= "r__framelimit" ,min= 0 ,max= 500 ,step= 2 },
{ id= "line" ,type= "line" },
{ id= "title" ,type= "title" ,text= "ui_mm_header_rendering_dist" ,align= "l" ,clr= {255,200,200,200} },
{ id= "vis_distance" ,type= "track" ,val= 2 ,cmd= "rs_vis_distance" ,min= 0.4 ,max= 1.5 ,step= 0.1 ,no_str= true },
{ id= "optimize_static_geom" ,type= "track" ,val= 2 ,cmd= "r__optimize_static_geom" ,min= 0 ,max= 4 ,step= 1 ,no_str= true ,invert= true },
{ id= "optimize_dynamic_geom" ,type= "track" ,val= 2 ,cmd= "r__optimize_dynamic_geom" ,min= 0 ,max= 4 ,step= 1 ,no_str= true ,invert= true },
{ id= "optimize_shadow_geom" ,type= "check" ,val= 1 ,cmd= "r__optimize_shadow_geom" },
{ id= "line" ,type= "line" },
{ id= "title" ,type= "title" ,text= "ui_mm_header_rendering_quality" ,align= "l" ,clr= {255,200,200,200} },
{ id= "texture_lod" ,type= "track" ,val= 2 ,cmd= "texture_lod" ,min= 0 ,max= 4 ,step= 1 ,no_str= true ,invert= true ,vid= true ,restart= true },
{ id= "geometry_lod" ,type= "track" ,val= 2 ,cmd= "r__geometry_lod" ,min= 0.1 ,max= 1.5 ,step= 0.1 },
{ id= "mipbias" ,type= "track" ,val= 2 ,cmd= "r__tf_mipbias" ,min= -0.5 ,max= 0.5 ,step= 0.1 ,no_str= true ,invert= true },
{ id= "tf_aniso" ,type= "list" ,val= 0 ,cmd= "r__tf_aniso" ,content={ {"0","0"},{"4","4"},{"8","8"},{"16","16"} } ,vid= true, no_str= true },
{ id= "ssample" ,type= "track" ,val= 2 ,cmd= "r__supersample" ,min= 1 ,max= 8 ,step= 1 ,vid= true ,precondition= {for_renderer,"renderer_r1","renderer_r2a","renderer_r2","renderer_r2.5"} },
{ id= "ssample_list" ,type= "list" ,val= 0 ,cmd= "r3_msaa" ,content={ {"st_opt_off","st_opt_off"},{"2x","x2"},{"4x","x4"},{"8x","x8"} } ,vid= true ,precondition= {for_renderer,"renderer_r3","renderer_r4"} , no_str= true },
{ id= "smaa" ,type= "list" ,val= 0 ,cmd= "r2_smaa" ,content={ {"off","st_opt_off"},{"low","st_opt_low"},{"medium","st_opt_medium"},{"high","st_opt_high"},{"ultra","st_opt_ultra"} } ,precondition= {for_renderer,"renderer_r2a","renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"}, no_str= true },
{ id= "detail_textures" ,type= "check" ,val= 1 ,cmd= "r1_detail_textures" ,vid= true ,precondition= {for_renderer,"renderer_r1"} },
{ id= "detail_bump" ,type= "check" ,val= 1 ,cmd= "r2_detail_bump" ,vid= true ,precondition= {for_renderer,"renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "steep_parallax" ,type= "check" ,val= 1 ,cmd= "r2_steep_parallax" ,vid= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "enable_tessellation" ,type= "check" ,val= 1 ,cmd= "r4_enable_tessellation" ,vid= true ,precondition= {for_renderer,"renderer_r4"} },
{ id= "line" ,type= "line" },
{ id= "title" ,type= "title" ,text= "ui_mm_header_grass" ,align= "l" ,clr= {255,200,200,200} },
{ id= "detail_density" ,type= "track" ,val= 2 ,cmd= "r__detail_density" ,min= 0.04 ,max= 1 ,step= 0.02 ,prec = 2 ,invert= true ,vid= true ,restart= true ,no_str= true },
{ id= "detail_radius" ,type= "track" ,val= 2 ,cmd= "r__detail_radius" ,min= 50 ,max= 250 ,step= 20 ,restart= true ,vid= true },
{ id= "detail_height" ,type= "track" ,val= 2 ,cmd= "r__detail_height" ,min= 0.5 ,max= 2 ,step= 0.1 ,restart= true ,vid= true },
{ id= "grass_shadow" ,type= "check" ,val= 1 ,cmd= "r__enable_grass_shadow" ,vid= true ,precondition= {for_renderer,"renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "line" ,type= "line" ,precondition= {for_renderer,"renderer_r2a","renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "title" ,type= "title" ,text= "ui_mm_header_lighting" ,align= "l" ,clr= {255,200,200,200} ,precondition= {for_renderer,"renderer_r2a","renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"}},
{ id= "slight_fade" ,type= "track" ,val= 2 ,cmd= "r2_slight_fade" ,min= 0.2 ,max= 1 ,step= 0.1 ,vid= true ,precondition= {for_renderer,"renderer_r2a","renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "ls_squality" ,type= "track" ,val= 2 ,cmd= "r2_ls_squality" ,min= 0.5 ,max= 1 ,step= 0.5 ,vid= true ,precondition= {for_renderer,"renderer_r2a","renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "actor_shadow" ,type= "check" ,val= 1 ,cmd= "r__actor_shadow" ,precondition= {for_renderer,"renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "gloss_factor" ,type= "track" ,val= 2 ,cmd= "r2_gloss_factor" ,min= 0 ,max= 10 ,step= 0.5 ,precondition= {for_renderer,"renderer_r2a","renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "sun" ,type= "check" ,val= 1 ,cmd= "r2_sun" ,vid= true ,precondition= {for_renderer,"renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "sun_quality" ,type= "list" ,val= 0 ,cmd= "r2_sun_quality" ,content={cont_sun_quality} ,vid= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
-- { id= "sun_details" ,type= "check" ,val= 1 ,cmd= "r2_sun_details" ,vid= true ,precondition= {for_renderer,"renderer_r2","renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "sunshafts_mode" ,type= "radio_v" ,val= 0 ,cmd= "r2_sunshafts_mode" ,content={ {"off"},{"volumetric"},{"screen_space"},{"combined"} } ,vid= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "sunshafts_quality" ,type= "list" ,val= 0 ,cmd= "r2_sunshafts_quality" ,content={ {"st_opt_low","low"},{"st_opt_medium","medium"},{"st_opt_high","high"} } ,vid= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "sunshafts_value" ,type= "track" ,val= 2 ,cmd= "r2_sunshafts_value" ,min= 0.5 ,max= 2 ,step= 0.1 ,vid= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "sunshafts_min" ,type= "track" ,val= 2 ,cmd= "r2_sunshafts_min" ,min= 0 ,max= 0.5 ,step= 0.05 ,vid= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "ssao_mode" ,type= "radio_v" ,val= 0 ,cmd= "r2_ssao_mode" ,content={ {"disabled","st_opt_off"},{"default","SSDO"},{"hbao","HBAO"},{"hdao","HDAO"}} ,vid= true , no_str = true ,restart= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "ssao" ,type= "list" ,val= 0 ,cmd= "r2_ssao" ,content={ {"st_opt_off","off"},{"st_opt_low","low"},{"st_opt_medium","medium"},{"st_opt_high","high"} } ,vid= true ,restart= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "volumetric_lights" ,type= "check" ,val= 1 ,cmd= "r2_volumetric_lights" ,vid= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "line" ,type= "line" ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"}},
{ id= "title" ,type= "title" ,text= "ui_mm_header_effects" ,align= "l" ,clr= {255,200,200,200} ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"}},
{ id= "soft_water" ,type= "check" ,val= 1 ,cmd= "r2_soft_water" ,vid= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "soft_particles" ,type= "check" ,val= 1 ,cmd= "r2_soft_particles" ,vid= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "dof_enable" ,type= "check" ,val= 1 ,cmd= "r2_dof_enable" ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "mblur_enable" ,type= "check" ,val= 1 ,cmd= "r2_mblur_enabled" ,vid= true ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "mblur" ,type= "track" ,val= 2 ,def= 0.4 ,cmd= "r2_mblur" ,min= 0 ,max= 1 ,step= 0.05 ,precondition= {for_renderer,"renderer_r2.5","renderer_r3","renderer_r4"} },
{ id= "dynamic_wet_surfaces" ,type= "check" ,val= 1 ,cmd= "r3_dynamic_wet_surfaces" ,vid= true ,precondition= {for_renderer,"renderer_r3","renderer_r4"} },
{ id= "volumetric_smoke" ,type= "check" ,val= 1 ,cmd= "r3_volumetric_smoke" ,vid= true ,precondition= {for_renderer,"renderer_r3","renderer_r4"} },
-- { id= "msaa_alphatest" ,type= "list" ,val= 0 ,cmd= "r3_msaa_alphatest" ,content={ {"st_opt_off","off"},{"st_atest_msaa_dx10_0","atest_msaa_dx10_0"},{"st_atest_msaa_dx10_1","atest_msaa_dx10_1"} } ,vid= true ,precondition= {for_renderer,"renderer_r3","renderer_r4"} },
-- { id= "msaa_opt" ,type= "check" ,val= 1 ,cmd= "r3_msaa_opt" ,vid= true ,precondition= {for_renderer,"renderer_r3","renderer_r4"} },
},},
{ id= "hud" ,sh=true ,gr={
{ id= "slide_hud" ,type= "slide" ,link= "ui_options_slider_hud" ,text= "ui_mm_title_hud" ,size= {512,50} ,spacing= 20 },
{ id= "show_crosshair" ,type= "check" ,val= 1 ,def= false ,cmd= "hud_crosshair" },
{ id= "crosshair_dist" ,type= "check" ,val= 1 ,def= false ,cmd= "hud_crosshair_dist" },
{ id= "crosshair_clr_a" ,type= "track" ,val= 2 ,min= 0 ,max= 255 ,step= 1 ,curr= {curr_crosshair_clr,"a"} ,functor= {func_crosshair_clr,"a"} },
{ id= "crosshair_clr_r" ,type= "track" ,val= 2 ,min= 0 ,max= 255 ,step= 1 ,curr= {curr_crosshair_clr,"r"} ,functor= {func_crosshair_clr,"r"} },
{ id= "crosshair_clr_g" ,type= "track" ,val= 2 ,min= 0 ,max= 255 ,step= 1 ,curr= {curr_crosshair_clr,"g"} ,functor= {func_crosshair_clr,"g"} },
{ id= "crosshair_clr_b" ,type= "track" ,val= 2 ,min= 0 ,max= 255 ,step= 1 ,curr= {curr_crosshair_clr,"b"} ,functor= {func_crosshair_clr,"b"} },
{ id= "line" ,type= "line" },
{ id= "show_hud" ,type= "check" ,val= 1 ,def= true ,cmd= "hud_draw" },
-- { id= "dynamic_crosshair" ,type= "check" ,val= 1 ,def= false ,cmd= "cl_dynamiccrosshair" },
{ id= "show_slots" ,type= "check" ,val= 1 ,def= true ,functor= {func_slot_hud} },
{ id= "show_wpn" ,type= "check" ,val= 1 ,def= true ,cmd= "hud_weapon" },
{ id= "show_tracers" ,type= "check" ,val= 1 ,def= true ,cmd= "g_use_tracers" },
{ id= "show_minimap" ,type= "check" ,val= 1 ,def= false ,functor= {func_hud_minimap} },
{ id= "show_enemy_health" ,type= "check" ,val= 1 ,def= false },
{ id= "show_identity" ,type= "check" ,val= 1 ,def= false ,cmd= "hud_info" },
{ id= "autohide_stamina_bar" ,type= "check" ,val= 1 ,def= true ,functor= {func_hud_autohide_bar} },
{ id= "3d_pda" ,type= "check" ,val= 1 ,def= true ,cmd= "g_3d_pda" },
{ id= "ironsights_zoom_factor" ,type= "track" ,val= 2 ,min= 1 ,max= 2 ,step= 0.1 ,cmd= "g_ironsights_zoom_factor" },
{ id= "head_bob_factor" ,type= "track" ,val= 2 ,min= 0 ,max= 2 ,step= 0.1 ,cmd= "head_bob_factor" },
},},
{ id= "player" ,sh=true ,gr={
{ id= "slide_player" ,type= "slide" ,link= "ui_options_slider_player" ,text= "ui_mm_title_effects" ,size= {512,50} },
{ id= "animations" ,type= "check" ,val= 1 ,def= true },
{ id= "item_swap_animation" ,type= "check" ,val= 1 ,def= true },
{ id= "shoot_effects" ,type= "check" ,val= 1 ,def= false },
{ id= "radiation_effect" ,type= "check" ,val= 1 ,def= true },
{ id= "blood_splash" ,type= "check" ,val= 1 ,def= false },
{ id= "bleed_effect" ,type= "check" ,val= 1 ,def= true },
--{ id="tiny_pfx" ,type= "check" ,val= 1 ,def= true },
{ id= "slide_mask" ,type= "slide" ,link= "ui_options_slider_mask" ,text= "ui_mm_title_mask" ,size= {512,50} ,spacing= 20 },
{ id= "mask_hud" ,type= "check" ,val= 1 ,def= true },
{ id= "breathing_fog" ,type= "check" ,val= 1 ,def= true },
{ id= "rain_droplets" ,type= "check" ,val= 1 ,def= true },
{ id= "visor_reflection" ,type= "check" ,val= 1 ,def= true },
},},
{ id= "weather" ,sh=true ,gr={
{ id= "clear_slide" ,type= "slide" ,link= "ui_options_slider_weather_clear" ,text= "st_wthr_clear" ,size= {512,50} },
{ id= "clear_period" ,type= "radio_h" ,val= 2 ,def= random_choice(4,6) ,content={ {3,"3h"},{4,"4h"},{5,"5h"},{6,"6h"},{7,"7h"},{8,"8h"},{9,"9h"},{10,"10h"},{11,"11h"},{12,"12h"} } ,hint= "video_weather_period" ,force_horz = true },
{ id= "clear_occurrence" ,type= "radio_v" ,val= 2 ,def= random_choice(2,3) ,content={ {1,"none"},{2,"rare"},{3,"regular"} } ,hint= "video_weather_occurrence" },
{ id= "partly_slide" ,type= "slide" ,link= "ui_options_slider_weather_partly" ,text= "st_wthr_partly" ,size= {512,50} },
{ id= "partly_period" ,type= "radio_h" ,val= 2 ,def= random_choice(4,6) ,content={ {3,"3h"},{4,"4h"},{5,"5h"},{6,"6h"},{7,"7h"},{8,"8h"},{9,"9h"},{10,"10h"},{11,"11h"},{12,"12h"} } ,hint= "video_weather_period" ,force_horz = true },
{ id= "partly_occurrence" ,type= "radio_v" ,val= 2 ,def= random_choice(2,3) ,content={ {1,"none"},{2,"rare"},{3,"regular"} } ,hint= "video_weather_occurrence" },
{ id= "cloudy_slide" ,type= "slide" ,link= "ui_options_slider_weather_cloudy" ,text= "st_wthr_cloudy" ,size= {512,50} },
{ id= "cloudy_period" ,type= "radio_h" ,val= 2 ,def= random_choice(4,6) ,content={ {3,"3h"},{4,"4h"},{5,"5h"},{6,"6h"},{7,"7h"},{8,"8h"},{9,"9h"},{10,"10h"},{11,"11h"},{12,"12h"} } ,hint= "video_weather_period" ,force_horz = true },
{ id= "cloudy_occurrence" ,type= "radio_v" ,val= 2 ,def= random_choice(2,3) ,content={ {1,"none"},{2,"rare"},{3,"regular"} } ,hint= "video_weather_occurrence" },
{ id= "rain_slide" ,type= "slide" ,link= "ui_options_slider_weather_rain" ,text= "st_wthr_rain" ,size= {512,50} },
{ id= "rain_period" ,type= "radio_h" ,val= 2 ,def= random_choice(4,6) ,content={ {3,"3h"},{4,"4h"},{5,"5h"},{6,"6h"},{7,"7h"},{8,"8h"},{9,"9h"},{10,"10h"},{11,"11h"},{12,"12h"} } ,hint= "video_weather_period" ,force_horz = true },
{ id= "rain_occurrence" ,type= "radio_v" ,val= 2 ,def= random_choice(2,3) ,content={ {1,"none"},{2,"rare"},{3,"regular"} } ,hint= "video_weather_occurrence" },
{ id= "storm_slide" ,type= "slide" ,link= "ui_options_slider_weather_storm" ,text= "st_wthr_storm" ,size= {512,50} },
{ id= "storm_period" ,type= "radio_h" ,val= 2 ,def= random_choice(4,6) ,content={ {3,"3h"},{4,"4h"},{5,"5h"},{6,"6h"},{7,"7h"},{8,"8h"},{9,"9h"},{10,"10h"},{11,"11h"},{12,"12h"} } ,hint= "video_weather_period" ,force_horz = true },
{ id= "storm_occurrence" ,type= "radio_v" ,val= 2 ,def= random_choice(2,3) ,content={ {1,"none"},{2,"rare"},{3,"regular"} } ,hint= "video_weather_occurrence" },
{ id= "foggy_slide" ,type= "slide" ,link= "ui_options_slider_weather_foggy" ,text= "st_wthr_foggy" ,size= {512,50} },
{ id= "foggy_period" ,type= "radio_h" ,val= 2 ,def= random_choice(4,6) ,content={ {3,"3h"},{4,"4h"},{5,"5h"},{6,"6h"},{7,"7h"},{8,"8h"},{9,"9h"},{10,"10h"},{11,"11h"},{12,"12h"} } ,hint= "video_weather_period" ,force_horz = true },
{ id= "foggy_occurrence" ,type= "radio_v" ,val= 2 ,def= random_choice(2,3) ,content={ {1,"none"},{2,"rare"},{3,"regular"} } ,hint= "video_weather_occurrence" },
},},
{ id= "night" ,sh=true ,gr={
{ id= "slide_night" ,type= "slide" ,link= "ui_options_slider_night" ,text= "ui_mm_title_night" ,size= {512,50} },
{ id= "brightness" ,type= "list" ,val= 0 ,def= "slight" ,content= {{"dark","dark_night"} , {"slight","slight_night"} , {"medium","medium_night"} , {"bright","bright_night"}} },
{ id= "moon_cycle" ,type= "radio_h" ,val= 2 ,def= 8 ,content= {{8,"8days"} , {28,"28days"}} },
{ id= "moon_phase_state" ,type= "check" ,val= 1 ,def= false },
{ id= "moon_phase" ,type= "radio_v" ,val= 2 ,def= 8 ,content= {{0,"moon_phase_0"} , {1,"moon_phase_1"} , {2,"moon_phase_2"} , {3,"moon_phase_3"} , {4,"moon_phase_4"} , {5,"moon_phase_5"} , {6,"moon_phase_6"} , {7,"moon_phase_7"}} },
},},
},},
{ id= "sound" ,gr={
{ id= "general" ,sh=true ,gr={
{ id= "slide_sound" ,type= "slide" ,link= "ui_options_slider_sound" ,text= "ui_mm_title_sound" ,size= {512,50} },
{ id= "master_volume" ,type= "track" ,val= 2 ,cmd= "snd_volume_eff" ,min= 0 ,max= 1 ,step= 0.1 },
{ id= "music_volume" ,type= "track" ,val= 2 ,cmd= "snd_volume_music" ,min= 0 ,max= 1 ,step= 0.1 },
{ id= "sound_device" ,type= "list" ,val= 0 ,cmd= "snd_device" ,no_str= true ,restart= true },
{ id= "eax" ,type= "check" ,val= 1 ,cmd= "snd_efx" },
{ id= "dynamic_music" ,type= "check" ,val= 1 ,cmd= "g_dynamic_music" },
{ id= "caption" ,type= "radio_v" ,val= 0 ,def= "none" ,content={ {"none"},{"storyonly"},{"all"} } },
},},
{ id= "environment" ,sh=true ,gr={
{ id= "slide_sound" ,type= "slide" ,link= "ui_options_slider_sound_environment" ,text= "ui_mm_title_sound_environment" ,size= {512,50} },
{ id= "ambient_volume" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 1 ,step= 0.1 },
{ id= "wind_sound" ,type= "check" ,val= 1 ,def= true },
{ id= "breathing_sound" ,type= "check" ,val= 1 ,def= true },
{ id= "helmet_rain_sound" ,type= "check" ,val= 1 ,def= true },
},},
{ id= "radio" ,sh=true ,gr={
{ id= "slide_radio" ,type= "slide" ,link= "ui_options_slider_radio" ,text= "ui_mm_title_sound_radio" ,size= {512,50} },
{ id= "zone" ,type= "check" ,val= 1 ,def= true },
{ id= "emission_intereferences" ,type= "check" ,val= 1 ,def= true },
{ id= "underground_intereferences" ,type= "check" ,val= 1 ,def= true },
{ id= "display_tracks" ,type= "check" ,val= 1 ,def= false },
-- playlist names
},},
},},
{ id= "control" ,gr={
{ id= "general" ,sh=true ,gr={
{ id= "slide_control" ,type= "slide" ,link= "ui_options_slider_control" ,text= "ui_mm_title_control" ,size= {512,50} },
{ id= "mouse_sens" ,type= "track" ,val= 2 ,cmd= "mouse_sens" ,min= 0.001 ,max= 0.6 ,step= 0.01 },
{ id= "mouse_sens_aim" ,type= "track" ,val= 2 ,cmd= "mouse_sens_aim" ,min= 0.5 ,max= 2 ,step= 0.05, def= 1 },
{ id= "mouse_invert" ,type= "check" ,val= 1 ,cmd= "mouse_invert" ,def= false },
{ id= "crouch_toggle" ,type= "check" ,val= 1 ,cmd= "g_crouch_toggle" ,def= false },
{ id= "walk_toggle" ,type= "check" ,val= 1 ,cmd= "g_walk_toggle" ,def= false },
{ id= "sprint_toggle" ,type= "check" ,val= 1 ,cmd= "g_sprint_toggle" ,def= true },
{ id= "lookout_toggle" ,type= "check" ,val= 1 ,cmd= "g_lookout_toggle" ,def= false },
{ id= "aim_toggle" ,type= "check" ,val= 1 ,cmd= "wpn_aim_toggle" ,def= false ,bool_to_num= true },
{ id= "pickup_mode" ,type= "check" ,val= 1 ,cmd= "g_multi_item_pickup" ,def= true },
{ id= "simple_pda_mode" ,type= "check" ,val= 1 ,cmd= "g_simple_pda" ,def= true },
{ id= "disassembly_warning" ,type= "check" ,val= 1 ,def= true },
},},
{ id= "keybind" ,sh=true ,gr={},}, -- ENGINE!
},},
{ id= "gameplay" ,gr={
{ id= "general" ,sh=true ,gr={
{ id= "slide_player" ,type= "slide" ,link= "ui_options_slider_player" ,text= "ui_mm_title_gameplay" ,size= {512,50} },
{ id= "player_name" ,type= "input" ,val= 0 ,curr= {curr_player_name} ,functor= {func_player_name} ,precondition= {level_present} },
{ id= "outfit_portrait" ,type= "check" ,val= 1 ,def= true },
{ id= "hardcore_ai_aim" ,type= "check" ,val= 1 ,def= false ,functor= {func_hardcore_ai_aim} },
{ id= "show_tip_reputation" ,type= "check" ,val= 1 ,def= true },
{ id= "mechanic_feature" ,type= "check" ,val= 1 ,def= false },
{ id= "line" ,type= "line" },
{ id= "release_dropped_items" ,type= "check" ,val= 1 ,def= true },
{ id= "corpse_max_count" ,type= "track" ,val= 2 ,def= 5 ,min= 5 ,max= 30 ,step= 1 },
{ id= "corpse_min_dist" ,type= "track" ,val= 2 ,def= 75 ,min= 75 ,max= 200 ,step= 5 },
{ id= "line" ,type= "line" },
{ id= "npc_loot_distance" ,type= "track" ,val= 2 ,def= 5 ,min= 0 ,max= 25 ,step= 1 },
{ id= "line" ,type= "line" },
{ id= "max_tasks" ,type= "track" ,val= 2 , def= 2 ,min= 1 ,max= 10 ,step= 1 },
{ id= "line" ,type= "line" },
{ id= "need_equipped_hkit" ,type= "check" ,val= 1 ,def= true },
},},
{ id= "silent_kills" ,sh=true ,gr={
{ id= "sk_desc" ,type= "desc" ,text= "st_sk_desc" ,clr= {255,125,175,200} },
{ id= "sk_enabled" ,type= "check" ,val= 1 ,def= false},
{ id= "sk_melee_enabled" ,type= "check" ,val= 1 ,def= true},
{ id= "sk_all_melee_ok" ,type= "check" ,val= 1 ,def= false},
{ id= "sk_gun_enabled" ,type= "check" ,val= 1 ,def= true},
{ id= "sk_headshot_only" ,type= "check" ,val= 1 ,def= true},
{ id= "sk_fresh_time" ,type= "input" ,val= 0 ,def= 300},
{ id= "sk_suspect_dist" ,type= "input" ,val= 0 ,def= 10},
{ id= "sk_melee_hear_dist" ,type= "input" ,val= 0 ,def= 5},
{ id= "sk_gun_hear_dist" ,type= "input" ,val= 0 ,def= 10},
},},
{ id= "economy_diff" ,sh=true ,precondition = {level_present} ,output = "ui_mm_warning_economy_diff_not_level_present" ,presets= {"economy_easy","economy_medium","economy_hard"} ,gr={
{ id= "slide_economy_diff" ,type= "slide" ,link= "ui_options_slider_economy_diff" ,text= "ui_mm_title_economy_diff" ,size= {512,50} },
{ id= "desc_ingame_only" ,type= "desc" ,text= "ui_mm_desc_economy_diff" ,clr= {255,200,75,75} },
{ id= "condition_buy_override" ,type= "track" ,val= 2 , curr = {trader_cond,'get'}, functor= {trader_cond,"set"} ,min= 0 ,max= 100 ,step= 1 },
{ id= "goodwill" ,type= "track" ,val= 2 ,curr= {curr_economy,"goodwill"} ,functor= {func_economy_diff,"goodwill"} ,min= 0.5 ,max= 3 ,step= 0.1 },
{ id= "rewards" ,type= "track" ,val= 2 ,curr= {curr_economy,"rewards"} ,functor= {func_economy_diff,"rewards"} ,min= 0.5 ,max= 3 ,step= 0.1 },
{ id= "repair" ,type= "track" ,val= 2 ,curr= {curr_economy,"repair"} ,functor= {func_economy_diff,"repair"} ,min= 1 ,max= 2 ,step= 0.1 },
{ id= "upgrade" ,type= "track" ,val= 2 ,curr= {curr_economy,"upgrade"} ,functor= {func_economy_diff,"upgrade"} ,min= 1 ,max= 3 ,step= 0.1 },
{ id= "buy" ,type= "track" ,val= 2 ,curr= {curr_economy,"buy"} ,functor= {func_economy_diff,"buy"} ,min= 0.5 ,max= 2 ,step= 0.1 },
{ id= "sell" ,type= "track" ,val= 2 ,curr= {curr_economy,"sell"} ,functor= {func_economy_diff,"sell"} ,min= 0.5 ,max= 2 ,step= 0.1 },
{ id= "loots" ,type= "track" ,val= 2 ,curr= {curr_economy,"loots"} ,functor= {func_economy_diff,"loots"} ,min= 0.5 ,max= 3 ,step= 0.1 },
{ id= "money_loots" ,type= "track" ,val= 2 ,curr= {curr_economy,"money_loots"} ,functor= {func_economy_diff,"money_loots"} ,min= 0.5 ,max= 3 ,step= 0.1 },
{ id= "stash_chance" ,type= "track" ,val= 2 ,curr= {curr_economy,"stash_chance"} ,functor= {func_economy_diff,"stash_chance"} ,min= 0.1 ,max= 1 ,step= 0.05 },
{ id= "weapon_degradation" ,type= "track" ,val= 2 ,curr= {curr_economy,"weapon_degradation"} ,functor= {func_economy_diff,"weapon_degradation"} ,min= 0.5 ,max= 1 ,step= 0.05 },
{ id= "battery_consumption" ,type= "track" ,val= 2 ,curr= {curr_economy,"battery_consumption"} ,functor= {func_economy_diff,"battery_consumption"} ,min= 0.5 ,max= 3 ,step= 0.1 },
{ id= "restock" ,type= "track" ,val= 2 ,curr= {curr_economy,"restock"} ,functor= {func_economy_diff,"restock"} ,min= 12 ,max= 168 ,step= 1 },
{ id= "arty_chance" ,type= "track" ,val= 2 ,curr= {curr_economy,"arty_chance"} ,functor= {func_economy_diff,"arty_chance"} ,min= 0.1 ,max= 1 ,step= 0.05 },
{ id= "percentage_parts" ,type= "check" ,val= 1 ,curr= {curr_economy,"percentage_parts"} ,functor= {func_economy_diff,"percentage_parts"} },
{ id= "limited_bolts" ,type= "check" ,val= 1 ,curr= {curr_economy,"limited_bolts"} ,functor= {func_economy_diff,"limited_bolts"} },
{ id= "outfit_drops" ,type= "list" ,val= 2 ,curr= {curr_economy,"outfit_drops"} ,functor= {func_economy_diff,"outfit_drops"} ,content= {{1,"off"} , {2,"progressive"} , {3,"full"}} },
},},
{ id= "gameplay_diff" ,sh=true ,precondition = {level_present} ,output = "ui_mm_warning_gameplay_diff_not_level_present" ,presets= {"gameplay_easy","gameplay_medium","gameplay_hard"} ,gr={
{ id= "slide_gameplay_diff" ,type= "slide" ,link= "ui_options_slider_gameplay_diff" ,text= "ui_mm_title_gameplay_diff" ,size= {512,50} },
{ id= "desc_ingame_only" ,type= "desc" ,text= "ui_mm_desc_gameplay_diff" ,clr= {255,200,75,75} },
{ id= "actor_immunities" ,type= "list" ,val= 2 ,curr= {curr_gameplay,"actor_immunities"} ,functor= {func_gameplay_diff,"actor_immunities"} ,content= {{1,"great"} , {2,"good"} , {3,"average"} , {4,"poor"}} },
{ id= "hit_power" ,type= "track" ,val= 2 ,curr= {curr_gameplay,"hit_power"} ,functor= {func_gameplay_diff,"hit_power"} ,min= 0.5 ,max= 3 ,step= 0.1 },
{ id= "dispersion_base" ,type= "track" ,val= 2 ,curr= {curr_gameplay,"dispersion_base"} ,functor= {func_gameplay_diff,"dispersion_base"} ,min= 1 ,max= 3 ,step= 0.1 },
{ id= "dispersion_factor" ,type= "track" ,val= 2 ,curr= {curr_gameplay,"dispersion_factor"} ,functor= {func_gameplay_diff,"dispersion_factor"} ,min= 1 ,max= 5 ,step= 0.1 },
{ id= "power_loss_bias" ,type= "track" ,val= 2 ,curr= {curr_gameplay,"power_loss_bias"} ,functor= {func_gameplay_diff,"power_loss_bias"} ,min= 0.0 ,max= 1 ,step= 0.05 },
{ id= "weight" ,type= "track" ,val= 2 ,curr= {curr_gameplay,"weight"} ,functor= {func_gameplay_diff,"weight"} ,min= 10 ,max= 50 ,step= 1 },
{ id= "thirst" ,type= "check" ,val= 1 ,curr= {curr_gameplay,"thirst"} ,functor= {func_gameplay_diff,"thirst"} },
{ id= "sleep" ,type= "check" ,val= 1 ,curr= {curr_gameplay,"sleep"} ,functor= {func_gameplay_diff,"sleep"} },
{ id= "radiation_day" ,type= "check" ,val= 1 ,curr= {curr_gameplay,"radiation_day"} ,functor= {func_gameplay_diff,"radiation_day"} },
{ id= "notify_geiger" ,type= "check" ,val= 1 ,curr= {curr_gameplay,"notify_geiger"} ,functor= {func_gameplay_diff,"notify_geiger"} },
{ id= "notify_anomaly" ,type= "check" ,val= 1 ,curr= {curr_gameplay,"notify_anomaly"} ,functor= {func_gameplay_diff,"notify_anomaly"} },
},},
{ id= "disguise" ,sh=true ,gr={
{ id= "slide_disguise" ,type= "slide" ,link= "ui_options_slider_disguise" ,text= "ui_mm_menu_disguise" ,size= {512,50} },
{ id= "desc_disguise" ,type= "desc" ,text= "ui_mm_desc_disguise" ,clr= {255,125,175,200} },
{ id= "line" ,type= "line" },
{ id= "state" ,type= "check" ,val= 1 ,def= true },
{ id= "line" ,type= "line" },
{ id= "active_item" ,type= "check" ,val= 1 ,def= true },
{ id= "active_item_factor" ,type= "track" ,val= 2 ,def= 1 ,min= 0.1 ,max= 2 ,step= 0.1 },
{ id= "weapon" ,type= "check" ,val= 1 ,def= true },
{ id= "weapon_factor" ,type= "track" ,val= 2 ,def= 1 ,min= 0.1 ,max= 2 ,step= 0.1 },
{ id= "outfit" ,type= "check" ,val= 1 ,def= true },
{ id= "outfit_factor" ,type= "track" ,val= 2 ,def= 1 ,min= 0.1 ,max= 2 ,step= 0.1 },
{ id= "helmet" ,type= "check" ,val= 1 ,def= true },
{ id= "helmet_factor" ,type= "track" ,val= 2 ,def= 1 ,min= 0.1 ,max= 2 ,step= 0.1 },
{ id= "backpack" ,type= "check" ,val= 1 ,def= true },
{ id= "backpack_factor" ,type= "track" ,val= 2 ,def= 1 ,min= 0.1 ,max= 2 ,step= 0.1 },
{ id= "inventory" ,type= "check" ,val= 1 ,def= true },
{ id= "inventory_factor" ,type= "track" ,val= 2 ,def= 1 ,min= 0.1 ,max= 2 ,step= 0.1 },
{ id= "line" ,type= "line" },
{ id= "speed" ,type= "check" ,val= 1 ,def= true },
{ id= "speed_factor" ,type= "track" ,val= 2 ,def= 1 ,min= 0.1 ,max= 2 ,step= 0.1 },
{ id= "distance" ,type= "check" ,val= 1 ,def= true },
{ id= "distance_factor" ,type= "track" ,val= 2 ,def= 5 ,min= 1 ,max= 100 ,step= 1 },
{ id= "stay_time" ,type= "check" ,val= 1 ,def= true },
{ id= "stay_time_factor" ,type= "track" ,val= 2 ,def= 20 ,min= 1 ,max= 100 ,step= 1 },
},},
{ id= "fast_travel" ,sh=true ,gr={
{ id= "slide_fast_travel" ,type= "slide" ,link= "ui_options_slider_fast_travel" ,text= "ui_mm_menu_fast_travel" ,size= {512,50} },
{ id= "desc_fast_travel" ,type= "desc" ,text= "ui_mm_desc_fast_travel" ,clr= {255,125,175,200} },
{ id= "line" ,type= "line" },
{ id= "state" ,type= "list" ,val= 2 ,def= 0 ,content= {{0,"disabled"} , {1,"visit_only"} , {2,"show_all"}} },
{ id= "line" ,type= "line" },
{ id= "on_combat" ,type= "check" ,val= 1 ,def= false },
{ id= "on_overweight" ,type= "check" ,val= 1 ,def= false },
{ id= "on_damage" ,type= "check" ,val= 1 ,def= false },
{ id= "on_emission" ,type= "check" ,val= 1 ,def= false },
{ id= "long_names" ,type= "check" ,val= 1 ,def= false },
{ id= "visit_message" ,type= "check" ,val= 1 ,def= false },
{ id= "time" ,type= "check" ,val= 1 ,def= false },
},},
{ id= "backpack_travel" ,sh=true ,gr={
{ id= "slide_backpack_travel" ,type= "slide" ,link= "ui_options_slider_fast_travel" ,text= "ui_mm_menu_backpack_travel" ,size= {512,50} },
{ id= "desc_backpack_travel" ,type= "desc" ,text= "ui_mm_desc_backpack_travel" ,clr= {255,125,175,200} },
{ id= "line" ,type= "line" },
{ id= "state" ,type= "check" ,val= 1 ,def= false },
{ id= "line" ,type= "line" },
{ id= "on_combat" ,type= "check" ,val= 1 ,def= false },
{ id= "on_overweight" ,type= "check" ,val= 1 ,def= false },
{ id= "on_damage" ,type= "check" ,val= 1 ,def= false },
{ id= "on_emission" ,type= "check" ,val= 1 ,def= false },
{ id= "time" ,type= "check" ,val= 1 ,def= false },
},},
},},
{ id= "alife" ,gr={
{ id= "general" ,sh=true ,gr={
{ id= "slide_alife" ,type= "slide" ,link= "ui_options_slider_alife" ,text= "ui_mm_title_alife" ,size= {512,50} },
{ id= "alife_mutant_pop" ,type= "list" ,val= 2 ,def= 0.75 ,content= {{0.25} , {0.5} , {0.75} , {1}} ,no_str= true },
{ id= "alife_stalker_pop" ,type= "list" ,val= 2 ,def= 0.5 ,content= {{0.25} , {0.5} , {0.75} , {1}} ,no_str= true },
{ id= "offline_combat" ,type= "list" ,val= 0 ,def= "full" ,content= {{"full","full"} , {"on_smarts_only","on_smarts_only"}, {"off","off"}} },
{ id= "excl_dist" ,type= "list" ,val= 2 ,def= 75 ,content= {{0} , {25} , {50} , {75} , {100}} ,no_str= true },
{ id= "dynamic_anomalies" ,type= "check" ,val= 1 ,def= true },
{ id= "dynamic_relations" ,type= "check" ,val= 1 ,def= false },
{ id= "war_goodwill_reset" ,type= "check" ,val= 1 ,def= false },
{ id= "heli_engine_sound" ,type= "check" ,val= 1 ,def= false },
{ id= "heli_spawn" ,type= "check" ,val= 1 ,def= false },
},},
{ id= "event" ,sh=true ,gr={
{ id= "slide_emission" ,type= "slide" ,link= "ui_options_slider_emission" ,text= "ui_mm_title_emission" ,size= {512,50} },
{ id= "emission_state" ,type= "check" ,val= 1 ,def= true },
{ id= "emission_frequency" ,type= "list" ,val= 2 ,def= 24 ,content= {{12,"every_12h"} , {24,"every_24h"} , {48,"every_2d"} , {96,"every_4d"}} },
{ id= "emission_fate" ,type= "list" ,val= 0 ,def= "kill_at_wave" ,content= {{"kill_at_end"} , {"kill_at_wave"} , {"turn_to_zombie"} , {"explode"}} },
{ id= "emission_warning" ,type= "list" ,val= 0 ,def= "siren_radio" ,content= {{"siren_radio"} , {"siren"} , {"radio"} , {"no_warning"}} },
{ id= "emission_task" ,type= "check" ,val= 1 ,def= true },
{ id= "slide_psi_storm" ,type= "slide" ,link= "ui_options_slider_psi_storm" ,text= "ui_mm_title_psi_storm" ,size= {512,50} ,spacing= 20 },
{ id= "psi_storm_state" ,type= "check" ,val= 1 ,def= true },
{ id= "psi_storm_frequency" ,type= "list" ,val= 2 ,def= 24 ,content= {{12,"every_12h"} , {24,"every_24h"} , {48,"every_2d"} , {96,"every_4d"}} },
{ id= "psi_storm_fate" ,type= "list" ,val= 0 ,def= "kill_at_vortex" ,content= {{"kill_at_vortex"} , {"turn_to_zombie"} , {"unhurt"}} },
{ id= "psi_storm_warning" ,type= "list" ,val= 0 ,def= "siren" ,content= {{"siren"} , {"no_warning"}} },
{ id= "psi_storm_task" ,type= "check" ,val= 1 ,def= true },
},},
{ id= "warfare" ,presets= {"warfare_default","warfare_slow","warfare_slower"} ,gr={
{ id= "general" ,sh=true ,presets= {"warfare_default","warfare_yoko"} ,gr={
{ id= "slide_warfare" ,type= "slide" ,link= "ui_options_slider_warfare" ,text= "ui_mm_title_warfare" ,size= {512,50} },
{ id= "all_out_war" ,type= "check" ,val= 1 ,def= false },
{ id= "random_starting_locations" ,type= "check" ,val= 1 ,def= false },
{ id= "random_starting_character" ,type= "check" ,val= 1 ,def= false },
{ id= "random_stalker_chance" ,type= "track" ,val= 2 ,def= 50 ,min= 0 ,max= 100 ,step= 1 },
{ id= "random_monster_chance" ,type= "track" ,val= 2 ,def= 50 ,min= 0 ,max= 100 ,step= 1 },
{ id= "auto_capture" ,type= "check" ,val= 1 ,def= true },
{ id= "auto_capture_max_distance" ,type= "track" ,val= 2 ,def= 50 ,min= 0 ,max= 100 ,step= 1 },
{ id= "auto_capture_wait_time" ,type= "track" ,val= 2 ,def= 15 ,min= 0 ,max= 100 ,step= 1 },
{ id= "fog_of_war" ,type= "check" ,val= 1 ,def= true },
{ id= "fog_of_war_distance" ,type= "track" ,val= 2 ,def= 100 ,min= 0 ,max= 100 ,step= 1 },
{ id= "hide_unfriendly_squads" ,type= "check" ,val= 1 ,def= true },
{ id= "hide_smarts" ,type= "check" ,val= 1 ,def= false },
{ id= "hide_underground_smarts" ,type= "check" ,val= 1 ,def= true },
{ id= "enemy_new_game_bonus" ,type= "check" ,val= 1 ,def= false },
{ id= "enemy_resource_boost" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id= "enemy_base_boost" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id= "actor_influence_weight" ,type= "track" ,val= 2 ,def= 250 ,min= 0 ,max= 10000 ,step= 50 },
{ id= "novice_squad_price" ,type= "track" ,val= 2 ,def= 1000 ,min= 0 ,max= 100000 ,step= 1000 },
{ id= "advanced_squad_price" ,type= "track" ,val= 2 ,def= 10000 ,min= 0 ,max= 100000 ,step= 1000 },
{ id= "veteran_squad_price" ,type= "track" ,val= 2 ,def= 50000 ,min= 0 ,max= 100000 ,step= 1000 },
{ id= "heli_price" ,type= "track" ,val= 2 ,def= 75000 ,min= 0 ,max= 100000 ,step= 1000 },
{ id= "actor_support_enemy_rank_weight" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 50 ,step= 1 },
{ id= "actor_support_reward_money" ,type= "track" ,val= 2 ,def= 500 ,min= 0 ,max= 10000 ,step= 50 },
{ id= "actor_support_reward_influence" ,type= "track" ,val= 2 ,def= 0.2 ,min= 0 ,max= 2 ,step= 0.1 },
{ id= "monster_max_squads_per_level" ,type= "track" ,val= 2 ,def= 5 ,min= 1 ,max= 200 ,step= 1 },
{ id= "monster_min_faction_respawn" ,type= "track" ,val= 2 ,def= 5 ,min= 1 ,max= 200 ,step= 1 },
{ id= "monster_max_faction_respawn" ,type= "track" ,val= 2 ,def= 30 ,min= 1 ,max= 200 ,step= 1 },
{ id= "zombies_act_as_faction" ,type= "check" ,val= 1 ,def= false },
--{ id= "offline_combat_distance" ,type= "track" ,val= 2 ,def= 100 ,min= 1 ,max= 200 ,step= 1 },
{ id= "enable_mutant_offline_combat" ,type= "check" ,val= 1 ,def= true },
{ id= "disable_smart_pop_cap" ,type= "check" ,val= 1 ,def= false },
{ id= "purge_zone_on_emission" ,type= "check" ,val= 1 ,def= false },
{ id= "purge_zone_percentage" ,type= "track" ,val= 2 ,def= 50 ,min= 1 ,max= 100 ,step= 1 },
{ id= "debug_logging" ,type= "check" ,val= 1 ,def= false },
},},
{ id= "azazel" ,sh=true ,presets= {"warfare_default","warfare_yoko"} ,gr={
{ id= "slide_warfare" ,type= "slide" ,link= "ui_options_slider_warfare" ,text= "ui_mm_title_warfare_azazel" ,size= {512,50} },
{ id="state" ,type= "check" ,val= 1 ,def= true },
{ id="respawn_as_companions" ,type= "check" ,val= 1 ,def= true },
{ id="respawn_as_actor_faction" ,type= "check" ,val= 1 ,def= true },
{ id="respawn_as_allies" ,type= "check" ,val= 1 ,def= false },
{ id="respawn_as_neutrals" ,type= "check" ,val= 1 ,def= false },
{ id="respawn_as_enemies" ,type= "check" ,val= 1 ,def= false },
{ id="respawn_as_nearest" ,type= "check" ,val= 1 ,def= true },
{ id="companion_dist_mult" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 100 ,step= 1 },
{ id="actor_faction_dist_mult" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 100 ,step= 1 },
{ id="friend_dist_mult" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 100 ,step= 1 },
{ id="ally_dist_mult" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 100 ,step= 1 },
{ id="neutral_dist_mult" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 100 ,step= 1 },
{ id="enemy_dist_mult" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 100 ,step= 1 },
{ id="companion_min_respawn_dist" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id="companion_max_respawn_dist" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id="actor_faction_min_respawn_dist" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id="actor_faction_max_respawn_dist" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id="ally_min_respawn_dist" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id="ally_max_respawn_dist" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id="neutral_respawn_min_dist" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id="neutral_respawn_max_dist" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id="enemy_respawn_min_dist" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
{ id="enemy_respawn_max_dist" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 100 ,step= 1 },
},},
{ id= "stalker" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction" ,apply_to_all=true ,gr={
{ id= "slide_warfare" ,type= "slide" ,link= "ui_options_slider_warfare" ,text= "ui_mm_title_warfare_faction" ,size= {512,50} },
{ id="participate_in_warfare" ,type= "check" ,val= 1 ,def= true ,hint="alife_warfare_faction_participate_in_warfare" },
{ id="spawn_on_new_game" ,type= "check" ,val= 1 ,def= true ,hint="alife_warfare_faction_spawn_on_new_game" },
{ id="random_spawn_entries" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_random_spawn_entries" },
{ id="linked_level_targeting" ,type= "check" ,val= 1 ,def= true ,hint="alife_warfare_faction_linked_level_targeting" },
{ id="ignore_empty_targets" ,type= "check" ,val= 1 ,def= false ,hint="alife_warfare_faction_ignore_empty_targets" },
{ id="expansion_aggression" ,type= "track" ,val= 2 ,def= 50 ,min= 0 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_expansion_aggression" },
{ id="offline_power_multiplier" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_offline_power_multiplier" },
{ id="night_activity_chance" ,type= "track" ,val= 2 ,def= 50 ,min= 0 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_night_activity_chance" },
{ id="keep_last_base" ,type= "check" ,val= 1 ,def= false ,hint="alife_warfare_faction_keep_last_base" },
{ id="min_faction_respawn" ,type= "track" ,val= 2 ,def= 15 ,min= 0 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_min_faction_respawn" },
{ id="max_faction_respawn" ,type= "track" ,val= 2 ,def= 90 ,min= 0 ,max= 200 ,step= 1 ,hint="alife_warfare_faction_max_faction_respawn" },
{ id="min_invasion_size" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 10 ,step= 0.1 ,hint="alife_warfare_faction_min_invasion_size" },
{ id="max_invasion_size" ,type= "track" ,val= 2 ,def= 2.5 ,min= 0 ,max= 10 ,step= 0.1 ,hint="alife_warfare_faction_max_invasion_size" },
{ id="min_invasion_depart_time" ,type= "track" ,val= 2 ,def= 5 ,min= 0 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_min_invasion_depart_time" },
{ id="max_invasion_depart_time" ,type= "track" ,val= 2 ,def= 30 ,min= 0 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_max_invasion_depart_time" },
{ id="min_patrol_time" ,type= "track" ,val= 2 ,def= 60 ,min= 0 ,max= 1000 ,step= 10 ,hint="alife_warfare_faction_min_patrol_time" },
{ id="max_patrol_time" ,type= "track" ,val= 2 ,def= 240 ,min= 0 ,max= 1000 ,step= 10 ,hint="alife_warfare_faction_max_patrol_time" },
{ id="min_patrol_squads" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 10 ,step= 1 ,hint="alife_warfare_faction_min_patrol_squads" },
{ id="max_patrol_squads" ,type= "track" ,val= 2 ,def= 2 ,min= 0 ,max= 10 ,step= 1 ,hint="alife_warfare_faction_max_patrol_squads" },
{ id="patrol_hunt_chance" ,type= "track" ,val= 2 ,def= 50 ,min= 0 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_patrol_hunt_chance" },
{ id="min_patrol_rest_time" ,type= "track" ,val= 2 ,def= 60 ,min= 0 ,max= 1000 ,step= 10 ,hint="alife_warfare_faction_min_patrol_rest_time" },
{ id="max_patrol_rest_time" ,type= "track" ,val= 2 ,def= 240 ,min= 0 ,max= 1000 ,step= 10 ,hint="alife_warfare_faction_max_patrol_rest_time" },
{ id="min_resurgence_wait_time" ,type= "track" ,val= 2 ,def= 30 ,min= 0 ,max= 1000 ,step= 10 ,hint="alife_warfare_faction_min_resurgence_wait_time" },
{ id="max_resurgence_wait_time" ,type= "track" ,val= 2 ,def= 120 ,min= 0 ,max= 1000 ,step= 10 ,hint="alife_warfare_faction_max_resurgence_wait_time" },
{ id="random_squad_count" ,type= "check" ,val= 1 ,def= false ,hint="alife_warfare_faction_random_squad_count" },
{ id="min_random_squad_count" ,type= "track" ,val= 2 ,def= 1 ,min= 0 ,max= 20 ,step= 1 ,hint="alife_warfare_faction_min_random_squad_count" },
{ id="max_random_squad_count" ,type= "track" ,val= 2 ,def= 3 ,min= 0 ,max= 20 ,step= 1 ,hint="alife_warfare_faction_max_random_squad_count" },
{ id="random_patrols" ,type= "check" ,val= 1 ,def= false ,hint="alife_warfare_faction_random_patrols" },
{ id="max_random_patrols" ,type= "track" ,val= 2 ,def= 15 ,min= 0 ,max= 20 ,step= 1 ,hint="alife_warfare_faction_max_random_patrols" },
{ id="min_random_patrol_time" ,type= "track" ,val= 2 ,def= 30 ,min= 0 ,max= 1000 ,step= 10 ,hint="alife_warfare_faction_min_random_patrol_time" },
{ id="max_random_patrol_time" ,type= "track" ,val= 2 ,def= 180 ,min= 0 ,max= 1000 ,step= 10 ,hint="alife_warfare_faction_max_random_patrol_time" },
{ id="base_priority" ,type= "track" ,val= 2 ,def= 10 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_base_priority" },
{ id="resource_priority" ,type= "track" ,val= 2 ,def= 5 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_resource_priority" },
{ id="territory_priority" ,type= "track" ,val= 2 ,def= -5 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_territory_priority" },
{ id="flag_priority" ,type= "track" ,val= 2 ,def= 1 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_flag_priority" },
{ id="is_being_targeted_priority" ,type= "track" ,val= 2 ,def= 2 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_is_being_targeted_priority" },
{ id="target_weaker_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_target_weaker_priority" },
{ id="target_stronger_priority" ,type= "track" ,val= 2 ,def= 1 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_target_stronger_priority" },
{ id="target_faction_stronger_priority" ,type= "track" ,val= 2 ,def= 1 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_target_faction_stronger_priority" },
{ id="target_faction_weaker_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_target_faction_weaker_priority" },
{ id="target_resource_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_target_resource_priority" },
{ id="target_on_same_level_priority" ,type= "track" ,val= 2 ,def= 100 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_target_on_same_level_priority" },
{ id="max_smart_targets_per_base" ,type= "track" ,val= 2 ,def= 2 ,min= 0 ,max= 10 ,step= 1 ,hint="alife_warfare_faction_max_smart_targets_per_base" },
{ id="resource_count_modifier" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 1 ,step= 0.01 ,hint="alife_warfare_faction_resource_count_modifier" },
{ id="base_count_modifier" ,type= "track" ,val= 2 ,def= 0 ,min= 0 ,max= 1 ,step= 0.01 ,hint="alife_warfare_faction_base_count_modifier" },
{ id="lvl_k00_marsh_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_k00_marsh_priority" },
{ id="lvl_k01_darkscape_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_k01_darkscape_priority" },
{ id="lvl_l01_escape_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l01_escape_priority" },
{ id="lvl_l02_garbage_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l02_garbage_priority" },
{ id="lvl_l03_agroprom_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l03_agroprom_priority" },
{ id="lvl_l04_darkvalley_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l04_darkvalley_priority" },
{ id="lvl_l05_bar_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l05_bar_priority" },
{ id="lvl_l06_rostok_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l06_rostok_priority" },
{ id="lvl_l07_military_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l07_military_priority" },
{ id="lvl_l08_yantar_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l08_yantar_priority" },
{ id="lvl_l09_deadcity_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l09_deadcity_priority" },
{ id="lvl_l10_limansk_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l10_limansk_priority" },
{ id="lvl_l10_radar_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l10_radar_priority" },
{ id="lvl_l10_red_forest_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l10_red_forest_priority" },
{ id="lvl_l11_hospital_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l11_hospital_priority" },
{ id="lvl_l11_pripyat_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l11_pripyat_priority" },
{ id="lvl_l12_stancia_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l12_stancia_priority" },
{ id="lvl_l12u_sarcofag_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l12u_sarcofag_priority" },
{ id="lvl_l12u_control_monolith_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l12u_control_monolith_priority" },
{ id="lvl_l12_stancia_2_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l12_stancia_2_priority" },
{ id="lvl_l13_generators_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_l13_generators_priority" },
{ id="lvl_zaton_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_zaton_priority" },
{ id="lvl_jupiter_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_jupiter_priority" },
{ id="lvl_pripyat_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_pripyat_priority" },
{ id="lvl_jupiter_underground_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_jupiter_underground_priority" },
{ id="lvl_k02_trucks_cemetery_priority" ,type= "track" ,val= 2 ,def= 0 ,min= -100 ,max= 100 ,step= 1 ,hint="alife_warfare_faction_lvl_k02_trucks_cemetery_priority" },
},},
},},
{ id= "dynamic_news" ,sh=true ,gr={
{ id= "slide_dynamic_news" ,type= "slide" ,link= "ui_options_slider_news" ,text= "ui_mm_menu_dynamic_news" ,size= {512,50} },
{ id= "desc_dynamic_news" ,type= "desc" ,text= "ui_mm_desc_dynamic_news" ,clr= {255,125,175,200} },
{ id= "line" ,type= "line" },
{ id="message_duration" ,type= "track" ,val= 2 ,def= 10 ,min= 5 ,max= 30 ,step= 1 },
{ id= "line" ,type= "line" },
{ id="death_stalker_news" ,type= "check" ,val= 1 ,def= true },
{ id="death_mutant_news" ,type= "check" ,val= 1 ,def= true },
{ id="generic_death_news" ,type= "check" ,val= 1 ,def= false },
{ id="death_report_news" ,type= "check" ,val= 1 ,def= false },
{ id="kill_wounded_news" ,type= "check" ,val= 1 ,def= true },
{ id="found_artifact_news" ,type= "check" ,val= 1 ,def= true },
{ id="heli_call_news" ,type= "check" ,val= 1 ,def= true },
{ id="loot_news" ,type= "check" ,val= 1 ,def= true },
{ id= "line" ,type= "line" },
{ id="reaction_news" ,type= "check" ,val= 1 ,def= true },
{ id= "line" ,type= "line" },
{ id="weather_news" ,type= "check" ,val= 1 ,def= true },
{ id="time_news" ,type= "check" ,val= 1 ,def= true },
{ id="nearby_activity_news" ,type= "check" ,val= 1 ,def= true },
{ id="dumb_zombie_news" ,type= "check" ,val= 1 ,def= true },
{ id="cycle_of_special_news" ,type= "track" ,val= 2 ,def= 240 ,min= 60 ,max= 1800 ,step= 60 },
{ id= "line" ,type= "line" },
{ id="bounty_news" ,type= "check" ,val= 1 ,def= true },
{ id="cycle_of_task_news" ,type= "track" ,val= 2 ,def= 300 ,min= 60 ,max= 1800 ,step= 60 },
{ id= "line" ,type= "line" },
{ id="random_msg_news" ,type= "check" ,val= 1 ,def= true },
{ id="factions_report_news" ,type= "check" ,val= 1 ,def= true },
{ id="zone_activity_news" ,type= "check" ,val= 1 ,def= true },
{ id="found_dead_news" ,type= "check" ,val= 1 ,def= true },
{ id="surge_news" ,type= "check" ,val= 1 ,def= true },
{ id="cycle_of_random_news" ,type= "track" ,val= 2 ,def= 240 ,min= 60 ,max= 1800 ,step= 60 },
{ id= "line" ,type= "line" },
{ id="companions_news" ,type= "check" ,val= 1 ,def= true },
{ id="cycle_of_companions_news" ,type= "track" ,val= 2 ,def= 240 ,min= 60 ,max= 1800 ,step= 60 },
},},
},},
{ id= "other" ,sh=true ,gr={
{ id= "slide_other" ,type= "slide" ,link= "ui_options_slider_other" ,text= "ui_mm_title_other" ,size= {512,50} },
{ id= "localization" ,type= "list" ,val= 0 ,def= "eng" ,curr= {curr_localization} ,content= {{"eng"} , {"rus"}} ,no_str= true ,functor= {func_localization} },
{ id= "discord" ,type= "check" ,val= 1 ,def= true ,cmd= "discord_status" },
{ id= "line" ,type= "line" ,precondition = {debug_only} },
{ id= "important_save" ,type= "check" ,val= 1 ,def= true ,cmd= "g_important_save" },
-- { id= "autosave_timer" ,type= "check" ,val= 1 ,def= false ,functor= {func_autosave_timer} }, -- removed for custom value box below
{ id= "autosave_timer_new" ,type= "input" ,val= 0 ,def= 0, functor ={func_autosave_timer_new}},
{ id= "quicksave_cnt" ,type= "list" ,val= 2 ,def= 5 ,content= {{1} , {5} , {10} , {15} , {20}} ,no_str= true },
{ id= "line" ,type= "line" ,precondition = {debug_only} },
{ id= "debug_hud" ,type= "check" ,val= 1 ,def= false ,precondition = {debug_only} ,functor= {func_debug_hud} },
{ id= "debug_map_hud" ,type= "check" ,val= 1 ,def= false ,precondition = {debug_only} ,functor= {func_debug_map_hud} },
{ id= "debug_error" ,type= "check" ,val= 1 ,def= false ,precondition = {debug_only} },
},},
}
init_opt_coder()
end
function init_opt_coder()
------------------------------------------------------------------------
-- Auto-complete factions options for warfare
local t = {}
copy_table(t, options[5].gr[3].gr[3].gr)
options[5].gr[3].gr[4] = { id= "bandit" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[5] = { id= "csky" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[6] = { id= "army" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[7] = { id= "freedom" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[8] = { id= "dolg" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[9] = { id= "ecolog" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[10] = { id= "killer" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[11] = { id= "monolith" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[12] = { id= "renegade" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[13] = { id= "greh" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[14] = { id= "isg" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
options[5].gr[3].gr[15] = { id= "zombied" ,sh=true ,presets= {"warfare_default","warfare_yoko"}, id_gr= "warfare_faction", apply_to_all=true ,gr=t }
------------------------------------------------------------------------
-- Auto-complete options for radio music
local ltx = ini_file("plugins\\radio_zone_fm.ltx") -- File control: reading the ltx file that controls the hotkeys
local num_of_plyr = ltx:r_float_ex("trx_radio_plyr","number_of_playlists") or 2 -- Number of Music Player plylists
local radio_size = #options[2].gr[3].gr
for i=1,num_of_plyr do
radio_size = radio_size + 1
options[2].gr[3].gr[radio_size] = { id= ("playlist_name_"..i) ,type= "input" ,val= 0 ,def= {def_radio_playlist, i} }
end
------------------------------------------------------------------------
-- Coding options
local function code_option(gr, id, num)
local path
for i=1,#gr do
if allowed_type[gr[i].type] then
path = cc(id , gr[i].id)
opt_index[path] = cc(num , i)
opt_val[path] = gr[i].val
--printf("-[%s] | index: %s - type: %s", path, opt_index[path], opt_val[path])
end
end
end
local id_1, id_2, id_3
-- Level 1
for i=1,#options do
id_1 = options[i].id
if options[i].sh then
code_option(options[i].gr, id_1, i)
else
-- Level 2
for ii=1,#options[i].gr do
id_2 = options[i].gr[ii].id
if options[i].gr[ii].sh then
code_option( (options[i].gr[ii].gr), (id_1 .._opt_.. id_2), (i .._opt_.. ii) )
else
-- Level 3
for iii=1,#options[i].gr[ii].gr do
id_3 = options[i].gr[ii].gr[iii].id
if options[i].gr[ii].gr[iii].sh then
code_option( (options[i].gr[ii].gr[iii].gr), (id_1 .._opt_.. id_2 .._opt_.. id_3), (i .._opt_.. ii .._opt_.. iii) )
else
----
end
end
end
end
end
end
end
------------------------------------------------------------
-- Functors
------------------------------------------------------------
-- Special
function start_lighting_ui(self)
self:On_Cancel()
ui_ctrl_lighting.start()
end
-- Preconditions
function level_present()
return level.present()
end
function debug_only()
return DEV_DEBUG
end
function for_renderer(...)
local rend = {...}
local curr_rend = get_console_cmd(0, "renderer")
local result = false
for i=1,#rend do
result = result or curr_rend == rend[i]
end
return result
end
-- Default values
function def_radio_playlist(i)
if (i == 1) then return "OST"
elseif (i == 2) then return "TRX_Special"
else return "Playlist_" .. tostring(i)
end
end
-- Contents
function cont_vid_mode()
local ratio = {
[round_idp(5/4, 1)] = "(5:4)",
[round_idp(4/3, 1)] = "(4:3)",
[round_idp(16/9, 1)] = "(16:9)",
[round_idp(16/10, 1)] = "(16:10)",
[round_idp(21/9, 1)] = "(21:9)",
[round_idp(32/9, 1)] = "(32:9)",
}
local ress = game.get_resolutions() -- get all res list from engine in one string
ress = str_explode(ress,",")
local cont = {}
for i=1,#ress do
local res = str_explode(ress[i],"x")
local w = tonumber(res[1])
local h = tonumber(res[2])
local rat = ratio[round_idp(w/h , 1)] or ""
cont[#cont + 1] = { (w.."x"..h) , (w.." x "..h.." "..rat) }
end
return cont
end
function cont_renderer()
if for_renderer("renderer_r2a","renderer_r2","renderer_r2.5") then
return {{"renderer_r2a"},{"renderer_r2"},{"renderer_r2.5"}}
else
local curr = get_console_cmd(0, "renderer")
return {{curr}}
end
end
function cont_sun_quality()
local cont = { {"st_opt_low","low"},{"st_opt_medium","medium"},{"st_opt_high","high"} }
if for_renderer("renderer_r3","renderer_r4") then
cont[#cont + 1] = {"st_opt_ultra","ultra"}
cont[#cont + 1] = {"st_opt_extreme","extreme"}
end
return cont
end
-- Functor to execute on apply
function func_screen_mode()
local val = opt_temp["video/basic/screen_mode"]
if (val == nil) then
val = axr_main.config:r_value(opt_section, "video/basic/screen_mode", 2)
end
if (val == 1) then
exec_console_cmd("rs_screenmode fullscreen")
elseif (val == 2) then
exec_console_cmd("rs_screenmode borderless")
else
exec_console_cmd("rs_screenmode windowed")
end
--[[
if val == 1 then
exec_console_cmd("rs_fullscreen on")
exec_console_cmd("rs_borderless 0")
else
exec_console_cmd("rs_fullscreen off")
exec_console_cmd("rs_borderless 1")
end
--]]
end
function func_localization()
local val = opt_temp["other/localization"]
if (val == nil) then
val = axr_main.config:r_value(opt_section, "other/localization", 0)
end
if (ini_loc:r_value("string_table","language") ~= val) then
ini_loc:w_value("string_table","language",val)
ini_loc:save()
reload_ini_sys()
game.reload_language()
if level.present() then
SendScriptCallback("on_localization_change")
end
end
end
function func_slot_hud()
if level.present() then
local val = opt_temp["video/hud/show_slots"]
if (val == nil) then
val = axr_main.config:r_value(opt_section, "video/hud/show_slots", 1)
end
if val then
item_artefact.activate_hud()
else
item_artefact.deactivate_hud()
end
end
end
function func_hardcore_ai_aim()
if level.present() then
local val = opt_temp["gameplay/general/hardcore_ai_aim"]
if (val == nil) then
val = axr_main.config:r_value(opt_section, "gameplay/general/hardcore_ai_aim", 1)
end
if val then
exec_console_cmd("ai_aim_max_angle 20.0")
exec_console_cmd("ai_aim_min_angle 17.0")
exec_console_cmd("ai_aim_min_speed 2.5")
exec_console_cmd("ai_aim_predict_time 0.28")
else
exec_console_cmd("ai_aim_max_angle 0.7854")
exec_console_cmd("ai_aim_min_angle 0.19635")
exec_console_cmd("ai_aim_min_speed 0.24")
exec_console_cmd("ai_aim_predict_time 0.40")
end
end
end
-- unused
-- function func_autosave_timer()
-- if (level.present() and game_autosave) then
-- local val = opt_temp["other/autosave_timer"]
-- if (val == nil) then
-- val = axr_main.config:r_value(opt_section, "other/autosave_timer", 1)
-- end
-- if val then
-- game_autosave.activate_feature()
-- else
-- game_autosave.deactivate_feature()
-- end
-- end
-- end
function func_autosave_timer_new()
game_autosave_new.option_updated()
end
function func_hud_minimap()
if (level.present()) then
local val = opt_temp["video/hud/show_minimap"]
if (val == nil) then
val = axr_main.config:r_value(opt_section, "video/hud/show_minimap", 0)
end
local maingameui = ActorMenu.get_maingame()
if (maingameui and maingameui.UIZoneMap) then
maingameui.UIZoneMap.disabled = not val
maingameui.UIMotionIcon:Show(maingameui.UIZoneMap.disabled == false)
end
end
end
function func_hud_autohide_bar()
if (level.present()) then
local val = opt_temp["video/hud/autohide_stamina_bar"]
if (val == nil) then
val = axr_main.config:r_value(opt_section, "video/hud/autohide_stamina_bar", 1)
end
actor_effects.toggle_hud_autohide(val)
end
end
function func_player_name()
local se_actor = alife():actor()
if se_actor then
local curr_name = se_actor:character_name()
local new_name = opt_temp["gameplay/general/player_name"] or axr_main.config:r_value(opt_section, "gameplay/general/player_name", 0)
if new_name and (new_name ~= "") and (new_name ~= curr_name) then
se_actor:set_character_name(new_name)
printf("- Changed player name from (%s) to (%s)", curr_name, new_name)
end
end
end
function func_gameplay_diff(fac)
if (opt_temp["gameplay/gameplay_diff/"..fac] ~= nil) then
game_difficulties.set_game_factor(fac, opt_temp["gameplay/gameplay_diff/"..fac])
end
end
function func_economy_diff(fac)
if (opt_temp["gameplay/economy_diff/"..fac] ~= nil) then
game_difficulties.set_eco_factor(fac, opt_temp["gameplay/economy_diff/"..fac])
end
end
function func_debug_hud()
if (level.present() and xrs_debug_tools) then
local val = opt_temp["other/debug_hud"]
if (val == nil) then
val = axr_main.config:r_value(opt_section, "other/debug_hud", 1)
end
if val then
xrs_debug_tools.activate_feature()
else
xrs_debug_tools.deactivate_feature()
end
end
end
function func_debug_map_hud()
if (level.present() and ui_map_debug_ex) then
local val = opt_temp["other/debug_map_hud"]
if (val == nil) then
val = axr_main.config:r_value(opt_section, "other/debug_map_hud", 1)
end
if val then
ui_map_debug_ex.activate_feature()
else
ui_map_debug_ex.deactivate_feature()
end
end
end
function func_crosshair_clr(n)
local val = opt_temp["video/hud/crosshair_clr_" .. n]
if (val == nil) then
return
end
local curr = get_console_cmd(nil, "g_crosshair_color")
curr = tostring(curr)
local r,g,b,a = string.match(curr,"(%d+), (%d+), (%d+), (%d+)")
if n == "a" then
a = val
elseif n == "r" then
r = val
elseif n == "g" then
g = val
elseif n == "b" then
b = val
end
exec_console_cmd( strformat("g_crosshair_color (%s, %s, %s, %s)",r,g,b,a) )
end
-- Current value override
function curr_renderer()
return get_console_cmd(0, "renderer")
end
function curr_screen_mode()
local sm = get_console_cmd(0, "rs_screenmode")
if (sm == "fullscreen") then
return 1
elseif (sm == "borderless") then
return 2
end
return 3
end
function curr_player_name()
return alife():actor():character_name()
end
function curr_economy(fact)
local value = game_difficulties.get_eco_factor(fact)
return value
end
function curr_gameplay(fact)
local value = game_difficulties.get_game_factor(fact)
return value
end
function curr_localization()
local val = ini_loc:r_value("string_table","language") or axr_main.config:r_value(opt_section, "other/localization", 0)
return val
end
function curr_crosshair_clr(n)
local curr = get_console_cmd(nil, "g_crosshair_color")
curr = tostring(curr)
local r,g,b,a = string.match(curr,"(%d+), (%d+), (%d+), (%d+)")
if n == "a" then
return tonumber(a)
elseif n == "r" then
return tonumber(r)
elseif n == "g" then
return tonumber(g)
elseif n == "b" then
return tonumber(b)
end
return 255
end
-- Utilities
function is_int(num)
return (m_floor(num) == num)
end
function exec(func,...)
if (not func) then
return false
end
return func(...)
end
function str_opt_explode(id, by_num)
local nums = by_num and opt_index[id] or id
local t = nums and str_explode(nums, _opt_) or {}
if by_num then
for i=1,#t do
t[i] = tonumber(t[i])
end
end
return t
end
function get_opt_table(id)
local t = str_opt_explode(id, true)
if #t == 0 then
return {}
end
if #t == 1 then
return options[t[1]]
elseif #t == 2 then
return options[t[1]].gr[t[2]]
elseif #t == 3 then
return options[t[1]].gr[t[2]].gr[t[3]]
elseif #t == 4 then
return options[t[1]].gr[t[2]].gr[t[3]].gr[t[4]]
end
end
function check_opt_table(id)
local t = str_opt_explode(id, true)
return #t > 0
end
--------------------
function get(id)
if (#options == 0) then
init_opt_base()
end
local value = axr_main.config:r_value(opt_section, id, opt_val[id])
if (value ~= nil) then
--print_dbg("/Got axr_option [%s] = %s", id, value)
return value
end
-- Write in axr_main if it doesn't exist
local v = get_opt_table(id)
if v.cmd then
if v.val == 0 then
value = get_console_cmd(0, v.cmd)
elseif v.val == 1 then
value = get_console_cmd(1, v.cmd)
elseif v.val == 2 then
value = get_console_cmd(0, v.cmd) --get_console_cmd(2, v.cmd)
value = tonumber(value)
if v.min and v.max then
value = clamp(value, v.min, v.max)
end
value = round_idp(value, v.prec or precision)
end
elseif (type(v.def) == "table") then
value = exec(unpack(v.def))
axr_main.config:w_value(opt_section, id, value)
axr_main.config:save()
else
value = v.def
axr_main.config:w_value(opt_section, id, value)
axr_main.config:save()
end
--print_dbg("/Got option [%s] = %s", id, value)
if (value == nil) then
printe("!Found nil option value [%s]", id)
end
return value
end
function set(id, value)
axr_main.config:w_value(opt_section, id, value)
axr_main.config:save()
end
--------------------
--===========================================================
--//////////////////////// OPTIONS //////////////////////////
--===========================================================
class "UIOptions" (CUIScriptWnd)
function UIOptions:__init() super()
self.last_tree = {}
self.last_path = nil
self.last_curr_tree = nil
self._Cap = {}
self._Check = {}
self._List = {}
self._Input = {}
self._Track = {}
self._Radio = {}
-- Prepare the options table
if (#options == 0) then
init_opt_base()
end
self:InitControls()
self:InitCallBacks()
self:Reset()
end
function UIOptions:__finalize()
end
function UIOptions:InitControls()
self:SetWndRect (Frect():set(0,0,1024,768))
self:Enable (true)
self.xml = CScriptXmlInit()
local xml = self.xml
xml:ParseFile ("ui_options.xml")
self.background = xml:InitStatic("background", self)
self.dialog = xml:InitStatic("main", self)
xml:InitStatic("main:frame", self.dialog)
-- Buttons
self.btn_accept = xml:Init3tButton("main:btn_accept", self.dialog)
self:Register(self.btn_accept, "btn_accept")
self.btn_reset = xml:Init3tButton("main:btn_reset", self.dialog)
self:Register(self.btn_reset, "btn_reset")
self.btn_default = xml:Init3tButton("main:btn_default", self.dialog)
self:Register(self.btn_default, "btn_default")
self.btn_cancel = xml:Init3tButton("main:btn_cancel", self.dialog)
self:Register(self.btn_cancel, "btn_cancel")
-- Pending text
--xml:InitFrame("main:notify_frame", self.dialog)
self.pending = xml:InitTextWnd("main:notify", self.dialog)
-- Options lists
self.tree = {}
self.bl = {}
-- Options showcase
self.scroll_opt = xml:InitScrollView("main:scroll", self.dialog)
-- Presets
self.preset_cap = xml:InitStatic("main:cap_preset", self.dialog)
self.preset = xml:InitComboBox("main:preset",self.dialog)
self:Register(self.preset, "preset")
self.preset:Show(false)
self.preset_cap:Show(false)
-- Message box
self.message_box = CUIMessageBoxEx()
self:Register (self.message_box, "mb")
-- Hint Window
self.hint_wnd = utils_ui.UIHint(self)
end
function UIOptions:InitCallBacks()
self:AddCallback("btn_accept", ui_events.BUTTON_CLICKED, self.OnButton_Accept, self)
self:AddCallback("btn_reset", ui_events.BUTTON_CLICKED, self.OnButton_Reset, self)
self:AddCallback("btn_default", ui_events.BUTTON_CLICKED, self.OnButton_Default, self)
self:AddCallback("btn_cancel", ui_events.BUTTON_CLICKED, self.OnButton_Cancel, self)
self:AddCallback("preset", ui_events.LIST_ITEM_SELECT, self.Callback_Preset, self)
self:AddCallback("mb", ui_events.MESSAGE_BOX_YES_CLICKED, self.On_Discard, self)
--self:AddCallback("mb", ui_events.MESSAGE_BOX_NO_CLICKED, self.On_Discard,self)
end
function UIOptions:Update()
CUIScriptWnd.Update(self)
-- Show hint on hover
for id,ctrl in pairs(self._Cap) do
if ctrl:IsCursorOverWindow() then
local str = opt_str .. id .. "_desc"
local str_t = game.translate_string(str)
if (str ~= str_t) then
self.hint_wnd:Update(str_t)
end
return
end
end
self.hint_wnd:Update()
-- Hack to simulate tracing method for TrackBar value changes. TODO: add callback support for CUITrackBar in engine, this is just silly
for id,e in pairs(self._Track) do
if e.ctrl:IsCursorOverWindow() then
local v = self:GetOption(id)
local value = round_idp(e.ctrl:GetFValue(), v.prec or precision)
if (value ~= e.value) then
e.value = value
self:Callback_Track(e.txt, e.path, e.opt, v, value)
return
end
end
end
end
function UIOptions:Reset()
-- Clear all trees
for i=1,3 do
if self.tree[i] then
if type(self.tree[i]) == table then
for j=1,#self.tree[i] do
self.tree[i][j]:Clear()
end
else
self.tree[i]:Clear()
end
end
end
self:Register_Tree(1, "", options, 1)
end
function UIOptions:Reset_opt(curr_tree, path, flags)
flags = flags or {}
local xml = self.xml
self.scroll_opt:Clear()
-- If options tree has a precondition that must be met, don't show it if it returns false
if curr_tree.precondition and (not exec(unpack(curr_tree.precondition))) then
if curr_tree.output then
local _txt = xml:InitTextWnd("elements:block", nil)
_txt:SetText( game.translate_string(curr_tree.output) )
self.scroll_opt:AddWindow(_txt, true)
_txt:SetAutoDelete(false)
end
else
-- Keybinds
if (self.dlg_controls == nil) then
self.dlg_controls = opt_controls()
self.dlg_controls:InitControls(0,0, xml, self)
self.dlg_controls:Show (false)
self.dialog:AttachChild (self.dlg_controls)
xml:InitWindow ("tab_size", 0, self.dlg_controls)
local opt = COptionsManager()
opt:SetCurrentValues("key_binding")
opt:SaveBackupValues("key_binding")
end
if (path == "control/keybind") then
self.dlg_controls:Show(true)
self.Keybinds_Shown = true
else
self.dlg_controls:Show(false)
end
-- Presets
self:Register_Preset(curr_tree)
if curr_tree.apply_to_all and curr_tree.id_gr then
flags.apply_to_all = true
flags.group = curr_tree.id_gr
else
flags.apply_to_all = nil
end
empty_table(self._Cap)
empty_table(self._Check)
empty_table(self._List)
empty_table(self._Input)
empty_table(self._Track)
empty_table(self._Radio)
for i=1,#curr_tree.gr do
-- Check preconditions
local to_hide = curr_tree.gr[i].precondition and (not exec(unpack(curr_tree.gr[i].precondition)))
for j=1,10 do -- support for 10 preconditions
if (not curr_tree.gr[i]["precondition_" .. j]) then
break
elseif (not exec(unpack(curr_tree.gr[i]["precondition_" .. j]))) then
to_hide = true
break
end
end
if (not to_hide) then
local opt = curr_tree.gr[i].id
local v = curr_tree.gr[i]
local _st = xml:InitStatic("main:st", nil)
local _h = 0
----------- Support
if (v.type == "line") then
_h = self:Register_Line(xml, _st)
elseif (v.type == "image") then
_h = self:Register_Image(xml, _st, v)
elseif (v.type == "slide") then
_h = self:Register_Slide(xml, _st, v)
elseif (v.type == "title") then
_h = self:Register_Title(xml, _st, v)
elseif (v.type == "desc") then
_h = self:Register_Desc(xml, _st, v)
----------- Option
elseif (v.type == "check") then
_h = self:Register_Check(xml, _st, path, opt, v, flags)
elseif (v.type == "button") then
_h = self:Register_Button(xml, _st, path, opt, v, flags)
elseif (v.type == "list") then
_h = self:Register_List(xml, _st, path, opt, v, flags)
elseif (v.type == "input") then
_h = self:Register_Input(xml, _st, path, opt, v, flags)
elseif (v.type == "track") then
_h = self:Register_Track(xml, _st, path, opt, v, flags)
elseif (v.type == "radio_h") then
_h = self:Register_Radio(xml, _st, path, opt, v, true, flags)
elseif (v.type == "radio_v") then
_h = self:Register_Radio(xml, _st, path, opt, v, false, flags)
end
_st:SetWndSize(vector2():set(_st:GetWidth(), _h + 10))
self.scroll_opt:AddWindow(_st, true)
_st:SetAutoDelete(true)
end
end
if self.Save_AXR then
self.Save_AXR = false
axr_main.config:save()
end
end
end
function UIOptions:Reset_last_opt()
Register_UI("UIOptions")
if self.last_curr_tree and self.last_path then
self:Reset_opt(self.last_curr_tree, self.last_path)
self:UpdatePending()
end
end
------------------------------------------------------------
-- Elements
------------------------------------------------------------
function UIOptions:Register_Cap(xml, handler, id, hint)
id = s_gsub(id, _opt_, "_")
self._Cap[id] = xml:InitStatic("elements:cap",handler)
self._Cap[id]:TextControl():SetText( game.translate_string(opt_str .. (hint or id)) )
return self._Cap[id]:GetHeight()
end
function UIOptions:Register_Line(xml, handler)
local line = xml:InitStatic("elements:line",handler)
return (line:GetHeight() + 10)
end
function UIOptions:Register_Image(xml, handler, v)
local pic = xml:InitStatic("elements:image",handler)
if v.link then
if (v.pos) then
local pos = pic:GetWndPos()
pic:SetWndPos(vector2():set( pos.x + v.pos[1] , pos.y + v.pos[2] ))
end
if (v.size) then
pic:SetWndSize(vector2():set( v.size[1] , v.size[2] ))
end
pic:InitTexture(v.link)
pic:SetStretchTexture(v.stretch and true or false)
end
return pic:GetHeight()
end
function UIOptions:Register_Slide(xml, handler, v)
local frame = xml:InitStatic("elements:slide", handler)
local _pos = frame:GetWndPos()
frame:SetWndPos(vector2():set( _pos.x , _pos.y + (v.spacing or 20) ))
local pic = xml:InitStatic("elements:slide:pic", frame)
if v.link then
pic:InitTexture(v.link)
pic:SetStretchTexture(true)
if (v.pos) then
local pos = pic:GetWndPos()
pic:SetWndPos(vector2():set( pos.x + v.pos[1] , pos.y + v.pos[2] ))
end
if (v.size) then
pic:SetWndSize(vector2():set( v.size[1] * width_factor , v.size[2] ))
end
pic:InitTexture(v.link)
end
local txt = xml:InitTextWnd("elements:slide:txt", frame)
if v.text then
txt:SetText( game.translate_string(v.text) )
end
xml:InitStatic("elements:slide:line_1", frame)
xml:InitStatic("elements:slide:line_2", frame)
return (pic:GetHeight() + 20)
end
function UIOptions:Register_Title(xml, handler, v)
local title = xml:InitTextWnd("elements:title_" .. (v.align or "l"), handler)
title:SetText( game.translate_string(v.text) )
title:AdjustHeightToText()
title:SetWndSize(vector2():set(title:GetWidth(), title:GetHeight() + 20))
if v.clr and v.clr[4] then
title:SetTextColor( GetARGB(v.clr[1], v.clr[2], v.clr[3], v.clr[4]) )
end
return title:GetHeight()
end
function UIOptions:Register_Desc(xml, handler, v)
local desc = xml:InitTextWnd("elements:desc", handler)
desc:SetText( game.translate_string(v.text) )
desc:AdjustHeightToText()
desc:SetWndSize(vector2():set(desc:GetWidth(), desc:GetHeight() + 20))
if v.clr and v.clr[4] then
desc:SetTextColor( GetARGB(v.clr[1], v.clr[2], v.clr[3], v.clr[4]) )
end
return desc:GetHeight()
end
function UIOptions:Register_Check(xml, handler, path, opt, v, flags)
local id = cc(path , opt)
-- Caption
local h = self:Register_Cap(xml, handler, id, v.hint)
-- Apply to all button
if flags.apply_to_all and flags.group then
self:Register_BtnAll(xml, handler, path, opt, v, flags)
end
-- Create control
local ctrl = xml:InitCheck("elements:check",handler)
if (ctrl:GetHeight() > h) then
h = ctrl:GetHeight()
end
-- Get values
local value = self:GetValue(path, opt, v, flags)
ctrl:SetCheck(value)
-- Register
local id_ctrl = self:Stacker(path, opt, v)
self:Register(ctrl, id_ctrl)
local _wrapper = function(handler) -- we need wrapper in order to pass ctrl to method
self:Callback_Check(ctrl, path, opt, v)
end
self:AddCallback(id_ctrl, ui_events.BUTTON_CLICKED, _wrapper, self)
return h
end
function UIOptions:Callback_Check(ctrl, path, opt, v)
local value = ctrl:GetCheck()
self:CacheValue(path, opt, value, v)
end
function UIOptions:Register_Button(xml, handler, path, opt, v, flags)
local id = cc(path , opt)
--[[ Apply to all button
if flags.apply_to_all and flags.group then
self:Register_BtnAll(xml, handler, path, opt, v, flags)
end
--]]
xml:InitFrame("elements:frame_button", handler)
-- Create control
local ctrl = xml:Init3tButton("elements:btn_button", handler)
local h = ctrl:GetHeight()
-- Caption
local id_cap = s_gsub(id, _opt_, "_")
self._Cap[id_cap] = xml:InitStatic("elements:cap_button",handler)
self._Cap[id_cap]:TextControl():SetText( game.translate_string(opt_str .. (v.hint or id_cap)) )
if (self._Cap[id_cap]:GetHeight() > h) then
h = self._Cap[id_cap]:GetHeight()
end
-- Register
local id_ctrl = self:Stacker(path, opt, v)
self:Register(ctrl, id_ctrl)
local _wrapper = function(handler) -- we need wrapper in order to pass ctrl to method
self:Callback_Button(ctrl, path, opt, v)
end
self:AddCallback(id_ctrl, ui_events.BUTTON_CLICKED, _wrapper, self)
return h
end
function UIOptions:Callback_Button(ctrl, path, opt, v)
if v.functor_ui then
local id = cc(path , opt)
print_dbg("- Executing functor_ui of [%s]",id)
exec(unpack(v.functor_ui),self)
end
if v.functor then
local id = cc(path , opt)
print_dbg("- Executing functor of [%s]",id)
exec(unpack(v.functor))
end
end
function UIOptions:Register_List(xml, handler, path, opt, v, flags)
local id = cc(path , opt)
-- Caption
local h = self:Register_Cap(xml, handler, id, v.hint)
-- Apply to all button
if flags.apply_to_all and flags.group then
self:Register_BtnAll(xml, handler, path, opt, v, flags)
end
-- Create control
local ctrl = xml:InitComboBox("elements:list",handler)
if (ctrl:GetHeight() > h) then
--h = ctrl:GetHeight()
end
-- Get values
local idx
local value = self:GetValue(path, opt, v, flags)
local content = self:GetContent(path, opt, v)
-- Setup
for i=1,#content do
local str_2 = content[i][2] or tostring(content[i][1])
local str = v.no_str and str_2 or game.translate_string(opt_str_lst .. str_2)
ctrl:AddItem( game.translate_string(str), i)
if content[i][1] == value then
idx = i
end
end
idx = idx or 1
local str_2 = content[idx][2] or tostring(content[idx][1])
local str = v.no_str and str_2 or game.translate_string(opt_str_lst .. str_2)
ctrl:enable_id( idx )
ctrl:SetText( game.translate_string(str) )
-- Register
local id_ctrl = self:Stacker(path, opt, v)
self:Register(ctrl, id_ctrl)
local _wrapper = function(handler) -- we need wrapper in order to pass ctrl to method
self:Callback_List(ctrl, path, opt, v)
end
self:AddCallback(id_ctrl, ui_events.LIST_ITEM_SELECT, _wrapper, self)
return h
end
function UIOptions:Callback_List(ctrl, path, opt, v)
local i = ctrl:CurrentID()
local content = self:GetContent(path, opt, v)
self:CacheValue(path, opt, content[i][1], v)
end
function UIOptions:Register_Input(xml, handler, path, opt, v, flags)
local id = cc(path , opt)
-- Caption
local h = self:Register_Cap(xml, handler, id, v.hint)
-- Apply to all button
if flags.apply_to_all and flags.group then
self:Register_BtnAll(xml, handler, path, opt, v, flags)
end
-- Create control
local ctrl = xml:InitEditBox("elements:input",handler)
if (ctrl:GetHeight() > h) then
h = ctrl:GetHeight()
end
-- Get values
local value = self:GetValue(path, opt, v, flags)
ctrl:SetText(value)
-- Register
local id_ctrl = self:Stacker(path, opt, v)
self:Register(ctrl, id_ctrl)
local _wrapper = function(handler) -- we need wrapper in order to pass ctrl to method
self:Callback_Input(ctrl, path, opt, v)
end
self:AddCallback(id_ctrl, ui_events.EDIT_TEXT_COMMIT, _wrapper, self)
return h
end
function UIOptions:Callback_Input(ctrl, path, opt, v)
local value = ctrl:GetText()
if not (value and value ~= "") then
ctrl:SetText( self:GetCurrentValue(path, opt, v) or self:GetDefaultValue(path, opt, v) )
return
end
if (v.val == 1) then
value = tonumber(value)
if (not value) then
ctrl:SetText( self:GetCurrentValue(path, opt, v) or self:GetDefaultValue(path, opt, v) )
return
end
value = clamp(value, v.min, v.max)
end
self:CacheValue(path, opt, value, v)
ctrl:SetText(value)
end
function UIOptions:Register_Track(xml, handler, path, opt, v, flags)
local id = cc(path , opt)
-- Caption
local h = self:Register_Cap(xml, handler, id, v.hint)
-- Apply to all button
if flags.apply_to_all and flags.group then
self:Register_BtnAll(xml, handler, path, opt, v, flags)
end
-- Create control
self._Track[id] = {}
self._Track[id].ctrl = xml:InitTrackBar("elements:track",handler)
self._Track[id].path = path
self._Track[id].opt = opt
if (self._Track[id].ctrl:GetHeight() > h) then
h = self._Track[id].ctrl:GetHeight()
end
self._Track[id].txt = xml:InitTextWnd("elements:track_value",handler)
-- Get values
local value = self:GetValue(path, opt, v, flags)
value = clamp(value, v.min, v.max)
value = round_idp(value, v.prec or precision)
local int = false --is_int(value) and is_int(v.step) and is_int(v.min) and is_int(v.max)
self._Track[id].value = value -- temp
self._Track[id].ctrl:SetInvert(v.invert and true or false)
self._Track[id].ctrl:SetStep(v.step)
if int then
self._Track[id].ctrl:SetOptIBounds(v.min, v.max)
self._Track[id].ctrl:SetIValue(value)
else
self._Track[id].ctrl:SetOptFBounds(v.min, v.max)
self._Track[id].ctrl:SetFValue(value)
end
if (not v.no_str) then
self._Track[id].txt:SetText(value)
end
return h
end
function UIOptions:Callback_Track(ctrl, path, opt, v, value)
if (not v.no_str) then
ctrl:SetText(value)
end
self:CacheValue(path, opt, value, v)
end
function UIOptions:Register_Radio(xml, handler, path, opt, v, typ, flags)
local id = cc(path , opt)
-- Caption
local h = self:Register_Cap(xml, handler, id, v.hint)
-- Apply to all button
if flags.apply_to_all and flags.group then
self:Register_BtnAll(xml, handler, path, opt, v, flags)
end
-- Determine type
local str = typ and "horz" or "vert"
local content = self:GetContent(path, opt, v)
local num = #content
if num > 8 and (not v.force_horz) then
typ = false
str = "vert"
end
-- Create control
local frame = xml:InitStatic("elements:radio_" .. str, handler)
local ctrl = {}
local txt
local offset = typ and m_floor(frame:GetWidth()/num) or 30
local h_factor = typ and 1 or 0
local v_factor = typ and 0 or 1
local h1, h2 = 0, 0
--printf("offset: %s - h_factor: %s - v_factor: %s - num: %s", offset, h_factor, v_factor, num)
for i=1,num do
-- Buttons
ctrl[i] = xml:InitCheck("elements:radio_" .. str .. ":btn", frame)
local pos = ctrl[i]:GetWndPos()
h1 = (h1 * v_factor) + ctrl[i]:GetHeight()
ctrl[i]:SetWndPos(vector2():set( pos.x + ((i-1) * offset * h_factor) , pos.y + ((i-1) * offset * v_factor) ))
-- Text
txt = xml:InitTextWnd("elements:radio_" .. str .. ":txt", frame)
local pos2 = txt:GetWndPos()
h2 = h_factor * txt:GetHeight()
txt:SetWndPos(vector2():set( pos2.x + ((i-1) * offset * h_factor) , pos2.y - (v_factor * 30) + ((i-1) * offset * v_factor) ))
local str_2 = content[i][2] or tostring(content[i][1])
local str = v.no_str and game.translate_string(str_2) or game.translate_string(opt_str_lst .. str_2)
txt:SetText( str )
if (h1 + h2 > h) then
h = h1 + h2
end
end
-- Get values
local value = self:GetValue(path, opt, v, flags)
local id_ctrl = self:Stacker(path, opt, v)
for i=1,num do
if (content[i][1] == value) then
ctrl[i]:SetCheck(true)
else
ctrl[i]:SetCheck(false)
end
-- Register
self:Register(ctrl[i], id_ctrl .. i)
local _wrapper = function(handler) -- we need wrapper in order to pass ctrl to method
self:Callback_Radio(ctrl, path, opt, v, i)
end
self:AddCallback(id_ctrl .. i, ui_events.BUTTON_CLICKED, _wrapper, self)
end
return h
end
function UIOptions:Callback_Radio(ctrl, path, opt, v, n)
local value = ctrl[n]:GetCheck()
--printf("n = %s", n)
if value then
for i=1,#ctrl do
if i ~= n then
ctrl[i]:SetCheck(false)
end
end
local content = self:GetContent(path, opt, v)
self:CacheValue(path, opt, content[n][1], v)
else
ctrl[n]:SetCheck(true)
end
end
function UIOptions:Register_BtnAll(xml, handler, path, opt, v, flags)
local ctrl = xml:Init3tButton("elements:btn_all",handler)
xml:InitStatic("elements:cap_all",handler)
local id_ctrl = self:Stacker(path, opt, v)
self:Register(ctrl, id_ctrl)
local _wrapper = function(handler) -- we need wrapper in order to pass ctrl to method
self:Callback_BtnAll(ctrl, path, opt, v, flags)
end
self:AddCallback(id_ctrl, ui_events.BUTTON_CLICKED, _wrapper, self)
end
function UIOptions:Callback_BtnAll(ctrl, path, opt, v, flags)
local id = cc(path , opt)
local group = flags.group
local value = self:GetValue(path, opt, v, flags)
-- Set same value for identical options of same group
local function set_in_group(p, group, path, opt, value)
print_dbg("~set_in_group | current path: %s - target opt: %s", path, opt)
for i=1,#p do
local path_ext = path and (path ~= "") and cc(path , p[i].id) or p[i].id
if p[i].sh then
print_dbg("~set_in_group | current path: %s - target opt: %s", path_ext, opt)
if (p[i].id_gr == group) then
local gr = p[i].gr
for j=1,#gr do
if gr[j].id == opt then
local id_ext = cc(path_ext , opt)
if check_opt_table(id_ext) then
print_dbg("-set_in_group | Found match: %s", id_ext)
self:CacheValue(path_ext, opt, value, gr[j])
end
end
end
end
else
set_in_group(p[i].gr, group, path_ext, opt, value)
end
end
end
set_in_group(options, group, "", opt, value)
end
function UIOptions:Register_Preset(ct)
if ct.presets then
self.preset:ClearList()
--
for i=1,#ct.presets do
self.preset:AddItem( game.translate_string(opt_str_prst .. ct.presets[i]), i)
end
if (ct.curr_preset) then
self.preset:SetText( game.translate_string(opt_str_prst .. ct.curr_preset) )
end
self.preset:Show(true)
self.preset_cap:Show(true)
else
self.preset:ClearList()
self.preset:Show(false)
self.preset_cap:Show(false)
end
end
function UIOptions:Callback_Preset()
if not (self.last_curr_tree and self.last_path) then
return
end
local txt = self.preset:GetText()
if not (txt and txt ~= "") then
return
end
-- Retrieve the preset section
local pres
local presets = self.last_curr_tree.presets
for i=1,#presets do
if game.translate_string(opt_str_prst .. presets[i]) == txt then
pres = presets[i]
break
end
end
if pres and ini_pres:section_exist(pres) then
self.last_curr_tree.curr_preset = pres
--self:Reset_opt(self.last_curr_tree, self.last_path, { preset = pres })
local n = ini_pres:line_count(pres)
local result, id, value
for i=0,n-1 do
result, id, value = ini_pres:r_line_ex(pres,i,"","")
-- Validate option
local v = get_opt_table(id)
if v and v.type then
-- No need to modify options that can't be seen
local to_hide = v.precondition and (not exec(unpack(v.precondition)))
if (not to_hide) then
-- Get proper value
if v.val == 0 then
elseif v.val == 1 then
value = (value == "true") and true or false
elseif v.val == 2 then
value = tonumber(value)
end
-- Extract path and opt
local t = str_opt_explode(id)
local opt = t[#t]
local path = t[1]
for i=2,#t-1 do
path = cc(path , t[i])
end
-- Cache changes
self:CacheValue(path, opt, value, v)
end
end
end
-- Update XML elements
self:Reset_opt(self.last_curr_tree, self.last_path)
-- Update state
self:UpdatePending()
end
end
function UIOptions:Register_Tree(tr, path, group, idx)
print_dbg("-Register_Tree | tr: %s - path: %s", tr, path)
local xml = self.xml
if (not self.tree[tr]) then
self.tree[tr] = {}
end
if (not self.tree[tr][path]) then
self.tree[tr][path] = xml:InitScrollView("main:tree_" .. tr, self.dialog)
--[[
local pos = self.tree[tr][path]:GetWndPos()
if tr == 3 then idx = 1 end
self.tree[tr][path]:SetWndPos(vector2():set( pos.x , pos.y + (25*(idx-1)) ))
--]]
if (not self.bl[tr]) then
self.bl[tr] = {}
end
self.bl[tr][path] = {}
-- Fill tree
for i=1,#group do
local _st = xml:InitStatic("main:st_tree", nil)
self.bl[tr][path][i] = xml:InitCheck("elements:btn_list", _st)
self.bl[tr][path][i]:SetCheck(false)
local txt = xml:InitTextWnd("elements:txt_list", _st)
txt:SetText( game.translate_string(opt_str_menu .. group[i].id) )
txt:SetTextColor( clr_tree[tr] )
self.tree[tr][path]:AddWindow(_st, true)
_st:SetAutoDelete(false)
end
-- Set Callback for tree buttons
for i=1,#self.bl[tr][path] do
local path_i = (path ~= "") and cc(path , group[i].id) or group[i].id
self:Register(self.bl[tr][path][i], ("tree_"..path_i))
local _wrapper = function(handler) -- we need wrapper in order to pass ctrl to method
self:Callback_Tree(tr, path_i, group[i], self.bl[tr][path], i)
end
self:AddCallback(("tree_"..path_i), ui_events.BUTTON_CLICKED, _wrapper, self)
end
end
self.tree[tr][path]:Show(true)
self.bl[tr][path][1]:SetCheck(true)
local path_1 = (path ~= "") and cc(path , group[1].id) or group[1].id
self:Callback_Tree(tr, path_1, group[1], self.bl[tr][path], 1)
end
function UIOptions:Callback_Tree(tr, path, group, ctrl, i)
print_dbg("-Callback_Tree | tr: %s - path: %s - index: %s", tr, path, i)
-- Radio buttons behavior
if (ctrl[i]:GetCheck() == false) then
ctrl[i]:SetCheck(true)
return
end
for k=1,#ctrl do
if k ~= i then
ctrl[k]:SetCheck(false)
end
end
-- Hide all sub trees
for k=tr+1,#self.tree do
for _,v in pairs(self.tree[k]) do
v:Show(false)
end
end
-- If its an option list, show it
if group.sh then
self:Reset_opt(group, path)
-- Caching current options
self.last_path = path
if (not self.last_curr_tree) then
self.last_curr_tree = {}
end
empty_table(self.last_curr_tree)
copy_table(self.last_curr_tree, group)
else
self:Register_Tree(tr+1, path, group.gr, i)
end
end
------------------------------------------------------------
-- Utilities
------------------------------------------------------------
function UIOptions:GetValue(path, opt, v, flags)
-- NOTE: make sure to check for nil values only, since false exists as legit value for commands and check boxes
local value
if flags and flags.def then
value = self:GetDefaultValue(path, opt, v)
elseif flags and flags.preset then
local pres = flags.preset
local id = cc(path , opt)
if v.val == 0 then
value = ini_pres:r_string_ex(pres, id)
elseif v.val == 1 then
value = ini_pres:r_bool_ex(pres, id)
elseif v.val == 2 then
value = ini_pres:r_float_ex(pres, id)
end
end
if (value ~= nil) or (flags and flags.def) then
if (value ~= nil) and (v.type == "track") then
value = clamp(value, v.min, v.max)
end
self:CacheValue(path, opt, value, v)
end
if (value == nil) then
value = self:GetCurrentValue(path, opt, v)
end
if (value == nil) then
value = self:GetDefaultValue(path, opt, v)
end
if (value ~= nil) and (v.type == "track") then
value = clamp(value, v.min, v.max)
end
return value
end
function UIOptions:GetDefaultValue(path, opt, v)
local id = cc(path , opt)
local value
if (type(v.def) == "table") then
value = exec(unpack(v.def))
else
value = v.def
end
-- We cache default values for the first time, so current values rely on them up later
-- because some default values are randomized and player might not touch them, thus causing randomized effects where they are used in-game
if (axr_main.config:r_value(opt_section, id, v.val) == nil) and (value ~= nil) then
axr_main.config:w_value(opt_section, id, value)
self.Save_AXR = true
end
return value
end
function UIOptions:GetCurrentValue(path, opt, v)
local id = cc(path , opt)
if (opt_temp[id] ~= nil) then
local _id = s_gsub(id, _opt_, "_")
if self._Cap[_id] and self._Cap[_id]:IsShown() then
self._Cap[_id]:TextControl():SetTextColor( clr_o )
end
return opt_temp[id]
end
local value
if v.curr then
value = exec(unpack(v.curr))
elseif v.cmd then
if v.val == 0 then
value = get_console_cmd(0, v.cmd)
elseif v.val == 1 then
value = get_console_cmd(1, v.cmd)
elseif v.val == 2 then
value = get_console_cmd(0, v.cmd) --get_console_cmd(2, v.cmd) -- some commands are integers, using get_float will return 0. This is a walkaround
value = tonumber(value)
if v.min and v.max then
value = clamp(value, v.min, v.max)
end
value = round_idp(value, v.prec or precision)
end
else
value = axr_main.config:r_value(opt_section, id, v.val)
end
return value
end
function UIOptions:GetContent(path, opt, v)
if v.cmd and (not v.content) then
local value
if v.val == 0 then
value = get_console_cmd(0, v.cmd)
elseif v.val == 1 then
value = get_console_cmd(1, v.cmd)
elseif v.val == 2 then
value = get_console_cmd(0, v.cmd) --get_console_cmd(2, v.cmd)
value = tonumber(value)
if v.min and v.max then
value = clamp(value, v.min, v.max)
end
value = round_idp(value, v.prec or precision)
end
return {{value,tostring(value)}}
elseif (type(v.content[1]) == "function") then
return exec(unpack(v.content))
else
return v.content
end
end
function UIOptions:GetOption(id)
local t = str_explode(id,_opt_ )
local v = options
for i=1,#t do
for j=1,#v do
if v[j].id == t[i] then
if i == #t then
v = v[j]
else
v = v[j].gr
end
break
end
end
end
return v
end
function UIOptions:CacheValue(path, opt, value, v)
local id = cc(path , opt)
-- Do a backup of current values first
if (opt_backup[id] == nil) then
opt_backup[id] = self:GetValue(path, opt, v)
print_dbg("# Backup [%s] = %s", id, opt_backup[id])
end
-- Cache changed values
if (value ~= nil) and (value ~= opt_backup[id]) then
opt_temp[id] = value
print_dbg("/ Cached [%s] = %s", id, value)
else
opt_temp[id] = nil -- no need to cache current values
print_dbg("~ Cleared cache [%s]", id)
end
-- Change text color
local _id = s_gsub(id, _opt_, "_")
if self._Cap[_id] and self._Cap[_id]:IsShown() then
if (opt_temp[id] ~= nil) and (opt_temp[id] ~= opt_backup[id]) then
self._Cap[_id]:TextControl():SetTextColor( clr_o )
else
self._Cap[_id]:TextControl():SetTextColor( clr_g1 )
end
end
-- Update state
self:UpdatePending()
end
function UIOptions:Stacker(path, opt, v)
-- This assure that each time a control is created, an unique callback id is given to it
-- Why? because in normal case, jumping between options removes the previous ones constantly, getting back to them will create new controls and assign them to the old ids
-- This is bad because callbacks are still attached to the old controls, any fresh controls that get assigned to those ids will be inactive as a result
-- My solution is this function to generate unique id each time a control is created
if (not v.stack) then v.stack = 0 end
v.stack = v.stack + 1
return cc( cc(path , opt) , v.stack)
end
function UIOptions:UpdatePending()
local size = size_table(opt_temp)
if size > 0 then
self.pending:SetText( strformat( game.translate_string("ui_mm_warning_pending"), size) )
else
self.pending:SetText("")
end
end
------------------------------------------------------------
-- Callbacks
------------------------------------------------------------
function UIOptions:OnButton_Accept()
--if self.Need_VidRestart then
-- self.message_box:InitMessageBox("message_box_yes_no")
-- self.message_box:SetText(string.format("%s %d% s", game.translate_string("ui_mm_confirm_changes"), 15, game.translate_string("mp_si_sec")))
-- self.message_box:ShowDialog(true)
--else
self:On_Accept()
--end
end
function UIOptions:OnButton_Reset()
if self.last_path and self.last_curr_tree and is_not_empty(opt_temp) then
local to_reset
for id, val in pairs(opt_temp) do
if s_find(id,self.last_path) then
to_reset = true
opt_temp[id] = nil
local _id = s_gsub(id, _opt_, "_")
if self._Cap[_id] and self._Cap[_id]:IsShown() then
self._Cap[_id]:TextControl():SetTextColor( clr_g1 )
end
end
end
if (to_reset) then
self:UpdatePending()
self:Reset_opt(self.last_curr_tree, self.last_path)
end
end
if self.dlg_controls and self.dlg_controls:IsShown() then
local opt = COptionsManager()
opt:SetCurrentValues ("key_binding")
end
end
function UIOptions:OnButton_Default()
if self.last_path and self.last_curr_tree then
self:Reset_opt(self.last_curr_tree, self.last_path, { def = true })
end
if self.dlg_controls and self.dlg_controls:IsShown() then
exec_console_cmd("default_controls")
local opt = COptionsManager()
opt:SetCurrentValues ("key_binding")
end
end
function UIOptions:OnButton_Cancel()
if is_not_empty(opt_temp) then
self.message_box:InitMessageBox("message_box_yes_no")
self.message_box:SetText(game.translate_string("ui_mm_discard_changes"))
self.message_box:ShowDialog(true)
else
self:On_Cancel()
end
end
function UIOptions:On_Accept()
for id, val in pairs(opt_temp) do
local v = self:GetOption(id)
-- Cache the changes
if (not v.curr) then
print_dbg("- Saved [%s] := %s", id, val)
axr_main.config:w_value(opt_section, id, val)
self.Save_AXR = true
end
self.Change_Done = true
-- Execute functors if found
if v.functor then
if v.postcondition then
if exec(unpack(v.postcondition))then
print_dbg("- Executing postcondition functor of [%s]",id)
exec(unpack(v.functor))
end
else
print_dbg("- Executing functor of [%s]",id)
exec(unpack(v.functor))
end
end
-- See if it needs restart
if v.restart then
self.Need_Restart = true
end
if v.vid then
self.Need_VidRestart = true
end
-- Send callback and apply changes
if v.cmd then
local cmd_value = val
if type(cmd_value) == "boolean" then
if v.bool_to_num then
cmd_value = cmd_value and "1" or "0"
else
cmd_value = cmd_value and "on" or "off"
end
end
print_dbg("- Saved CMD [%s] := %s", id, cmd_value)
exec_console_cmd(v.cmd .. " " .. cmd_value)
self.Save_CFG = true
end
end
-- Save axr_options
if self.Save_AXR then
axr_main.config:save()
self.Save_AXR = false
end
-- Save keybinds
if self.Keybinds_Shown then
local opt = COptionsManager()
opt:SaveValues("key_binding")
opt:OptionsPostAccept()
self.Save_CFG = true
end
print_dbg("~ Change done: %s | Game restart: %s | Vid restart: %s | Save AXR: %s | Save CFG: %s | Keybinds: %s", self.Change_Done, self.Need_Restart, self.Need_VidRestart, self.Save_AXR, self.Save_CFG, self.Keybinds_Shown)
-- appdata
if self.Save_CFG then
print_dbg("- Saved CFG")
exec_console_cmd("cfg_save")
--exec_console_cmd("cfg_save tmp")
end
if level.present() and self.Change_Done then
print_dbg("% Sent callback (on_option_change)")
SendScriptCallback("on_option_change")
end
-- Clear cache
empty_table(opt_temp)
empty_table(opt_backup)
-- Exit
self:On_Cancel()
end
function UIOptions:On_Cancel()
self.owner:ShowDialog(true)
self:HideDialog()
self.owner:Show(true)
-- Restart vid
if self.Need_VidRestart then
exec_console_cmd("vid_restart")
end
if self.Need_Restart then
self.owner:SetMsg( game.translate_string("ui_mm_change_done_restart") , 7 )
self.message_box:InitMessageBox("message_box_restart_game")
self.message_box:ShowDialog(true)
elseif self.Change_Done then
self.owner:SetMsg( game.translate_string("ui_mm_change_done") , 5 )
end
self.Change_Done = false
self.Need_VidRestart = false
self.Need_Restart = false
self.Save_CFG = false
self.Keybinds_Shown = false
Unregister_UI("UIOptions")
end
function UIOptions:On_Discard()
empty_table(opt_temp)
if (self.last_path and self.last_curr_tree) then
self:UpdatePending()
self:Reset_opt(self.last_curr_tree, self.last_path)
end
self:On_Cancel()
end
function UIOptions:OnKeyboard(dik, keyboard_action)
local res = CUIScriptWnd.OnKeyboard(self,dik,keyboard_action)
if (res == false) then
local bind = dik_to_bind(dik)
if keyboard_action == ui_events.WINDOW_KEY_PRESSED then
if dik == DIK_keys.DIK_ESCAPE then
self:OnButton_Cancel()
end
end
end
return res
end
------------------------------------------------------------
-- Keybinds
------------------------------------------------------------
class "opt_controls" (CUIWindow)
function opt_controls:__init() super()
end
function opt_controls:__finalize()
end
function opt_controls:InitControls(x, y, xml, handler)
self:SetWndPos(vector2():set(x,y))
self:SetWndSize(vector2():set(738,416))
self:SetAutoDelete(true)
-- self.bk = xml:InitFrame("frame", self)
xml:InitFrameLine ("tab_controls:cap_keyboardsetup", self)
xml:InitFrameLine ("tab_controls:cap_keyboardsetup", self)
xml:InitKeyBinding ("tab_controls:key_binding", self)
end
function trader_cond(x)
if x == 'get' then
-- printf('@@@ returning %s', alife_storage_manager.get_state().trader_buy_condition_override or "0 (DEFAULT)")
return alife_storage_manager.get_state().trader_buy_condition_override or 0
else
-- printf('@@@ setting %s', opt_temp["gameplay/economy_diff/condition_buy_override"] or '0 (DEFAULT)')
alife_storage_manager.get_state().trader_buy_condition_override = opt_temp["gameplay/economy_diff/condition_buy_override"] or 0
end
end
------------------------------------------------------------
-- Tutorial: How to add new options:
------------------------------------------------------------
--[[
------------------------------------------------------------------------------------------------
Option name:
script will read option name (id) and show it automatically, naming should be like this: "ui_mm_[tree_1]_[tree_2]_[tree_...]_[option]"
[tree_n] and [option] are detemined from option path inside the table
Example: options["video"]["general"]["renderer"] name will come from "ui_mm_video_general_renderer" string
------------------------------------------------------------------------------------------------
Option description:
option description can show up in the hint window if its defined by its name followed by "_desc"
Example: option with a name of "ui_mm_video_general_renderer" will show its hint if "ui_mm_video_general_renderer_desc" exists
------------------------------------------------------------------------------------------------
Parameters of option Tree:
------------------------------------------------------------------------------------------------
- [id]
- Define: (string)
To give a tree its own identity
- [sh]
- Define: (boolean)
used to detemine that the sub-tree tables are actual list of options to set and show
- [precondition]
- Define: ( table {function, parameters} )
don't show tree options if its precondition return false
- [output]
- Define: (string)
Text to show when precondition fails
- [gr]
- Define: ( table { ... } )
Table of a sub-tree or options list
- [apply_to_all]
- Define: (boolean)
when you have options trees with similar options and group, you can use this to add "Apply to All" button to each option
clicking it will apply option changes to this option in all other trees from same group
you must give these a tree a group id
- [id_gr]
- Define: (string)
allows you to give options tree a group id, to connect them when you want to use "Apply to all" button for options
------------------------------------------------------------------------------------------------
Parameters of options:
------------------------------------------------------------------------------------------------
----------------------
Critical parameters:
--------------------
These parameters must be declared for elements
[id]
- Define: (string)
Option identity/name.
Option get stored in axr_main or called in other sripts by its path (IDs of sub trees and option):
Example: ( tree_1_id/tree_2_id/.../option_id )
[type]
- Define: (string)
- Possible values:
- Option elements:
"check" : Option, check box, either ON or OFF
"list" : Option, list of strings, useful for options with too many selections
"input" : Option, input box, you can type a value of your choice
"radio_h" : Option, radio box, select one out of many choices. Can fit up to 8 selections (Horizental layout)
"radio_v" : Option, radio box, select one out of many choices. Can fit up any number of selections (Vertical layout)
"track" : Option, track bar, easy way to control numric options with min/max values (can be used only if [val] = 2)
- Support elements:
"line" : Support element, a simple line to separate things around
"image" : Support element, 563x50 px image box, full-area coverage
"slide" : Support element, image box on left, text on right
"title" : Support element, title (left/right/center alignment)
"desc" : Support element, description (left alignment)
----------------------
Dependable parameters:
----------------------
These parameters must be declared when other specific parameters are declared already. They work along with them
[val]
- Define: (number)
- Used by: option elements: ALL
Option's value type: 0. string | 1. boolean | 2. float
It tells the script what kind of value the option is storing / dealing with
[cmd]:
- Define: (string)
- Used by: option elements: ALL (needed if you want to control a console command)
Tie an option to a console command, so when the option value get changed, it get applied directly to the command
The option will show command's current value
NOTE:
cmd options don't get cached in axr_options, instead they get stored in appdata/user.ltx
[def] parameter is not needed here since we engine applies default values to commands if they don't exist in user.ltx automatically
[def]
- Define: (boolean) / (number) / (string) / ( table {function, parameters} )
- Used by: option elements: ALL (not needed if [cmd] is used)
Default value of an option
when no cached values are found in axr_options, the default value will be used
[min]
- Define: (number)
- Used by: option elements: "input" / "track": (only if [val] = 2)
Minimum viable value for an option, to make sure a value stays in range
[max]
- Define: (number)
- Used by: option elements: "input" / "track": (only if [val] = 2)
Maximum viable value for an option, to make sure a value stays in range
[step]
- Define: (number)
- Used by: option elements: "track": (only if [val] = 2)
How much a value can be increased/decreased in one step
[content]
- Define: ( table {double pairs} ) / ( table {function, parameters} )
- Used by: option elements: "list" / "radio_h" / "radio_v":
Delcares option's selection list
Pairs: { value of the selection, string to show on UI }
Example: content= { {0,"off"} , {1,"half"} , {2,"full"}}
So the list or radio option will show 3 selections (translated strings): (ui_mm_lst_off) and (ui_mm_lst_half) and (ui_mm_lst_full)
When you select one and it get applied, the assosiated value will get applied
So picking the first one will pass ( 0 )
[link]
- Define: (string)
- Used by: support elements: "image" / "slide"
Link to texture you want to show
[text]
- Define: (string)
- Used by: support elements: "slide" / "title" / "desc"
String to show near the image, it will be translated
----------------------
Optional parameters:
----------------------
These parameters are completely optionals, and can be used for custom stuff
[force_horz]
- Define: (boolean)
- Used by: option elements: "radio_h"
Force the radio buttons into horizental layout, despite their number
[no_str]
- Define: (boolean)
- Used by: option elements: "list" / "radio_h" / "radio_v" / "track"
Usually, the 2nd key of pairs in content table are strings to show on the UI, by translating "opt_str_lst_(string)"
when we set [no_str] to true, it will show the string fromm table as it is without translations or "opt_str_lst_"
For TrackBars: no_str won't show value next to the slider
[prec]
- Define: (number)
- Used by: option elements: "track"
allowed number of zeros in a number
[precondition]
- Define: ( table {function, parameters} )
- Used by: option elements: ALL
Show the option on UI if the precondition function returns true
[functor]
- Define: ( table {function, parameters} )
- Used by: option elements: ALL
Execute a function when option's changes get applied
[postcondition]
- Define: ( table {function, parameters} )
- Used by: option elements: ALL, with defined [functor]
Option won't execute its functor when changes are applied, unless if the postcondition function returns true
[curr]
- Define: ( table {function, parameters} )
- Used by: option elements: ALL
get current value of an option by executing the declared function, instead of reading it from axr_options.ltx
[hint]
- Define: (string)
- Used by: option elements: ALL
Override default name / desc rule to replace the translation of an option with a custom one, should be set without "ui_mm_" and "_desc"
Example: { hint = "alife_warfare_capture"} will force the script to use "ui_mm_alife_warfare_capture" and "ui_mm_alife_warfare_capture_desc" for name and desc of the option
[clr]
- Define: ( table {a,r,b,g} )
- Used by: support elements: "title" / "desc"
determines the color of the text
[stretch]
- Define: (boolean)
- Used by: support elements: "slide"
force the texture to stretch or not
[pos]
- Define: ( table {x,y} )
- Used by: support elements: "slide"
custom pos for the texture
[size]
- Define: ( table {w,z} )
- Used by: support elements: "slide"
custom size for the texture
[align]
- Define: (string) "l" "r" "c"
- Used by: support elements: "title"
determines the alignment of the title
[spacing]
- Define: (number)
- Used by: support elements: "slide"
hight offset to add extra space
--]]