Version 0.95 BETA

This commit is contained in:
OTCv8
2019-10-02 03:38:52 +02:00
parent 9219c78f15
commit 5220a3bdd2
501 changed files with 38097 additions and 2 deletions

View File

@@ -0,0 +1,28 @@
Panel
OptionCheckBox
id: enableAudio
!text: tr('Enable audio')
OptionCheckBox
id: enableMusicSound
!text: tr('Enable music sound')
Label
id: musicSoundVolumeLabel
!text: tr('Music volume: %d', 100)
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 6
@onSetup: |
local value = modules.client_options.getOption('musicSoundVolume')
self:setText(tr('Music volume: %d', value))
OptionScrollbar
id: musicSoundVolume
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
minimum: 0
maximum: 100

View File

@@ -0,0 +1,28 @@
Panel
OptionCheckBox
id: showInfoMessagesInConsole
!text: tr('Show info messages in console')
OptionCheckBox
id: showEventMessagesInConsole
!text: tr('Show event messages in console')
OptionCheckBox
id: showStatusMessagesInConsole
!text: tr('Show status messages in console')
OptionCheckBox
id: showTimestampsInConsole
!text: tr('Show timestamps in console')
OptionCheckBox
id: showLevelsInConsole
!text: tr('Show levels in console')
OptionCheckBox
id: showPrivateMessagesInConsole
!text: tr('Show private messages in console')
OptionCheckBox
id: showPrivateMessagesOnScreen
!text: tr('Show private messages on screen')

View File

@@ -0,0 +1,113 @@
Panel
OptionCheckBox
id: classicControl
!text: tr('Classic control')
OptionCheckBox
id: autoChaseOverride
!text: tr('Allow auto chase override')
OptionCheckBox
id: displayText
!text: tr('Display text messages')
OptionCheckBox
id: wsadWalking
!text: tr('Enable WSAD walking')
!tooltip: tr('Disable chat and allow walk using WSAD keys')
OptionCheckBox
id: extentedPreWalking
!text: tr('Enable smooth walking (DASH)')
!tooltip: tr('Allows to execute next move without server confirmation of previous one')
OptionCheckBox
id: smartWalk
!text: tr('Enable smart walking')
!tooltip: tr('Will detect when to use diagonal step based on the\nkeys you are pressing')
Label
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
id: walkFirstStepDelayLabel
margin-top: 10
@onSetup: |
local value = modules.client_options.getOption('walkFirstStepDelay')
self:setText(tr('Walk delay after first step: %s ms', value))
OptionScrollbar
id: walkFirstStepDelay
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 3
minimum: 0
maximum: 300
Label
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
id: walkTurnDelayLabel
margin-top: 10
@onSetup: |
local value = modules.client_options.getOption('walkTurnDelay')
self:setText(tr('Walk delay after turn: %s ms', value))
OptionScrollbar
id: walkTurnDelay
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 3
minimum: 0
maximum: 300
Label
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
id: walkStairsDelayLabel
margin-top: 10
@onSetup: |
local value = modules.client_options.getOption('walkStairsDelay')
self:setText(tr('Walk delay after floor change: %s ms', value))
OptionScrollbar
id: walkStairsDelay
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 3
minimum: 0
maximum: 300
Label
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
id: walkTeleportDelayLabel
margin-top: 10
@onSetup: |
local value = modules.client_options.getOption('walkTeleportDelay')
self:setText(tr('Walk delay after teleport: %s ms', value))
OptionScrollbar
id: walkTeleportDelay
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 3
minimum: 0
maximum: 300
Button
id: changeLocale
!text: tr('Change language')
@onClick: modules.client_locales.createWindow()
anchors.top: prev.bottom
anchors.left: prev.left
margin-top: 12
width: 120

View File

