Index: ps/trunk/binaries/data/mods/public/gui/options/options.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/options/options.js (revision 26029) +++ ps/trunk/binaries/data/mods/public/gui/options/options.js (revision 26030) @@ -1,435 +1,448 @@ /** * Translated JSON file contents. */ var g_Options; /** * Names of config keys that have changed, value returned when closing the page. */ var g_ChangedKeys; /** * Vertical size of a tab button. */ var g_TabButtonHeight = 30; /** * Vertical space between two tab buttons. */ var g_TabButtonDist = 5; /** * Vertical distance between the top of the page and the first option. */ var g_OptionControlOffset = 5; /** * Vertical size of each option control. */ var g_OptionControlHeight = 26; /** * Vertical distance between two consecutive options. */ var g_OptionControlDist = 2; /** * Horizontal indentation to distinguish options that depend on another option. */ var g_DependentLabelIndentation = 25; /** * Color used to indicate that the string entered by the player isn't a sane color. */ var g_InsaneColor = "255 0 255"; /** * Defines the parsing of config strings and GUI control interaction for the different option types. * * @property configToValue - parses a string from the user config to a value of the declared type. * @property valueToGui - sets the GUI control to display the given value. * @property guiToValue - returns the value of the GUI control. * @property guiSetter - event name that should be considered a value change of the GUI control. * @property initGUI - sets properties of the GUI control that are independent of the current value. * @property sanitizeValue - Displays a visual clue if the entered value is invalid and returns a sane value. * @property tooltip - appends a custom tooltip to the given option description depending on the current value. */ var g_OptionType = { "boolean": { "configToValue": config => config == "true", "valueToGui": (value, control) => { control.checked = value; }, "guiToValue": control => control.checked, "guiSetter": "onPress" }, "string": { "configToValue": value => value, "valueToGui": (value, control) => { control.caption = value; }, "guiToValue": control => control.caption, "guiSetter": "onTextEdit" }, "color": { "configToValue": value => value, "valueToGui": (value, control) => { control.caption = value; }, "guiToValue": control => control.caption, "guiSetter": "onTextEdit", "sanitizeValue": (value, control, option) => { let color = guiToRgbColor(value); let sanitized = rgbToGuiColor(color); if (control) { control.sprite = sanitized == value ? "ModernDarkBoxWhite" : "ModernDarkBoxWhiteInvalid"; control.children[1].sprite = sanitized == value ? "color:" + value : "color:" + g_InsaneColor; } return sanitized; }, "tooltip": (value, option) => sprintf(translate("Default: %(value)s"), { "value": Engine.ConfigDB_GetValue("default", option.config) }) }, "number": { "configToValue": value => value, "valueToGui": (value, control) => { control.caption = value; }, "guiToValue": control => control.caption, "guiSetter": "onTextEdit", "sanitizeValue": (value, control, option) => { let sanitized = Math.min(option.max !== undefined ? option.max : +Infinity, Math.max(option.min !== undefined ? option.min : -Infinity, isNaN(+value) ? 0 : value)); if (control) control.sprite = sanitized == value ? "ModernDarkBoxWhite" : "ModernDarkBoxWhiteInvalid"; return sanitized; }, "tooltip": (value, option) => sprintf( option.min !== undefined && option.max !== undefined ? translateWithContext("option number", "Min: %(min)s, Max: %(max)s") : option.min !== undefined && option.max === undefined ? translateWithContext("option number", "Min: %(min)s") : option.min === undefined && option.max !== undefined ? translateWithContext("option number", "Max: %(max)s") : "", { "min": option.min, "max": option.max }) }, "dropdown": { "configToValue": value => value, "valueToGui": (value, control) => { control.selected = control.list_data.indexOf(value); }, "guiToValue": control => control.list_data[control.selected], "guiSetter": "onSelectionChange", "initGUI": (option, control) => { control.list = option.list.map(e => e.label); control.list_data = option.list.map(e => e.value); control.onHoverChange = () => { let item = option.list[control.hovered]; control.tooltip = item && item.tooltip || option.tooltip; }; } }, "dropdownNumber": { "configToValue": value => +value, "valueToGui": (value, control) => { control.selected = control.list_data.indexOf("" + value); }, "guiToValue": control => +control.list_data[control.selected], "guiSetter": "onSelectionChange", "initGUI": (option, control) => { control.list = option.list.map(e => e.label); control.list_data = option.list.map(e => e.value); control.onHoverChange = () => { const item = option.list[control.hovered]; control.tooltip = item && item.tooltip || option.tooltip; }; }, "timeout": (option, oldValue, hasChanges, newValue) => { if (!option.timeout) return; timedConfirmation( 500, 200, translate("Do you want to keep changes?"), 500, translate("Warning"), [translate("No"), translate("Yes")], [() => {this.revertChange(option, +oldValue, hasChanges);}, null] ); } }, "slider": { "configToValue": value => +value, "valueToGui": (value, control) => { control.value = +value; }, "guiToValue": control => control.value, "guiSetter": "onValueChange", "initGUI": (option, control) => { control.max_value = option.max; control.min_value = option.min; }, "tooltip": (value, option) => sprintf(translateWithContext("slider number", "Value: %(val)s (min: %(min)s, max: %(max)s)"), { "val": value.toFixed(2), "min": option.min.toFixed(2), "max": option.max.toFixed(2) }) } }; function init(data, hotloadData) { g_ChangedKeys = hotloadData ? hotloadData.changedKeys : new Set(); g_TabCategorySelected = hotloadData ? hotloadData.tabCategorySelected : 0; g_Options = Engine.ReadJSONFile("gui/options/options.json"); translateObjectKeys(g_Options, ["label", "tooltip"]); deepfreeze(g_Options); placeTabButtons( g_Options, false, g_TabButtonHeight, g_TabButtonDist, selectPanel, displayOptions); } function getHotloadData() { return { "tabCategorySelected": g_TabCategorySelected, "changedKeys": g_ChangedKeys }; } /** * Sets up labels and controls of all options of the currently selected category. */ function displayOptions() { // Hide all controls for (let body of Engine.GetGUIObjectByName("option_controls").children) { body.hidden = true; for (let control of body.children) control.hidden = true; } // Initialize label and control of each option for this category for (let i = 0; i < g_Options[g_TabCategorySelected].options.length; ++i) { // Position vertically let body = Engine.GetGUIObjectByName("option_control[" + i + "]"); let bodySize = body.size; bodySize.top = g_OptionControlOffset + i * (g_OptionControlHeight + g_OptionControlDist); bodySize.bottom = bodySize.top + g_OptionControlHeight; body.size = bodySize; body.hidden = false; // Load option data let option = g_Options[g_TabCategorySelected].options[i]; let optionType = g_OptionType[option.type]; let value = optionType.configToValue(Engine.ConfigDB_GetValue("user", option.config)); // Setup control let control = Engine.GetGUIObjectByName("option_control_" + option.type + "[" + i + "]"); control.tooltip = option.tooltip + (optionType.tooltip ? "\n" + optionType.tooltip(value, option) : ""); control.hidden = false; if (optionType.initGUI) optionType.initGUI(option, control); control[optionType.guiSetter] = function() {}; optionType.valueToGui(value, control); if (optionType.sanitizeValue) optionType.sanitizeValue(value, control, option); control[optionType.guiSetter] = function() { let value = optionType.guiToValue(control); if (optionType.sanitizeValue) optionType.sanitizeValue(value, control, option); const oldValue = optionType.configToValue(Engine.ConfigDB_GetValue("user", option.config)); control.tooltip = option.tooltip + (optionType.tooltip ? "\n" + optionType.tooltip(value, option) : ""); const hasChanges = Engine.ConfigDB_HasChanges("user"); Engine.ConfigDB_CreateValue("user", option.config, String(value)); Engine.ConfigDB_SetChanges("user", true); g_ChangedKeys.add(option.config); fireConfigChangeHandlers(new Set([option.config])); if (option.timeout) optionType.timeout(option, oldValue, hasChanges, value); if (option.function) Engine[option.function](value); enableButtons(); }; // Setup label let label = Engine.GetGUIObjectByName("option_label[" + i + "]"); label.caption = option.label; label.tooltip = option.tooltip; label.hidden = false; let labelSize = label.size; labelSize.left = option.dependencies ? g_DependentLabelIndentation : 0; labelSize.rright = control.size.rleft; label.size = labelSize; } enableButtons(); } /** * Enable exactly the buttons whose dependencies are met. */ function enableButtons() { g_Options[g_TabCategorySelected].options.forEach((option, i) => { + const isDependencyMet = dependency => { + if (typeof dependency === "string") + return Engine.ConfigDB_GetValue("user", dependency) == "true"; + else if (typeof dependency === "object") + { + const availableOps = { + "==": (config, value) => config == value, + "!=": (config, value) => config != value + }; + const op = availableOps[dependency.op] || availableOps["=="]; + return op(Engine.ConfigDB_GetValue("user", dependency.config), dependency.value); + } + error("Unsupported dependency: " + uneval(dependency)); + return false; + }; - let enabled = - !option.dependencies || - option.dependencies.every(config => Engine.ConfigDB_GetValue("user", config) == "true"); + const enabled = !option.dependencies || option.dependencies.every(isDependencyMet); Engine.GetGUIObjectByName("option_label[" + i + "]").enabled = enabled; Engine.GetGUIObjectByName("option_control_" + option.type + "[" + i + "]").enabled = enabled; }); - let hasChanges = Engine.ConfigDB_HasChanges("user"); + const hasChanges = Engine.ConfigDB_HasChanges("user"); Engine.GetGUIObjectByName("revertChanges").enabled = hasChanges; Engine.GetGUIObjectByName("saveChanges").enabled = hasChanges; } function setDefaults() { messageBox( 500, 200, translate("Resetting the options will erase your saved settings. Do you want to continue?"), translate("Warning"), [translate("No"), translate("Yes")], [null, reallySetDefaults] ); } function reallySetDefaults() { for (let category in g_Options) for (let option of g_Options[category].options) { Engine.ConfigDB_RemoveValue("user", option.config); g_ChangedKeys.add(option.config); } Engine.ConfigDB_WriteFile("user", "config/user.cfg"); revertChanges(); } function revertChange(option, oldValue, hadChanges) { if (!hadChanges) Engine.ConfigDB_SetChanges("user", false); Engine.ConfigDB_CreateValue("user", option.config, String(oldValue)); if (option.function) Engine[option.function](oldValue); displayOptions(); } function revertChanges() { Engine.ConfigDB_Reload("user"); Engine.ConfigDB_SetChanges("user", false); for (let category in g_Options) for (let option of g_Options[category].options) if (option.function) Engine[option.function]( g_OptionType[option.type].configToValue( Engine.ConfigDB_GetValue("user", option.config))); displayOptions(); } function saveChanges() { for (let category in g_Options) for (let i = 0; i < g_Options[category].options.length; ++i) { let option = g_Options[category].options[i]; let optionType = g_OptionType[option.type]; if (!optionType.sanitizeValue) continue; let value = optionType.configToValue(Engine.ConfigDB_GetValue("user", option.config)); if (value == optionType.sanitizeValue(value, undefined, option)) continue; selectPanel(category); messageBox( 500, 200, translate("Some setting values are invalid! Are you sure you want to save them?"), translate("Warning"), [translate("No"), translate("Yes")], [null, reallySaveChanges] ); return; } reallySaveChanges(); } function reallySaveChanges() { Engine.ConfigDB_WriteFile("user", "config/user.cfg"); Engine.ConfigDB_SetChanges("user", false); enableButtons(); } /** * Close GUI page and inform the parent GUI page which options changed. **/ function closePage() { if (Engine.ConfigDB_HasChanges("user")) messageBox( 500, 200, translate("You have unsaved changes, do you want to close this window?"), translate("Warning"), [translate("No"), translate("Yes")], [null, closePageWithoutConfirmation]); else closePageWithoutConfirmation(); } function closePageWithoutConfirmation() { Engine.PopGuiPage(g_ChangedKeys); } Index: ps/trunk/binaries/data/mods/public/gui/options/options.json =================================================================== --- ps/trunk/binaries/data/mods/public/gui/options/options.json (revision 26029) +++ ps/trunk/binaries/data/mods/public/gui/options/options.json (revision 26030) @@ -1,735 +1,735 @@ [ { "label": "General", "options": [ { "type": "string", "label": "Player name (single-player)", "tooltip": "How you want to be addressed in single-player matches.", "config": "playername.singleplayer" }, { "type": "string", "label": "Player name (multiplayer)", "tooltip": "How you want to be addressed in multiplayer matches (except lobby).", "config": "playername.multiplayer" }, { "type": "boolean", "label": "Background pause", "tooltip": "Pause single-player games when window loses focus.", "config": "pauseonfocusloss", "function": "PauseOnFocusLoss" }, { "type": "boolean", "label": "Enable welcome screen", "tooltip": "If you disable it, the welcome screen will still appear once, each time a new version is available. You can always launch it from the main menu.", "config": "gui.splashscreen.enable" }, { "type": "boolean", "label": "FPS overlay", "tooltip": "Show frames per second in top right corner.", "config": "overlay.fps" }, { "type": "boolean", "label": "Real time overlay", "tooltip": "Show current system time in top right corner.", "config": "overlay.realtime" }, { "type": "boolean", "label": "Game time overlay", "tooltip": "Show current simulation time in top right corner.", "config": "gui.session.timeelapsedcounter" }, { "type": "boolean", "label": "Ceasefire time overlay", "tooltip": "Always show the remaining ceasefire time.", "config": "gui.session.ceasefirecounter" }, { "type": "boolean", "label": "Chat timestamp", "tooltip": "Display the time at which a chat message was posted.", "config": "chat.timestamp" }, { "type": "dropdown", "label": "Naming of entities.", "tooltip": "How to show entity names.", "config": "gui.session.howtoshownames", "list": [ { "value": 0, "label": "Specific names first", "tooltip": "Display specific names before generic names." }, { "value": 1, "label": "Generic names first", "tooltip": "Display generic names before specific names." }, { "value": 2, "label": "Only specific names", "tooltip": "Display only specific names for entities." }, { "value": 3, "label": "Only generic names", "tooltip": "Display only generic names for entities." } ] } ] }, { "label": "Graphics (general)", "tooltip": "Set the balance between performance and visual appearance.", "options": [ { "type": "boolean", "label": "Windowed mode", "tooltip": "Start 0 A.D. in a window.", "config": "windowed" }, { "type": "boolean", "label": "Fog", "tooltip": "Enable fog.", "config": "fog" }, { "type": "boolean", "label": "Post-processing", "tooltip": "Use screen-space post-processing filters (HDR, Bloom, DOF, etc).", "config": "postproc" }, { "type": "boolean", "label": "Shadows", "tooltip": "Enable shadows.", "config": "shadows" }, { "type": "boolean", "label": "Unit silhouettes", "tooltip": "Show outlines of units behind structures.", "config": "silhouettes" }, { "type": "boolean", "label": "Particles", "tooltip": "Enable particles.", "config": "particles" }, { "type": "boolean", "label": "VSync", "tooltip": "Run vertical sync to fix screen tearing. REQUIRES GAME RESTART", "config": "vsync" }, { "type": "slider", "label": "FPS throttling in menus", "tooltip": "To save CPU workload, throttle render frequency in all menus. Set to maximum to disable throttling.", "config": "adaptivefps.menu", "min": 20, "max": 100 }, { "type": "slider", "label": "FPS throttling in games", "tooltip": "To save CPU workload, throttle render frequency in running games. Set to maximum to disable throttling.", "config": "adaptivefps.session", "min": 20, "max": 100 }, { "type": "dropdownNumber", "label": "GUI scale", "timeout": 500, "tooltip": "GUI scale", "config": "gui.scale", "function": "SetGUIScale", "list": [ { "value": 0.75, "label": "75%" }, { "value": 1.00, "label": "100%" }, { "value": 1.25, "label": "125%" }, { "value": 1.50, "label": "150%" }, { "value": 1.75, "label": "175%" }, { "value": 2.00, "label": "200%" }, { "value": 2.25, "label": "225%" }, { "value": 2.50, "label": "250%" } ] } ] }, { "label": "Graphics (advanced)", "tooltip": "More specific rendering settings.", "options": [ { "type": "boolean", "label": "Prefer GLSL", "tooltip": "Use OpenGL 2.0 shaders (recommended).", "config": "preferglsl" }, { "type": "boolean", "label": "Fog", "tooltip": "Enable fog.", "dependencies": ["preferglsl"], "config": "fog" }, { "type": "boolean", "label": "Post-processing", "tooltip": "Use screen-space post-processing filters (HDR, Bloom, DOF, etc).", "config": "postproc" }, { "type": "dropdown", "label": "Antialiasing", "tooltip": "Reduce aliasing effect on edges.", "dependencies": ["postproc", "preferglsl"], "config": "antialiasing", "list": [ { "value": "disabled", "label": "Disabled", "tooltip": "Do not use antialiasing." }, { "value": "fxaa", "label": "FXAA", "tooltip": "Fast, but simple antialiasing." }, { "value": "msaa2", "label": "MSAA (2×)", "tooltip": "Slow, but high-quality antialiasing, uses two samples per pixel. Supported for GL3.3+." }, { "value": "msaa4", "label": "MSAA (4×)", "tooltip": "Slow, but high-quality antialiasing, uses four samples per pixel. Supported for GL3.3+." }, { "value": "msaa8", "label": "MSAA (8×)", "tooltip": "Slow, but high-quality antialiasing, uses eight samples per pixel. Supported for GL3.3+." }, { "value": "msaa16", "label": "MSAA (16×)", "tooltip": "Slow, but high-quality antialiasing, uses sixteen samples per pixel. Supported for GL3.3+." } ] }, { "type": "dropdown", "label": "Sharpening", "tooltip": "Reduce blurry effects.", "dependencies": ["postproc", "preferglsl"], "config": "sharpening", "list": [ { "value": "disabled", "label": "Disabled", "tooltip": "Do not use sharpening." }, { "value": "cas", "label": "FidelityFX CAS", "tooltip": "Contrast adaptive sharpening, a fast, contrast based sharpening pass." } ] }, { "type": "slider", "label": "Sharpness factor", "tooltip": "The sharpness of the choosen pass.", - "dependencies": ["postproc", "preferglsl"], + "dependencies": ["postproc", "preferglsl", { "config": "sharpening", "op": "!=", "value": "disabled" }], "config": "sharpness", "min": 0, "max": 1 }, { "type": "dropdown", "label": "Model quality", "tooltip": "Model quality setting.", "config": "max_actor_quality", "list": [ { "value": 100, "label": "Low", "tooltip": "Simpler models for better performance." }, { "value": 150, "label": "Medium", "tooltip": "Average quality and average performance." }, { "value": 200, "label": "High", "tooltip": "High quality models." } ] }, { "type": "dropdown", "label": "Model appearance randomization", "tooltip": "Randomize the appearance of entities. Disabling gives a small performance improvement.", "config": "variant_diversity", "list": [ { "value": "none", "label": "None", "tooltip": "Entities will all look the same." }, { "value": "limited", "label": "Limited", "tooltip": "Entities will be less diverse." }, { "value": "full", "label": "Normal", "tooltip": "Entities appearance is randomized normally." } ] }, { "type": "slider", "label": "Shader effects", "tooltip": "Number of shader effects. REQUIRES GAME RESTART", "config": "materialmgr.quality", "min": 0, "max": 10 }, { "type": "boolean", "label": "Shadows", "tooltip": "Enable shadows.", "config": "shadows" }, { "type": "dropdown", "label": "Quality", "tooltip": "Shadow map resolution. High values can crash the game when using a graphics card with low memory!", "dependencies": ["shadows"], "config": "shadowquality", "list": [ { "value": -1, "label": "Low" }, { "value": 0, "label": "Medium" }, { "value": 1, "label": "High" }, { "value": 2, "label": "Very High" } ] }, { "type": "boolean", "label": "Filtering", "tooltip": "Smooth shadows.", "dependencies": ["shadows"], "config": "shadowpcf" }, { "type": "slider", "label": "Cutoff distance", "tooltip": "Hides shadows beyond a certain distance from a camera.", "dependencies": ["shadows"], "config": "shadowscutoffdistance", "min": 100, "max": 1500 }, { "type": "boolean", "label": "Cover whole map", "tooltip": "When ON shadows cover the whole map and shadows cutoff distance is ignored. Useful for making screenshots of a whole map.", "dependencies": ["shadows"], "config": "shadowscovermap" }, { "type": "boolean", "label": "Water effects", "tooltip": "When OFF, use the lowest settings possible to render water. This makes other settings irrelevant.", "config": "watereffects" }, { "type": "boolean", "label": "High-quality water effects", "tooltip": "Use higher-quality effects for water, rendering coastal waves, shore foam, and ships trails.", "dependencies": ["watereffects"], "config": "waterfancyeffects" }, { "type": "boolean", "label": "Water reflections", "tooltip": "Allow water to reflect a mirror image.", "dependencies": ["watereffects"], "config": "waterreflection" }, { "type": "boolean", "label": "Water refraction", "tooltip": "Use a real water refraction map and not transparency.", "dependencies": ["watereffects"], "config": "waterrefraction" }, { "type": "boolean", "label": "Real water depth", "tooltip": "Use actual water depth in rendering calculations.", "dependencies": ["watereffects", "waterrefraction"], "config": "waterrealdepth" } ] }, { "label": "Sound", "options": [ { "type": "slider", "label": "Master volume", "tooltip": "Master audio gain.", "config": "sound.mastergain", "function": "SetMasterGain", "min": 0, "max": 2 }, { "type": "slider", "label": "Music volume", "tooltip": "In game music gain.", "config": "sound.musicgain", "function": "SetMusicGain", "min": 0, "max": 2 }, { "type": "slider", "label": "Ambient volume", "tooltip": "In game ambient sound gain.", "config": "sound.ambientgain", "function": "SetAmbientGain", "min": 0, "max": 2 }, { "type": "slider", "label": "Action volume", "tooltip": "In game unit action sound gain.", "config": "sound.actiongain", "function": "SetActionGain", "min": 0, "max": 2 }, { "type": "slider", "label": "UI volume", "tooltip": "UI sound gain.", "config": "sound.uigain", "function": "SetUIGain", "min": 0, "max": 2 }, { "type": "boolean", "label": "Nick notification", "tooltip": "Receive audio notification when someone types your nick.", "config": "sound.notify.nick" }, { "type": "boolean", "label": "New player notification in game setup", "tooltip": "Receive audio notification when a new client joins the game setup.", "config": "sound.notify.gamesetup.join" } ] }, { "label": "Game Setup", "options": [ { "type": "boolean", "label": "Enable game setting tips", "tooltip": "Show tips when setting up a game.", "config": "gui.gamesetup.enabletips" }, { "type": "boolean", "label": "Enable settings panel slide", "tooltip": "Slide the settings panel when opening, closing or resizing.", "config": "gui.gamesetup.settingsslide" }, { "type": "boolean", "label": "Persist match settings", "tooltip": "Save and restore match settings for quick reuse when hosting another game.", "config": "persistmatchsettings" }, { "type": "dropdown", "label": "Default AI difficulty", "tooltip": "Default difficulty of the AI.", "config": "gui.gamesetup.aidifficulty", "list": [ { "value": 0, "label": "Sandbox" }, { "value": 1, "label": "Very Easy" }, { "value": 2, "label": "Easy" }, { "value": 3, "label": "Medium" }, { "value": 4, "label": "Hard" }, { "value": 5, "label": "Very Hard" } ] }, { "type": "dropdown", "label": "Default AI behavior", "tooltip": "Default behavior of the AI.", "config": "gui.gamesetup.aibehavior", "list": [ { "value": "random", "label": "Random" }, { "value": "balanced", "label": "Balanced" }, { "value": "aggressive", "label": "Aggressive" }, { "value": "defensive", "label": "Defensive" } ] }, { "type": "dropdown", "label": "Assign players", "tooltip": "Automatically assign joining clients to free player slots during the match setup.", "config": "gui.gamesetup.assignplayers", "list": [ { "value": "everyone", "label": "Everyone", "tooltip": "Players joining the match will be assigned if there is a free slot." }, { "value": "buddies", "label": "Buddies", "tooltip": "Players joining the match will only be assigned if they are a buddy of the host and if there is a free slot." }, { "value": "disabled", "label": "Disabled", "tooltip": "Players only receive a slot when the host assigns them explicitly." } ] } ] }, { "label": "Networking / Lobby", "tooltip": "These settings only affect the multiplayer.", "options": [ { "type": "boolean", "label": "TLS encryption", "tooltip": "Protect login and data exchanged with the lobby server using TLS encryption.", "config": "lobby.tls" }, { "type": "number", "label": "Chat backlog", "tooltip": "Number of backlogged messages to load when joining the lobby.", "config": "lobby.history", "min": "0" }, { "type": "boolean", "label": "Game rating column", "tooltip": "Show the average rating of the participating players in a column of the gamelist.", "config": "lobby.columns.gamerating" }, { "type": "boolean", "label": "Network warnings", "tooltip": "Show which player has a bad connection in multiplayer games.", "config": "overlay.netwarnings" }, { "type": "dropdown", "label": "Late observer joins", "tooltip": "Allow everybody or buddies only to join the game as observer after it started.", "config": "network.lateobservers", "list": [ { "value": "everyone", "label": "Everyone" }, { "value": "buddies", "label": "Buddies" }, { "value": "disabled", "label": "Disabled" } ] }, { "type": "number", "label": "Observer limit", "tooltip": "Prevent further observers from joining if the limit is reached.", "config": "network.observerlimit", "min": 0, "max": 32 }, { "type": "number", "label": "Max lag for observers", "tooltip": "When hosting, pause the game if observers are lagging more than this many turns. If set to -1, observers are ignored.", "config": "network.observermaxlag", "min": -1, "max": 10000 }, { "type": "boolean", "label": "(Observer) Speed up when lagging.", "tooltip": "When observing a game, automatically speed up if you start lagging, to catch up with the live match.", "config": "network.autocatchup" } ] }, { "label": "Game Session", "tooltip": "Change options regarding the in-game settings.", "options": [ { "type": "slider", "label": "Wounded unit health", "tooltip": "The wounded unit hotkey considers the selected units as wounded if their health percentage falls below this number.", "config": "gui.session.woundedunithotkeythreshold", "min": 0, "max": 100 }, { "type": "number", "label": "Batch training size", "tooltip": "Number of units trained per batch by default.", "config": "gui.session.batchtrainingsize", "min": 1, "max": 20 }, { "type": "slider", "label": "Scroll batch increment ratio", "tooltip": "Number of times you have to scroll to increase/decrease the batchsize by 1.", "config": "gui.session.scrollbatchratio", "min": 0.1, "max": 30 }, { "type": "slider", "label": "Flare display duration", "tooltip": "How long the flare markers on the minimap are displayed in seconds.", "config": "gui.session.flarelifetime", "min": 0, "max": 60 }, { "type": "boolean", "label": "Chat notification attack", "tooltip": "Show a chat notification if you are attacked by another player.", "config": "gui.session.notifications.attack" }, { "type": "boolean", "label": "Chat notification tribute", "tooltip": "Show a chat notification if an ally tributes resources to another team member if teams are locked, and all tributes in observer mode.", "config": "gui.session.notifications.tribute" }, { "type": "boolean", "label": "Chat notification barter", "tooltip": "Show a chat notification to observers when a player bartered resources.", "config": "gui.session.notifications.barter" }, { "type": "dropdown", "label": "Chat notification phase", "tooltip": "Show a chat notification if you or an ally have started, aborted or completed a new phase, and phases of all players in observer mode.", "config": "gui.session.notifications.phase", "list": [ { "value": "none", "label": "Disable" }, { "value": "completed", "label": "Completed" }, { "value": "all", "label": "All displayed" } ] }, { "type": "boolean", "label": "Attack range visualization", "tooltip": "Display the attack range of selected defensive structures. (It can also be toggled with the hotkey during a game).", "config": "gui.session.attackrange" }, { "type": "boolean", "label": "Aura range visualization", "tooltip": "Display the range of auras of selected units and structures. (It can also be toggled with the hotkey during a game).", "config": "gui.session.aurasrange" }, { "type": "boolean", "label": "Heal range visualization", "tooltip": "Display the healing range of selected units. (It can also be toggled with the hotkey during a game).", "config": "gui.session.healrange" }, { "type": "boolean", "label": "Rank icon above status bar", "tooltip": "Show rank icons above status bars.", "config": "gui.session.rankabovestatusbar" }, { "type": "boolean", "label": "Experience status bar", "tooltip": "Show an experience status bar above each selected unit.", "config": "gui.session.experiencestatusbar" }, { "type": "boolean", "label": "Detailed tooltips", "tooltip": "Show detailed tooltips for trainable units in unit-producing structures.", "config": "showdetailedtooltips" }, { "type": "dropdown", "label": "Sort resources and population tooltip", "tooltip": "Dynamically sort players in the resources and population tooltip by value.", "config": "gui.session.respoptooltipsort", "list": [ { "value": 0, "label": "Unordered" }, { "value": -1, "label": "Ascending" }, { "value": 1, "label": "Descending" } ] }, { "type": "color", "label": "Diplomacy colors: self", "tooltip": "Color of your units when diplomacy colors are enabled.", "config": "gui.session.diplomacycolors.self" }, { "type": "color", "label": "Diplomacy colors: ally", "tooltip": "Color of allies when diplomacy colors are enabled.", "config": "gui.session.diplomacycolors.ally" }, { "type": "color", "label": "Diplomacy colors: neutral", "tooltip": "Color of neutral players when diplomacy colors are enabled.", "config": "gui.session.diplomacycolors.neutral" }, { "type": "color", "label": "Diplomacy colors: enemy", "tooltip": "Color of enemies when diplomacy colors are enabled.", "config": "gui.session.diplomacycolors.enemy" }, { "type": "dropdown", "label": "Snap to edges", "tooltip": "This option allows to align new structures with nearby structures.", "config": "gui.session.snaptoedges", "list": [ { "value": "disabled", "label": "Hotkey to enable snapping", "tooltip": "New structures are aligned with nearby structures while pressing the hotkey." }, { "value": "enabled", "label": "Hotkey to disable snapping", "tooltip": "New structures are aligned with nearby structures unless the hotkey is pressed." } ] }, { "type": "dropdown", "label": "Control group membership", "tooltip": "Decide whether units can be part of multiple control groups.", "config": "gui.session.disjointcontrolgroups", "list": [ { "value": "true", "label": "Single", "tooltip": "When adding a Unit or Structure to a control group, they are removed from other control groups. Use this choice if you want control groups to refer to distinct armies." }, { "value": "false", "label": "Multiple", "tooltip": "Units and Structures can be part of multiple control groups. This is useful to keep control groups for distinct armies and a control group for the entire army simultaneously." } ] }, { "type": "dropdown", "label": "Formation control", "tooltip": "Decide whether formations are enabled for all orders or only 'Walk' and 'Patrol'.", "config": "gui.session.formationwalkonly", "list": [ { "value": "true", "label": "Walk/Patrol Only", "tooltip": "Other orders will disband existing formations." }, { "value": "false", "label": "No override", "tooltip": "Units in formations stay in formations." } ] } ] } ]