Index: ps/trunk/binaries/data/mods/public/gui/options/options.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/options/options.js (revision 20065) +++ ps/trunk/binaries/data/mods/public/gui/options/options.js (revision 20066) @@ -1,417 +1,391 @@ var g_HasCallback = false; var g_Controls; function init(data) { if (data && data.callback) g_HasCallback = true; g_Controls = {}; let options = Engine.ReadJSONFile("gui/options/options.json"); for (let category in options) { let lastSize; for (let i = 0; i < options[category].length; ++i) { let option = options[category][i]; if (!option.label || !option.parameters || !option.parameters.config) continue; let body = Engine.GetGUIObjectByName(category + "[" + i + "]"); let label = Engine.GetGUIObjectByName(category + "Label[" + i + "]"); let config = option.parameters.config; g_Controls[config] = { "control": setupControl(option, i, category), "label": label, "type": option.type, "dependencies": option.dependencies || undefined, "parameters": option.parameters }; label.caption = translate(option.label); label.tooltip = option.tooltip ? translate(option.tooltip) : ""; // Move each element to the correct place. if (lastSize) { let newSize = new GUISize(); newSize.left = lastSize.left; newSize.rright = lastSize.rright; newSize.top = lastSize.bottom; newSize.bottom = newSize.top + 26; body.size = newSize; lastSize = newSize; } else lastSize = body.size; // small right shift of options which depends on another one for (let opt of options[category]) { if (!opt.label || !opt.parameters || !opt.parameters.config) continue; if (!opt.dependencies || opt.dependencies.indexOf(config) == -1) continue; label.caption = " " + label.caption; break; } body.hidden = false; } } updateOptionPanel(); } /** * Setup the apropriate control for a given option. * * @param option Structure containing the data to setup an option. * @param prefix Prefix to use when accessing control, for example "generalSetting" when the tickbox name is generalSettingTickbox[i]. */ function setupControl(option, i, category) { let control; let onUpdate; let key = option.parameters.config; switch (option.type) { case "boolean": // More space for the label let text = Engine.GetGUIObjectByName(category + "Label[" + i + "]"); let size = text.size; size.rright = 87; text.size = size; control = Engine.GetGUIObjectByName(category + "Tickbox[" + i + "]"); let checked; - let keyRenderer; - for (let param in option.parameters) { switch (param) { case "config": checked = Engine.ConfigDB_GetValue("user", key) == "true"; break; - case "renderer": - keyRenderer = option.parameters.renderer; - if (!Engine["Renderer_Get" + keyRenderer + "Enabled"]) - { - warn("Invalid renderer key " + keyRenderer); - keyRenderer = undefined; - break; - } - if (Engine["Renderer_Get" + keyRenderer + "Enabled"]() != checked) - { - warn("Incompatible renderer option value for " + keyRenderer); - Engine["Renderer_Set" + keyRenderer + "Enabled"](checked); - } + case "function": break; default: warn("Unknown option source type '" + param + "'"); } } - onUpdate = function(key, keyRenderer) + onUpdate = function(key) { return function() { - if (keyRenderer) - Engine["Renderer_Set" + keyRenderer + "Enabled"](this.checked); + if (option.parameters.function) + Engine[option.parameters.function](this.checked); Engine.ConfigDB_CreateValue("user", key, String(this.checked)); Engine.ConfigDB_SetChanges("user", true); updateOptionPanel(); }; - }(key, keyRenderer); + }(key); // Load final data to the control element. control.checked = checked; control.onPress = onUpdate; break; case "slider": control = Engine.GetGUIObjectByName(category + "Slider[" + i + "]"); let value; let callbackFunction; let minvalue; let maxvalue; for (let param in option.parameters) { switch (param) { case "config": value = +Engine.ConfigDB_GetValue("user", key); break; case "function": if (Engine[option.parameters.function]) callbackFunction = option.parameters.function; break; case "min": minvalue = +option.parameters.min; break; case "max": maxvalue = +option.parameters.max; break; default: warn("Unknown option source type '" + param + "'"); } } onUpdate = function(key, callbackFunction, minvalue, maxvalue) { return function() { this.tooltip = (option.tooltip ? translate(option.tooltip) + "\n" : "") + sprintf(translateWithContext("slider number", "Value: %(val)s (min: %(min)s, max: %(max)s)"), { "val": +this.value.toFixed(2), "min": +minvalue.toFixed(2), "max": +maxvalue.toFixed(2) }); if (+Engine.ConfigDB_GetValue("user", key) === this.value) return; Engine.ConfigDB_CreateValue("user", key, this.value); Engine.ConfigDB_SetChanges("user", true); if (callbackFunction) Engine[callbackFunction](+this.value); updateOptionPanel(); }; }(key, callbackFunction, minvalue, maxvalue); control.value = value; control.max_value = maxvalue; control.min_value = minvalue; control.onValueChange = onUpdate; break; case "number": case "string": control = Engine.GetGUIObjectByName(category + "Input[" + i + "]"); let caption; let functionBody; let minval; let maxval; for (let param in option.parameters) { switch (param) { case "config": caption = Engine.ConfigDB_GetValue("user", key); break; case "function": if (Engine[option.parameters.function]) functionBody = option.parameters.function; break; case "min": minval = option.parameters.min; break; case "max": maxval = option.parameters.max; break; default: warn("Unknown option source type '" + param + "'"); } } // as the enter key is not necessarily pressed after modifying an entry, we will register the input also // - when the mouse leave the control (MouseLeave event) // - or when saving or closing the window (registerChanges function) // so we must ensure that something has indeed been modified onUpdate = function(key, functionBody, minval, maxval) { return function() { if (minval && +minval > +this.caption) this.caption = minval; if (maxval && +maxval < +this.caption) this.caption = maxval; if (Engine.ConfigDB_GetValue("user", key) == this.caption) return; Engine.ConfigDB_CreateValue("user", key, this.caption); Engine.ConfigDB_SetChanges("user", true); if (functionBody) Engine[functionBody](+this.caption); updateOptionPanel(); }; }(key, functionBody, minval, maxval); control.caption = caption; control.onPress = onUpdate; control.onMouseLeave = onUpdate; break; case "dropdown": { control = Engine.GetGUIObjectByName(category + "Dropdown[" + i + "]"); control.onSelectionChange = function(){}; // just the time to setup the value let config; let callbackFunction; for (let param in option.parameters) { switch (param) { case "config": config = Engine.ConfigDB_GetValue("user", key); break; case "function": if (Engine[option.parameters.function]) callbackFunction = option.parameters.function; break; case "list": control.list = option.parameters.list.map(e => translate(e.label)); let values = option.parameters.list.map(e => e.value); control.list_data = values; control.selected = values.map(String).indexOf(config); break; default: warn("Unknown option source type '" + param + "'"); } } onUpdate = function(key, callbackFunction) { return function() { Engine.ConfigDB_CreateValue("user", key, this.list_data[this.selected]); Engine.ConfigDB_SetChanges("user", true); if (callbackFunction) Engine[callbackFunction](this.list_data[this.selected]); updateOptionPanel(); }; }(key, callbackFunction); control.onSelectionChange = onUpdate; break; } default: warn("Unknown option type " + option.type + ", assuming string."); control = Engine.GetGUIObjectByName(category + "Input[" + i + "]"); break; } control.hidden = false; if (option.type == "slider") control.onValueChange(); else control.tooltip = option.tooltip ? translate(option.tooltip) : ""; return control; } function updateOptionPanel() { // Update dependencies for (let item in g_Controls) { let control = g_Controls[item]; if (control.type != "boolean" || !control.dependencies) continue; for (let dependency of control.dependencies) { g_Controls[dependency].control.enabled = control.control.checked; g_Controls[dependency].label.enabled = control.control.checked; } } // And main buttons let hasChanges = Engine.ConfigDB_HasChanges("user"); Engine.GetGUIObjectByName("revertChanges").enabled = hasChanges; Engine.GetGUIObjectByName("saveChanges").enabled = hasChanges; } /** * Register changes of input (text and number) controls */ function registerChanges() { for (let item in g_Controls) if (g_Controls[item].type == "number" || g_Controls[item].type == "string") g_Controls[item].control.onPress(); } 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 item in g_Controls) Engine.ConfigDB_RemoveValue("user", item); Engine.ConfigDB_WriteFile("user", "config/user.cfg"); revertChanges(); } function revertChanges() { Engine.ConfigDB_Reload("user"); for (let item in g_Controls) { let control = g_Controls[item]; - // needs to update renderer values (which are all of boolean type) - if (control.parameters.renderer) - { - if (control.type != "boolean") - { - warn("Invalid type option " + control.type + " defined in renderer for " + item + ": will not be reverted"); - continue; - } - let checked = Engine.ConfigDB_GetValue("user", item) == "true"; - Engine["Renderer_Set" + control.parameters.renderer + "Enabled"](checked); - } - // and the possible function calls (which are of number or string types) if (control.parameters.function) { - if (control.type != "string" && control.type != "number" && control.type != "slider" && control.type != "dropdown") - { - warn("Invalid type option " + control.type + " defined with function for " + item + ": will not be reverted"); - continue; - } - let caption = Engine.ConfigDB_GetValue("user", item); - Engine[control.parameters.function](+caption); + let value = Engine.ConfigDB_GetValue("user", item); + Engine[control.parameters.function]( + (control.type == "string" || control.type == "dropdown") ? + value : + control.type == "boolean" ? + value == "true" : + +value); } } Engine.ConfigDB_SetChanges("user", false); init(); } function saveChanges() { registerChanges(); Engine.ConfigDB_WriteFile("user", "config/user.cfg"); Engine.ConfigDB_SetChanges("user", false); updateOptionPanel(); } /** * Close GUI page and call callbacks if they exist. **/ function closePage() { registerChanges(); 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() { if (g_HasCallback) Engine.PopGuiPageCB(); else Engine.PopGuiPage(); } Index: ps/trunk/binaries/data/mods/public/gui/options/options.json =================================================================== --- ps/trunk/binaries/data/mods/public/gui/options/options.json (revision 20065) +++ ps/trunk/binaries/data/mods/public/gui/options/options.json (revision 20066) @@ -1,356 +1,356 @@ { "generalSetting": [ { "type": "string", "label": "Playername (Single Player)", "tooltip": "How you want to be addressed in Single Player matches.", "parameters": { "config": "playername.singleplayer" } }, { "type": "string", "label": "Playername (Multiplayer)", "tooltip": "How you want to be addressed in Multiplayer matches (except lobby).", "parameters": { "config": "playername.multiplayer" } }, { "type": "boolean", "label": "Windowed Mode", "tooltip": "Start 0 A.D. in a window", "parameters": { "config": "windowed" } }, { "type": "boolean", "label": "Background Pause", "tooltip": "Pause single player games when window loses focus", "parameters": { "config": "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.", "parameters": { "config": "gui.splashscreen.enable" } }, { "type": "boolean", "label": "Enable Game Setting Tips", "tooltip": "Show tips when setting up a game.", "parameters": { "config": "gui.gamesetup.enabletips" } }, { "type": "boolean", "label": "Detailed Tooltips", "tooltip": "Show detailed tooltips for trainable units in unit-producing buildings.", "parameters": { "config": "showdetailedtooltips" } }, { "type": "boolean", "label": "Network Warnings", "tooltip": "Show which player has a bad connection in multiplayer games.", "parameters": { "config": "overlay.netwarnings" } }, { "type": "boolean", "label": "FPS Overlay", "tooltip": "Show frames per second in top right corner.", "parameters": { "config": "overlay.fps" } }, { "type": "boolean", "label": "Realtime Overlay", "tooltip": "Show current system time in top right corner.", "parameters": { "config": "overlay.realtime" } }, { "type": "boolean", "label": "Gametime Overlay", "tooltip": "Show current simulation time in top right corner.", "parameters": { "config": "gui.session.timeelapsedcounter" } }, { "type": "boolean", "label": "Ceasefire Time Overlay", "tooltip": "Always show the remaining ceasefire time.", "parameters": { "config": "gui.session.ceasefirecounter" } }, { "type": "boolean", "label": "Persist Match Settings", "tooltip": "Save and restore match settings for quick reuse when hosting another game", "parameters": { "config": "persistmatchsettings" } }, { "type": "dropdown", "label": "Assign Players", "tooltip": "Automatically assign joining clients to free player slots during the match setup.", "parameters": { "config": "gui.gamesetup.assignplayers", "list": [ { "value": "everyone", "label": "Everyone" }, { "value": "buddies", "label": "Buddies" }, { "value": "disabled", "label": "Disabled" } ] } }, { "type": "dropdown", "label": "Late Observer Joins", "tooltip": "Allow everybody or buddies only to join the game as observer after it started", "parameters": { "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", "parameters": { "config": "network.observerlimit", "min": 0, "max": 32 } }, { "type": "number", "label": "Batch Training Size", "tooltip": "Number of units trained per batch", "parameters": { "config": "gui.session.batchtrainingsize", "min": 1, "max": 20 } }, { "type": "boolean", "label": "Chat Timestamp", "tooltip": "Show time that messages are posted in the lobby, gamesetup and ingame chat.", "parameters": { "config": "chat.timestamp" } }, { "type": "boolean", "label": "Aura Range Visualization", "tooltip": "Display the range of auras of selected units and structures (can also be toggled in-game with the hotkey).", "parameters": { "config": "gui.session.aurarange" } }, { "type": "boolean", "label": "Heal Range Visualization", "tooltip": "Display the healing range of selected units (can also be toggled in-game with the hotkey).", "parameters": { "config": "gui.session.healrange" } } ], "graphicsSetting": [ { "type": "boolean", "label": "Prefer GLSL", "tooltip": "Use OpenGL 2.0 shaders (recommended)", - "parameters": { "config": "preferglsl", "renderer": "PreferGLSL" } + "parameters": { "config": "preferglsl", "function": "Renderer_SetPreferGLSLEnabled" } }, { "type": "boolean", "label": "Post Processing", "tooltip": "Use screen-space postprocessing filters (HDR, Bloom, DOF, etc)", - "parameters": { "config": "postproc", "renderer": "Postproc" } + "parameters": { "config": "postproc", "function": "Renderer_SetPostprocEnabled" } }, { "type": "slider", "label": "Shader Effects", "tooltip": "Number of shader effects. REQUIRES GAME RESTART", "parameters": { "config": "materialmgr.quality", "min": 0, "max": 10 } }, { "type": "boolean", "label": "Shadows", "tooltip": "Enable shadows", - "parameters": { "config": "shadows", "renderer": "Shadows" }, + "parameters": { "config": "shadows", "function": "Renderer_SetShadowsEnabled" }, "dependencies": [ "shadowquality", "shadowpcf" ] }, { "type": "dropdown", "label": "Shadow Quality", "tooltip": "Shadow map resolution. High values can crash the game when using a graphics card with low memory!", "parameters": { "config": "shadowquality", "function": "Renderer_RecreateShadowMap", "list": [ { "value": -2, "label": "Very Low" }, { "value": -1, "label": "Low" }, { "value": 0, "label": "Medium" }, { "value": 1, "label": "High" }, { "value": 2, "label": "Very High" } ] } }, { "type": "boolean", "label": "Shadow Filtering", "tooltip": "Smooth shadows", - "parameters": { "config": "shadowpcf", "renderer": "ShadowPCF" } + "parameters": { "config": "shadowpcf", "function": "Renderer_SetShadowPCFEnabled" } }, { "type": "boolean", "label": "Unit Silhouettes", "tooltip": "Show outlines of units behind buildings", - "parameters": { "config": "silhouettes", "renderer": "Silhouettes" } + "parameters": { "config": "silhouettes", "function": "Renderer_SetSilhouettesEnabled" } }, { "type": "boolean", "label": "Particles", "tooltip": "Enable particles", - "parameters": { "config": "particles", "renderer": "Particles" } + "parameters": { "config": "particles", "function": "Renderer_SetParticlesEnabled" } }, { "type": "boolean", "label": "Water Effects", "tooltip": "When OFF, use the lowest settings possible to render water. This makes other settings irrelevant.", - "parameters": { "config": "watereffects", "renderer": "WaterEffects" }, + "parameters": { "config": "watereffects", "function": "Renderer_SetWaterEffectsEnabled" }, "dependencies": [ "waterfancyeffects", "waterrealdepth", "waterreflection", "waterrefraction", "watershadows" ] }, { "type": "boolean", "label": "HQ Water Effects", "tooltip": "Use higher-quality effects for water, rendering coastal waves, shore foam, and ships trails.", - "parameters": { "config": "waterfancyeffects", "renderer": "WaterFancyEffects" } + "parameters": { "config": "waterfancyeffects", "function": "Renderer_SetWaterFancyEffectsEnabled" } }, { "type": "boolean", "label": "Real Water Depth", "tooltip": "Use actual water depth in rendering calculations", - "parameters": { "config": "waterrealdepth", "renderer": "WaterRealDepth" } + "parameters": { "config": "waterrealdepth", "function": "Renderer_SetWaterRealDepthEnabled" } }, { "type": "boolean", "label": "Water Reflections", "tooltip": "Allow water to reflect a mirror image", - "parameters": { "config": "waterreflection", "renderer": "WaterReflection" } + "parameters": { "config": "waterreflection", "function": "Renderer_SetWaterReflectionEnabled" } }, { "type": "boolean", "label": "Water Refraction", "tooltip": "Use a real water refraction map and not transparency", - "parameters": { "config": "waterrefraction", "renderer": "WaterRefraction" } + "parameters": { "config": "waterrefraction", "function": "Renderer_SetWaterRefractionEnabled" } }, { "type": "boolean", "label": "Shadows on Water", "tooltip": "Cast shadows on water", - "parameters": { "config": "watershadows", "renderer": "WaterShadows" } + "parameters": { "config": "watershadows", "function": "Renderer_SetWaterShadowsEnabled" } }, { "type": "boolean", "label": "Smooth LOS", "tooltip": "Lift darkness and fog-of-war smoothly", - "parameters": { "config": "smoothlos", "renderer": "SmoothLOS" } + "parameters": { "config": "smoothlos", "function": "Renderer_SetSmoothLOSEnabled" } }, { "type": "boolean", "label": "Show Sky", "tooltip": "Render Sky", - "parameters": { "config": "showsky", "renderer": "ShowSky" } + "parameters": { "config": "showsky", "function": "Renderer_SetShowSkyEnabled" } }, { "type": "boolean", "label": "VSync", "tooltip": "Run vertical sync to fix screen tearing. REQUIRES GAME RESTART", "parameters": { "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.", "parameters": { "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.", "parameters": { "config": "adaptivefps.session", "min": 20, "max": 100 } } ], "soundSetting": [ { "type": "slider", "label": "Master Volume", "tooltip": "Master audio gain", "parameters": { "config": "sound.mastergain", "function": "SetMasterGain", "min": 0, "max": 2 } }, { "type": "slider", "label": "Music Volume", "tooltip": "In game music gain", "parameters": { "config": "sound.musicgain", "function": "SetMusicGain", "min": 0, "max": 2 } }, { "type": "slider", "label": "Ambient Volume", "tooltip": "In game ambient sound gain", "parameters": { "config": "sound.ambientgain", "function": "SetAmbientGain", "min": 0, "max": 2 } }, { "type": "slider", "label": "Action Volume", "tooltip": "In game unit action sound gain", "parameters": { "config": "sound.actiongain", "function": "SetActionGain", "min": 0, "max": 2 } }, { "type": "slider", "label": "UI Volume", "tooltip": "UI sound gain", "parameters": { "config": "sound.uigain", "function": "SetUIGain", "min": 0, "max": 2 } }, { "type": "boolean", "label": "Nick Notification", "tooltip": "Receive audio notification when someone types your nick", "parameters": { "config": "sound.notify.nick" } } ], "lobbySetting": [ { "type": "number", "label": "Chat Backlog", "tooltip": "Number of backlogged messages to load when joining the lobby", "parameters": { "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.", "parameters": { "config": "lobby.columns.gamerating" } } ], "notificationSetting": [ { "type": "boolean", "label": "Attack", "tooltip": "Show a chat notification if you are attacked by another player", "parameters": { "config": "gui.session.notifications.attack" } }, { "type": "boolean", "label": "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", "parameters": { "config": "gui.session.notifications.tribute" } }, { "type": "boolean", "label": "Barter", "tooltip": "Show a chat notification to observers when a player bartered resources", "parameters": { "config": "gui.session.notifications.barter" } }, { "type": "dropdown", "label": "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", "parameters": { "config": "gui.session.notifications.phase", "list": [ { "value": "none", "label": "Disable" }, { "value": "completed", "label": "Completed" }, { "value": "all", "label": "All displayed" } ] } } ] }