@@ -0,0 +1,125 @@
Panel
Label
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-wrap: false
@onSetup: |
self:setText(tr("GPU: ") .. g_graphics.getRenderer())
Label
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
text-wrap: false
@onSetup: |
self:setText(tr("Version: ") .. g_graphics.getVersion())
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin: 5 5 5 5
OptionCheckBox
id: vsync
!text: tr('Enable vertical synchronization')
!tooltip: tr('Limits FPS (usually to 60)')
@onSetup: |
if g_window.getPlatformType() == 'WIN32-EGL' then
self:setEnabled(false)
self:setText(tr('Enable vertical synchronization') .. " " .. tr('(OpenGL only)'))
end
OptionCheckBox
id: showFps
!text: tr('Show frame rate')
OptionCheckBox
id: enableLights
!text: tr('Enable lights')
OptionCheckBox
id: fullscreen
!text: tr('Fullscreen')
tooltip: Ctrl+Shift+F
Label
margin-top: 12
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
id: optimizationLevelLabel
!text: tr("Optimization level")
ComboBox
id: optimizationLevel
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-right: 2
margin-left: 2
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
@onSetup: |
self:addOption("Automatic")
self:addOption("None")
self:addOption("Low")
self:addOption("Medium")
self:addOption("High")
self:addOption("Maximum")
Label
id: backgroundFrameRateLabel
!text: tr('Game framerate limit: %s', 'max')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 12
@onSetup: |
local value = modules.client_options.getOption('backgroundFrameRate')
local text = value
if value <= 0 or value >= 201 then
text = 'max'
end
self:setText(tr('Game framerate limit: %s', text))
OptionScrollbar
id: backgroundFrameRate
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
minimum: 10
maximum: 201
Label
id: ambientLightLabel
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 6
@onSetup: |
local value = modules.client_options.getOption('ambientLight')
self:setText(tr('Ambient light: %s%%', value))
OptionScrollbar
id: ambientLight
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
minimum: 0
maximum: 100
Label
id: tips
margin-top: 20
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
text-auto-resize: true
text-align: left
text-wrap: true
!text: tr("If you have FPS issues:\n- Use OpenGL version (_gl)\n- Disable vertical synchronization\n- Set higher optimization level\n- Lower screen resolution\nOr report it via email to otclient@otclient.ovh")

View File

@@ -0,0 +1,159 @@
Panel
OptionCheckBox
id: classicView
!text: tr('Classic view')
OptionCheckBox
id: showPing
!text: tr('Show connection ping')
!tooltip: tr('Display connection speed to the server (milliseconds)')
OptionCheckBox
id: displayNames
!text: tr('Display creature names')
OptionCheckBox
id: displayHealth
!text: tr('Display creature health bars')
OptionCheckBox
id: displayHealthOnTop
!text: tr('Display creature health bars above texts')
OptionCheckBox
id: hidePlayerBars
!text: tr('Show player health bar')
OptionCheckBox
id: displayMana
!text: tr('Show player mana bar')
OptionCheckBox
id: topHealtManaBar
!text: tr('Show player top health and mana bar')
OptionCheckBox
id: showHealthManaCircle
!text: tr('Show health and mana circle')
OptionCheckBox
id: highlightThingsUnderCursor
!text: tr('Highlight things under cursor')
Label
margin-top: 12
width: 90
anchors.left: parent.left
anchors.top: prev.bottom
id: leftPanelsLabel
!text: tr("Left panels")
Label
width: 90
anchors.left: prev.right
anchors.top: prev.top
id: rightPanelsLabel
!text: tr("Right panels")
Label
width: 130
anchors.left: prev.right
anchors.top: prev.top
id: backpackPanelLabel
!text: tr("Container's panel")
!tooltip: tr("Open new containers in selected panel")
ComboBox
id: leftPanels
anchors.left: leftPanelsLabel.left
anchors.right: leftPanelsLabel.right
anchors.top: leftPanelsLabel.bottom
margin-top: 3
margin-right: 20
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
@onSetup: |
self:addOption("0")
self:addOption("1")
self:addOption("2")
self:addOption("3")
self:addOption("4")
ComboBox
id: rightPanels
anchors.left: rightPanelsLabel.left
anchors.right: rightPanelsLabel.right
anchors.top: rightPanelsLabel.bottom
margin-top: 3
margin-right: 20
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
@onSetup: |
self:addOption("1")
self:addOption("2")
self:addOption("3")
self:addOption("4")
ComboBox
id: containerPanel
anchors.left: backpackPanelLabel.left
anchors.right: backpackPanelLabel.right
anchors.top: backpackPanelLabel.bottom
margin-top: 3
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
@onSetup: |
self:addOption("1st left panel")
self:addOption("2nd left panel")
self:addOption("3rd left panel")
self:addOption("4th left panel")
self:addOption("1st right panel")
self:addOption("2nd right panel")
self:addOption("3rd right panel")
self:addOption("4th right panel")
Label
margin-top: 12
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
id: crosshairLabel
!text: tr("Crosshair")
ComboBox
id: crosshair
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-right: 2
margin-left: 2
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
@onSetup: |
self:addOption("None")
self:addOption("Default")
self:addOption("Full")
Label
id: floorFadingLabel
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 6
@onSetup: |
local value = modules.client_options.getOption('floorFading')
self:setText(tr('Floor fading: %s ms', value))
OptionScrollbar
id: floorFading
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
minimum: 0
maximum: 2000
Label
id: floorFadingLabel2
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 6
!text: (tr('Floor fading doesn\'t work with enabled light'))

View File

@@ -0,0 +1,365 @@
local defaultOptions = {
vsync = true,
showFps = true,
showPing = true,
fullscreen = false,
classicView = false,
classicControl = true,
smartWalk = false,
extentedPreWalking = true,
autoChaseOverride = true,
showStatusMessagesInConsole = true,
showEventMessagesInConsole = true,
showInfoMessagesInConsole = true,
showTimestampsInConsole = true,
showLevelsInConsole = true,
showPrivateMessagesInConsole = true,
showPrivateMessagesOnScreen = true,
rightPanels = 1,
leftPanels = 0,
containerPanel = 8,
backgroundFrameRate = 100,
enableAudio = false,
enableMusicSound = false,
musicSoundVolume = 100,
enableLights = false,
floorFading = 500,
crosshair = 2,
ambientLight = 100,
optimizationLevel = 1,
displayNames = true,
displayHealth = true,
displayMana = true,
displayHealthOnTop = false,
showHealthManaCircle = true,
hidePlayerBars = true,
highlightThingsUnderCursor = true,
topHealtManaBar = true,
displayText = true,
dontStretchShrink = false,
turnDelay = 30,
hotkeyDelay = 30,
wsadWalking = false,
walkFirstStepDelay = 200,
walkTurnDelay = 100,
walkStairsDelay = 50,
walkTeleportDelay = 200
}
local optionsWindow
local optionsButton
local optionsTabBar
local options = {}
local extraOptions = {}
local generalPanel
local interfacePanel
local consolePanel
local graphicsPanel
local soundPanel
local extrasPanel
local audioButton
function init()
for k,v in pairs(defaultOptions) do
g_settings.setDefault(k, v)
options[k] = v
end
for _, v in ipairs(g_extras.getAll()) do
extraOptions[v] = g_extras.get(v)
g_settings.setDefault("extras_" .. v, extraOptions[v])
end
optionsWindow = g_ui.displayUI('options')
optionsWindow:hide()
optionsTabBar = optionsWindow:getChildById('optionsTabBar')
optionsTabBar:setContentWidget(optionsWindow:getChildById('optionsTabContent'))
g_keyboard.bindKeyDown('Ctrl+Shift+F', function() toggleOption('fullscreen') end)
g_keyboard.bindKeyDown('Ctrl+N', toggleDisplays)
generalPanel = g_ui.loadUI('game')
optionsTabBar:addTab(tr('Game'), generalPanel, '/images/optionstab/game')
interfacePanel = g_ui.loadUI('interface')
optionsTabBar:addTab(tr('Interface'), interfacePanel, '/images/optionstab/game')
consolePanel = g_ui.loadUI('console')
optionsTabBar:addTab(tr('Console'), consolePanel, '/images/optionstab/console')
graphicsPanel = g_ui.loadUI('graphics')
optionsTabBar:addTab(tr('Graphics'), graphicsPanel, '/images/optionstab/graphics')
audioPanel = g_ui.loadUI('audio')
optionsTabBar:addTab(tr('Audio'), audioPanel, '/images/optionstab/audio')
extrasPanel = g_ui.createWidget('Panel')
for _, v in ipairs(g_extras.getAll()) do
local extrasButton = g_ui.createWidget('OptionCheckBox')
extrasButton:setId(v)
extrasButton:setText(g_extras.getDescription(v))
extrasPanel:addChild(extrasButton)
end
if not g_game.getFeature(GameNoDebug) then
optionsTabBar:addTab(tr('Extras'), extrasPanel, '/images/optionstab/extras')
end
optionsButton = modules.client_topmenu.addLeftButton('optionsButton', tr('Options'), '/images/topbuttons/options', toggle)
audioButton = modules.client_topmenu.addLeftButton('audioButton', tr('Audio'), '/images/topbuttons/audio', function() toggleOption('enableAudio') end)
addEvent(function() setup() end)
connect(g_game, { onGameStart = online,
onGameEnd = offline })
end
function terminate()
disconnect(g_game, { onGameStart = online,
onGameEnd = offline })
g_keyboard.unbindKeyDown('Ctrl+Shift+F')
g_keyboard.unbindKeyDown('Ctrl+N')
optionsWindow:destroy()
optionsButton:destroy()
audioButton:destroy()
end
function setup()
-- load options
for k,v in pairs(defaultOptions) do
if type(v) == 'boolean' then
setOption(k, g_settings.getBoolean(k), true)
elseif type(v) == 'number' then
setOption(k, g_settings.getNumber(k), true)
end
end
for _, v in ipairs(g_extras.getAll()) do
g_extras.set(v, g_settings.getBoolean("extras_" .. v))
local widget = extrasPanel:recursiveGetChildById(v)
if widget then
widget:setChecked(g_extras.get(v))
end
end
if g_game.isOnline() then
online()
end
end
function toggle()
if optionsWindow:isVisible() then
hide()
else
show()
end
end
function show()
optionsWindow:show()
optionsWindow:raise()
optionsWindow:focus()
end
function hide()
optionsWindow:hide()
end
function toggleDisplays()
if options['displayNames'] and options['displayHealth'] and options['displayMana'] then
setOption('displayNames', false)
elseif options['displayHealth'] then
setOption('displayHealth', false)
setOption('displayMana', false)
else
if not options['displayNames'] and not options['displayHealth'] then
setOption('displayNames', true)
else
setOption('displayHealth', true)
setOption('displayMana', true)
end
end
end
function toggleOption(key)
setOption(key, not getOption(key))
end
function setOption(key, value, force)
if extraOptions[key] ~= nil then
g_extras.set(key, value)
g_settings.set("extras_" .. key, value)
if key == "debugProxy" and modules.game_proxy then
if value then
modules.game_proxy.show()
else
modules.game_proxy.hide()
end
end
return
end
if modules.game_interface == nil then
return
end
if not force and options[key] == value then return end
local gameMapPanel = modules.game_interface.getMapPanel()
if key == 'vsync' then
g_window.setVerticalSync(value)
elseif key == 'showFps' then
modules.client_topmenu.setFpsVisible(value)
elseif key == 'showPing' then
modules.client_topmenu.setPingVisible(value)
elseif key == 'fullscreen' then
g_window.setFullscreen(value)
elseif key == 'enableAudio' then
if g_sounds ~= nil then
g_sounds.setAudioEnabled(value)
end
if value then
audioButton:setIcon('/images/topbuttons/audio')
else
audioButton:setIcon('/images/topbuttons/audio_mute')
end
elseif key == 'enableMusicSound' then
if g_sounds ~= nil then
g_sounds.getChannel(SoundChannels.Music):setEnabled(value)
end
elseif key == 'musicSoundVolume' then
if g_sounds ~= nil then
g_sounds.getChannel(SoundChannels.Music):setGain(value/100)
end
audioPanel:getChildById('musicSoundVolumeLabel'):setText(tr('Music volume: %d', value))
elseif key == 'showHealthManaCircle' then
modules.game_healthinfo.healthCircle:setVisible(value)
modules.game_healthinfo.healthCircleFront:setVisible(value)
modules.game_healthinfo.manaCircle:setVisible(value)
modules.game_healthinfo.manaCircleFront:setVisible(value)
elseif key == 'backgroundFrameRate' then
local text, v = value, value
if value <= 0 or value >= 201 then text = 'max' v = 0 end
graphicsPanel:getChildById('backgroundFrameRateLabel'):setText(tr('Game framerate limit: %s', text))
g_app.setMaxFps(v)
elseif key == 'enableLights' then
gameMapPanel:setDrawLights(value and options['ambientLight'] < 100)
graphicsPanel:getChildById('ambientLight'):setEnabled(value)
graphicsPanel:getChildById('ambientLightLabel'):setEnabled(value)
elseif key == 'floorFading' then
gameMapPanel:setFloorFading(value)
interfacePanel:getChildById('floorFadingLabel'):setText(tr('Floor fading: %s ms', value))
elseif key == 'crosshair' then
if value == 1 then
gameMapPanel:setCrosshair("")
elseif value == 2 then
gameMapPanel:setCrosshair("/data/images/crosshair/default.png")
elseif value == 3 then
gameMapPanel:setCrosshair("/data/images/crosshair/full.png")
end
elseif key == 'ambientLight' then
graphicsPanel:getChildById('ambientLightLabel'):setText(tr('Ambient light: %s%%', value))
gameMapPanel:setMinimumAmbientLight(value/100)
gameMapPanel:setDrawLights(options['enableLights'] and value < 100)
elseif key == 'optimizationLevel' then
g_adaptiveRenderer.setLevel(value - 2)
elseif key == 'displayNames' then
gameMapPanel:setDrawNames(value)
elseif key == 'displayHealth' then
gameMapPanel:setDrawHealthBars(value)
elseif key == 'displayMana' then
gameMapPanel:setDrawManaBar(value)
elseif key == 'displayHealthOnTop' then
gameMapPanel:setDrawHealthBarsOnTop(value)
elseif key == 'hidePlayerBars' then
gameMapPanel:setDrawPlayerBars(value)
elseif key == 'topHealtManaBar' then
modules.game_healthinfo.topHealthBar:setVisible(value)
modules.game_healthinfo.topManaBar:setVisible(value)
elseif key == 'displayText' then
gameMapPanel:setDrawTexts(value)
elseif key == 'dontStretchShrink' then
addEvent(function()
modules.game_interface.updateStretchShrink()
end)
elseif key == 'extentedPreWalking' then
if value then
g_game.setMaxPreWalkingSteps(2)
else
g_game.setMaxPreWalkingSteps(1)
end
elseif key == 'wsadWalking' then
if modules.game_console and modules.game_console.consoleToggleChat:isChecked() ~= value then
modules.game_console.consoleToggleChat:setChecked(value)
end
elseif key == 'walkFirstStepDelay' then
generalPanel:getChildById('walkFirstStepDelayLabel'):setText(tr('Walk delay after first step: %s ms', value))
elseif key == 'walkTurnDelay' then
generalPanel:getChildById('walkTurnDelayLabel'):setText(tr('Walk delay after turn: %s ms', value))
elseif key == 'walkStairsDelay' then
generalPanel:getChildById('walkStairsDelayLabel'):setText(tr('Walk delay after floor change: %s ms', value))
elseif key == 'walkTeleportDelay' then
generalPanel:getChildById('walkTeleportDelayLabel'):setText(tr('Walk delay after teleport: %s ms', value))
end
-- change value for keybind updates
for _,panel in pairs(optionsTabBar:getTabsPanel()) do
local widget = panel:recursiveGetChildById(key)
if widget then
if widget:getStyle().__class == 'UICheckBox' then
widget:setChecked(value)
elseif widget:getStyle().__class == 'UIScrollBar' then
widget:setValue(value)
elseif widget:getStyle().__class == 'UIComboBox' then
if valur ~= nil or value < 1 then
value = 1
end
if widget.currentIndex ~= value then
widget:setCurrentIndex(value)
end
end
break
end
end
g_settings.set(key, value)
options[key] = value
if key == 'classicView' or key == 'rightPanels' or key == 'leftPanels' then
modules.game_interface.refreshViewMode()
end
end
function getOption(key)
return options[key]
end
function addTab(name, panel, icon)
optionsTabBar:addTab(name, panel, icon)
end
function addButton(name, func, icon)
optionsTabBar:addButton(name, func, icon)
end
-- hide/show
function online()
setLightOptionsVisibility(not g_game.getFeature(GameForceLight))
end
function offline()
setLightOptionsVisibility(true)
end
-- classic view
-- graphics
function setLightOptionsVisibility(value)
graphicsPanel:getChildById('enableLights'):setEnabled(value)
graphicsPanel:getChildById('ambientLightLabel'):setEnabled(value)
graphicsPanel:getChildById('ambientLight'):setEnabled(value)
interfacePanel:getChildById('floorFading'):setEnabled(value)
interfacePanel:getChildById('floorFadingLabel'):setEnabled(value)
interfacePanel:getChildById('floorFadingLabel2'):setEnabled(value)
end

View File

@@ -0,0 +1,9 @@
Module
name: client_options
description: Create the options window
author: edubart, BeniS
website: https://github.com/edubart/otclient
sandboxed: true
scripts: [ options ]
@onLoad: init()
@onUnload: terminate()

View File

@@ -0,0 +1,49 @@
OptionCheckBox < CheckBox
@onCheckChange: modules.client_options.setOption(self:getId(), self:isChecked())
height: 16
$first:
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
$!first:
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 2
OptionScrollbar < HorizontalScrollBar
step: 1
@onValueChange: modules.client_options.setOption(self:getId(), self:getValue())
MainWindow
id: optionsWindow
!text: tr('Options')
size: 480 420
@onEnter: modules.client_options.hide()
@onEscape: modules.client_options.hide()
TabBarVertical
id: optionsTabBar
anchors.top: parent.top
anchors.left: parent.left
anchors.bottom: parent.bottom
Panel
id: optionsTabContent
anchors.top: optionsTabBar.top
anchors.left: optionsTabBar.right
anchors.right: parent.right
anchors.bottom: optionsTabBar.bottom
margin-left: 10
Button
!text: tr('Ok')
width: 64
anchors.right: parent.right
anchors.bottom: parent.bottom
@onClick: |
g_settings.save()
modules.client_options.hide()