diff --git a/data/fonts/terminus-10px.otfont b/data/fonts/terminus-10px.otfont
new file mode 100644
index 00000000..957da0de
--- /dev/null
+++ b/data/fonts/terminus-10px.otfont
@@ -0,0 +1,8 @@
+Font
+  name: terminus-10px
+  texture: terminus-10px
+  height: 12
+  y-offset: 0
+  glyph-size: 16 16
+  fixed-glyph-width: 6
+  space-width: 6
diff --git a/data/fonts/terminus-10px.png b/data/fonts/terminus-10px.png
new file mode 100644
index 00000000..4e8f500c
Binary files /dev/null and b/data/fonts/terminus-10px.png differ
diff --git a/modules/client/client.otmod b/modules/client/client.otmod
index 31ffa32b..e66a7d0d 100644
--- a/modules/client/client.otmod
+++ b/modules/client/client.otmod
@@ -14,8 +14,8 @@ Module
     - client_locales
     - client_topmenu
     - client_background
-    - client_entergame
     - client_options
+    - client_entergame
     - client_terminal
     - client_modulemanager
     - client_serverlist
diff --git a/modules/client_locales/locales.lua b/modules/client_locales/locales.lua
index b0642f49..ecf69966 100644
--- a/modules/client_locales/locales.lua
+++ b/modules/client_locales/locales.lua
@@ -121,7 +121,7 @@ function installLocale(locale)
     end
 
     if updatesNeeded > 0 then
-      pwarning('Locale \'' .. locale.name .. '\' is missing ' .. updatesNeeded .. ' translations.')
+      pdebug('Locale \'' .. locale.name .. '\' is missing ' .. updatesNeeded .. ' translations.')
     end
   end
 
@@ -182,7 +182,7 @@ function _G.tr(text, ...)
       if not translation then
         if translation == nil then
           if currentLocale.name ~= defaultLocaleName then
-            pwarning('Unable to translate: \"' .. text .. '\"')
+            pdebug('Unable to translate: \"' .. text .. '\"')
           end
         end
         translation = text
diff --git a/modules/client_locales/locales.otmod b/modules/client_locales/locales.otmod
index 403081b8..623701e7 100644
--- a/modules/client_locales/locales.otmod
+++ b/modules/client_locales/locales.otmod
@@ -5,6 +5,5 @@ Module
   website: www.otclient.info
   sandboxed: true
   scripts: [ locales ]
-  dependencies: [ client_topmenu ]
   @onLoad: init()
   @onUnload: terminate()
diff --git a/modules/client_options/options.lua b/modules/client_options/options.lua
index 91459bba..0fc0431f 100644
--- a/modules/client_options/options.lua
+++ b/modules/client_options/options.lua
@@ -82,45 +82,20 @@ local function setupGraphicsEngines()
 end
 
 function init()
+  for k,v in pairs(defaultOptions) do
+    g_settings.setDefault(k, v)
+    options[k] = v
+  end
+
   optionsWindow = g_ui.displayUI('options')
   optionsWindow:hide()
-  optionsButton = modules.client_topmenu.addLeftButton('optionsButton', tr('Options'), '/images/topbuttons/options', toggle)
 
   optionsTabBar = optionsWindow:getChildById('optionsTabBar')
   optionsTabBar:setContentWidget(optionsWindow:getChildById('optionsTabContent'))
 
-  addEvent(function() setup() end)
-
   g_keyboard.bindKeyDown('Ctrl+Shift+F', function() toggleOption('fullscreen') end)
   g_keyboard.bindKeyDown('Ctrl+N', toggleDisplays)
 
-  audioButton = modules.client_topmenu.addLeftButton('audioButton', tr('Audio'), '/images/topbuttons/audio', function() toggleOption('enableAudio') end)
-end
-
-function terminate()
-  g_keyboard.unbindKeyDown('Ctrl+Shift+F')
-  g_keyboard.unbindKeyDown('Ctrl+N')
-  optionsWindow:destroy()
-  optionsButton:destroy()
-  audioButton:destroy()
-  optionsTabBar = nil
-  generalPanel = nil
-  consolePanel = nil
-  graphicsPanel = nil
-  audioPanel = nil
-end
-
-function setup()
-  -- load options
-  for k,v in pairs(defaultOptions) do
-    g_settings.setDefault(k, v)
-    if type(v) == 'boolean' then
-      setOption(k, g_settings.getBoolean(k))
-    elseif type(v) == 'number' then
-      setOption(k, g_settings.getNumber(k))
-    end
-  end
-
   generalPanel = g_ui.loadUI('game')
   optionsTabBar:addTab(tr('Game'), generalPanel, '/images/optionstab/game')
 
@@ -133,7 +108,31 @@ function setup()
   audioPanel = g_ui.loadUI('audio')
   optionsTabBar:addTab(tr('Audio'), audioPanel, '/images/optionstab/audio')
 
+  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)
+end
+
+function terminate()
+  g_keyboard.unbindKeyDown('Ctrl+Shift+F')
+  g_keyboard.unbindKeyDown('Ctrl+N')
+  optionsWindow:destroy()
+  optionsButton:destroy()
+  audioButton:destroy()
+end
+
+function setup()
   setupGraphicsEngines()
+
+  -- 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
 end
 
 function toggle()
@@ -172,8 +171,8 @@ function toggleOption(key)
   setOption(key, not getOption(key))
 end
 
-function setOption(key, value)
-  if options[key] == value then return end
+function setOption(key, value, force)
+  if not force and options[key] == value then return end
   local gameMapPanel = modules.game_interface.getMapPanel()
 
   local panel = nil
@@ -188,66 +187,37 @@ function setOption(key, value)
     panel = graphicsPanel
   elseif key == 'enableAudio' then
     g_sounds.setAudioEnabled(value)
-    addEvent(function()
-      if value then
-        audioButton:setIcon('/images/topbuttons/audio')
-      else
-        audioButton:setIcon('/images/topbuttons/audio_mute')
-      end
-    end)
+    if value then
+      audioButton:setIcon('/images/topbuttons/audio')
+    else
+      audioButton:setIcon('/images/topbuttons/audio_mute')
+    end
     panel = audioPanel
   elseif key == 'enableMusicSound' then
     g_sounds.getChannel(SoundChannels.Music):setEnabled(value)
   elseif key == 'musicSoundVolume' then
     g_sounds.getChannel(SoundChannels.Music):setGain(value/100)
-    if audioPanel then
-      audioPanel:getChildById('musicSoundVolumeLabel'):setText(tr('Music volume: %d', value))
-    end
+    audioPanel:getChildById('musicSoundVolumeLabel'):setText(tr('Music volume: %d', value))
   elseif key == 'showLeftPanel' then
-    addEvent(function()
-      modules.game_interface.getLeftPanel():setOn(value)
-    end)
+    modules.game_interface.getLeftPanel():setOn(value)
   elseif key == 'backgroundFrameRate' then
     local text = value
-    if value <= 0 or value >= 201 then
-      text = 'max'
-      value = 0
-    end
-
-    if graphicsPanel then
-      graphicsPanel:getChildById('backgroundFrameRateLabel'):setText(tr('Game framerate limit: %s', text))
-    end
+    if value <= 0 or value >= 201 then text = 'max'  value = 0  end
+    graphicsPanel:getChildById('backgroundFrameRateLabel'):setText(tr('Game framerate limit: %s', text))
     g_app.setBackgroundPaneMaxFps(value)
   elseif key == 'foregroundFrameRate' then
     local text = value
-    if value <= 0 or value >= 61 then
-      text = 'max'
-      value = 0
-    end
-
-    if graphicsPanel then
-      graphicsPanel:getChildById('foregroundFrameRateLabel'):setText(tr('Interface framerate limit: %s', text))
-    end
+    if value <= 0 or value >= 61 then text = 'max' value = 0 end
+    graphicsPanel:getChildById('foregroundFrameRateLabel'):setText(tr('Interface framerate limit: %s', text))
     g_app.setForegroundPaneMaxFps(value)
   elseif key == 'enableLights' then
-    addEvent(function()
-      local map = modules.game_interface.getMapPanel()
-      map:setDrawLights(value and options['ambientLight'] < 100)
-
-      if graphicsPanel then
-        graphicsPanel:getChildById('ambientLight'):setEnabled(value)
-        graphicsPanel:getChildById('ambientLightLabel'):setEnabled(value)
-      end
-    end)
+    gameMapPanel:setDrawLights(value and options['ambientLight'] < 100)
+    graphicsPanel:getChildById('ambientLight'):setEnabled(value)
+    graphicsPanel:getChildById('ambientLightLabel'):setEnabled(value)
   elseif key == 'ambientLight' then
-    addEvent(function()
-      local map = modules.game_interface.getMapPanel()
-      if graphicsPanel then
-        graphicsPanel:getChildById('ambientLightLabel'):setText(tr('Ambient light: %s%%', value))
-      end
-      map:setMinimumAmbientLight(value/100)
-      map:setDrawLights(options['enableLights'] and value < 100)
-    end)
+    graphicsPanel:getChildById('ambientLightLabel'):setText(tr('Ambient light: %s%%', value))
+    gameMapPanel:setMinimumAmbientLight(value/100)
+    gameMapPanel:setDrawLights(options['enableLights'] and value < 100)
   elseif key == 'painterEngine' then
     g_graphics.selectPainterEngine(value)
   elseif key == 'displayNames' then
diff --git a/modules/client_options/options.otmod b/modules/client_options/options.otmod
index 1b99917a..8216f644 100644
--- a/modules/client_options/options.otmod
+++ b/modules/client_options/options.otmod
@@ -4,7 +4,6 @@ Module
   author: edubart, BeniS
   website: www.otclient.info
   sandboxed: true
-  dependencies: [ client_topmenu ]
   scripts: [ options ]
   @onLoad: init()
   @onUnload: terminate()
diff --git a/modules/client_terminal/commands.lua b/modules/client_terminal/commands.lua
index 5bd5b4cc..71c5cbaa 100644
--- a/modules/client_terminal/commands.lua
+++ b/modules/client_terminal/commands.lua
@@ -1,84 +1,17 @@
-function dumpWidgets(widget, level)
-  widget = widget or rootWidget
-  level = level or 0
-  for i=1,widget:getChildCount() do
-    local child = widget:getChildByIndex(i)
-    if child:isVisible() then
-      local name = child:getId()
-      if name:match('widget%d+') == nil then
-        print(string.rep(' ', level) .. name)
-      end
-      if child:getId() ~= 'terminalBuffer' then
-        dumpWidgets(child, level+1)
-      end
-    end
-  end
-end
-
-function drawDebugBoxes(enable)
+function draw_debug_boxes(enable)
   if enable == nil then enable = true end
   g_ui.setDebugBoxesDrawing(enable)
 end
 
-function hideMap()
-  local map = rootWidget:recursiveGetChildById('gameMapPanel')
-  if map then map:hide() end
+function hide_map()
+  modules.game_interface.getMapPanel():hide()
 end
 
-function showMap()
-  local map = rootWidget:recursiveGetChildById('gameMapPanel')
-  if map then map:show() end
+function show_map()
+  modules.game_interface.getMapPanel():show()
 end
 
-function debugContainersItems()
-  function UIItem:onHoverChange(hovered)
-    if hovered then
-      local item = self:getItem()
-      if item then
-        local text = "id: " ..item:getId() .. 
-                     "\n stackable: " ..tostring(item:isStackable()) ..
-                     "\n marketable: " ..tostring(item:isMarketable()) ..
-                     "\n vocation: "..(item:getMarketData() and item:getMarketData().restrictVocation or 'none') ..
-                     "\n cloth slot: " ..item:getClothSlot()
-        g_tooltip.display(text)
-      end
-    else
-      g_tooltip.hide()
-    end
-  end
-end
-
-function debugPosition(enable)
-  if enable == nil then enable = true end
-  local label = rootWidget:getChildById('debugPositionLabel')
-  if not label then
-    label = g_ui.createWidget('GameLabel', rootWidget)
-    label:setColor('pink')
-    label:setFont('terminus-14px-bold')
-    label:setId('debugPositionLabel')
-    label:setPosition({x= 10, y = 40 })
-    label:setPhantom(true)
-    label:setTextAutoResize(true)
-  end
-  if enable then
-    label.event = cycleEvent(function()
-      local player = g_game.getLocalPlayer()
-      if player then
-        local pos = g_game.getLocalPlayer():getPosition()
-        label:show()
-        label:setText('x: ' .. pos.x .. '\ny: ' .. pos.y .. '\nz: ' .. pos.z)
-      else
-        label:hide()
-      end
-    end, 100)
-  else
-    removeEvent(label.event)
-    label.event = nil
-    label:hide()
-  end
-end
-
-function autoReloadModule(name)
+function auto_reload_module(name)
   local function reloadEvent()
     reloadModule(name)
     scheduleEvent(reloadEvent, 1000)
@@ -86,14 +19,53 @@ function autoReloadModule(name)
   reloadEvent()
 end
 
-function createDebugUIItem(id)
-  local uiitem = g_ui.createWidget('Item', rootWidget)
-  uiitem:setPosition({x= 200, y = 200 })
-  uiitem:setItemId(id)
-  uiitem:show()
+local function pingBack(ping) print(g_game.getWorldName() .. ' => ' .. ping .. ' ms') end
+local pinging = false
+function ping()
+  if pinging then
+    pdebug('Ping stopped.')
+    g_game.setPingDelay(1000)
+    disconnect(g_game, 'onPingBack', pingBack)
+  else
+    if not (g_game.getFeature(GameClientPing) or g_game.getFeature(GameExtendedClientPing)) then
+      perror('this server does not support ping')
+      return
+    elseif not g_game.isOnline() then
+      perror('ping command is only allowed when online')
+      return
+    end
+
+    pdebug('Starting ping...')
+    g_game.setPingDelay(0)
+    connect(g_game, 'onPingBack', pingBack)
+  end
+  pinging = not pinging
 end
 
-function debugPings()
-  g_game.setPingDelay(0)
-  connect(g_game, { onPingBack = function(ping) print(g_game.getWorldName() .. ' => ' .. ping .. ' ms') end })
+function clear()
+  modules.client_terminal.clear()
+end
+
+function ls(path)
+  path = path or '/'
+  local files = g_resources.listDirectoryFiles(path)
+  for k,v in pairs(files) do
+    if g_resources.directoryExists(path .. v) then
+      modules.client_terminal.addLine(path .. v, 'blue')
+    else
+      pinfo(path .. v)
+    end
+  end
+end
+
+function about_version()
+  pinfo(g_app.getName() .. ' ' .. g_app.getVersion() .. '\n' ..
+        'Rev  ' .. g_app.getBuildRevision() .. ' ('.. g_app.getBuildCommit() .. ')\n' ..
+        'Built on ' .. g_app.getBuildDate())
+end
+
+function about_graphics()
+  pinfo('Vendor ' .. g_graphics.getVendor() )
+  pinfo('Renderer' .. g_graphics.getRenderer())
+  pinfo('Version' .. g_graphics.getVersion())
 end
diff --git a/modules/client_terminal/terminal.lua b/modules/client_terminal/terminal.lua
index 49601f38..db9b3ed5 100644
--- a/modules/client_terminal/terminal.lua
+++ b/modules/client_terminal/terminal.lua
@@ -1,17 +1,21 @@
 -- configs
-local LogColors = { [LogInfo] = 'white',
+local LogColors = { [LogDebug] = 'pink',
+                    [LogInfo] = 'white',
                     [LogWarning] = 'yellow',
                     [LogError] = 'red' }
-local MaxLogLines = 80
+local MaxLogLines = 512
 local LabelHeight = 16
 local MaxHistory = 1000
 
+local oldenv = getfenv(0)
+setfenv(0, _G)
+commandEnv = runinsandbox('commands')
+setfenv(0, oldenv)
+
 -- private variables
 local terminalWindow
 local terminalButton
 local logLocked = false
-local commandEnv = {}
-setmetatable(commandEnv, { __index = getfenv() } )
 local commandTextEdit
 local terminalBuffer
 local commandHistory = { }
@@ -96,9 +100,6 @@ local function doCommand()
 end
 
 local function onLog(level, message, time)
-  -- debug messages are ignored
-  if level == LogDebug then return end
-
   -- avoid logging while reporting logs (would cause a infinite loop)
   if logLocked then return end
 
@@ -137,11 +138,19 @@ function init()
   commandTextEdit = terminalWindow:getChildById('commandTextEdit')
   g_keyboard.bindKeyPress('Up', function() navigateCommand(1) end, commandTextEdit)
   g_keyboard.bindKeyPress('Down', function() navigateCommand(-1) end, commandTextEdit)
+  g_keyboard.bindKeyPress('Ctrl+C', 
+    function() 
+      if commandTextEdit:hasSelection() or not terminalSelectText:hasSelection() then return false end
+      g_window.setClipboardText(terminalSelectText:getSelection())
+    return true 
+    end, commandTextEdit)
   g_keyboard.bindKeyDown('Tab', completeCommand, commandTextEdit)
   g_keyboard.bindKeyDown('Enter', doCommand, commandTextEdit)
   g_keyboard.bindKeyDown('Escape', hide, terminalWindow)
 
-  terminalBuffer = terminalWindow:getChildById('terminalBuffer')
+  terminalBuffer = terminalWindow:recursiveGetChildById('terminalBuffer')
+  terminalSelectText = terminalWindow:recursiveGetChildById('terminalSelectText')
+
   g_logger.setOnLog(onLog)
   g_logger.fireOldMessages()
 end
@@ -194,6 +203,8 @@ function addLine(text, color)
   label:setId('terminalLabel' .. numLines)
   label:setText(text)
   label:setColor(color)
+
+  terminalSelectText:setText(terminalSelectText:getText() .. '\n' .. text)
 end
 
 function executeCommand(command)
@@ -203,6 +214,18 @@ function executeCommand(command)
   g_logger.log(LogInfo, '> ' .. command)
   logLocked = false
 
+  local func
+  local err
+
+  -- detect terminal commands
+  local command_name = command:match('^([%w_]+)[%s]*.*')
+  if command_name then
+    local args = string.split(command:match('^[%w]+[%s]*(.*)'), ' ')
+    if commandEnv[command_name] and type(commandEnv[command_name]) == 'function' then
+      func = function() modules.client_terminal.commandEnv[command_name](unpack(args)) end
+    end
+  end
+
   -- detect and convert commands with simple syntax
   local realCommand
   if string.sub(command, 1, 1) == '=' then
@@ -226,23 +249,32 @@ function executeCommand(command)
   --addLine(">> " .. command, "#ffffff")
 
   -- load command buffer
-  local func, err = loadstring(realCommand, "@")
-
-  -- check for syntax errors
   if not func then
-    g_logger.log(LogError, 'incorrect lua syntax: ' .. err:sub(5))
-    return
+    func, err = loadstring(realCommand, "@")
+
+    -- check for syntax errors
+    if not func then
+      g_logger.log(LogError, 'incorrect lua syntax: ' .. err:sub(5))
+      return
+    end
   end
 
   -- setup func env to commandEnv
   setfenv(func, commandEnv)
 
   -- execute the command
-  local ok, ret = pcall(func)
-  if ok then
-    -- if the command returned a value, print it
-    if ret then print(ret) end
-  else
-    g_logger.log(LogError, 'command failed: ' .. ret)
-  end
+  addEvent(function()
+    local ok, ret = pcall(func)
+    if ok then
+      -- if the command returned a value, print it
+      if ret then print(ret) end
+    else
+      g_logger.log(LogError, 'command failed: ' .. ret)
+    end
+  end)
+end
+
+function clear()
+  terminalBuffer:destroyChildren()
+  terminalSelectText:setText('')
 end
diff --git a/modules/client_terminal/terminal.otmod b/modules/client_terminal/terminal.otmod
index 5460b356..e3dc5115 100644
--- a/modules/client_terminal/terminal.otmod
+++ b/modules/client_terminal/terminal.otmod
@@ -3,7 +3,7 @@ Module
   description: Terminal for executing lua functions
   author: edubart
   website: www.otclient.info
-  scripts: [ terminal, commands ]
+  scripts: [ terminal ]
   sandboxed: true
   @onLoad: init()
   @onUnload: terminate()
diff --git a/modules/client_terminal/terminal.otui b/modules/client_terminal/terminal.otui
index 9e6945e0..ec9c01ae 100644
--- a/modules/client_terminal/terminal.otui
+++ b/modules/client_terminal/terminal.otui
@@ -1,7 +1,21 @@
 TerminalLabel < UILabel
-  font: terminus-14px-bold
+  font: terminus-10px
   text-wrap: true
   text-auto-resize: true
+  phantom: true
+
+TerminalSelectText < UITextEdit
+  font: terminus-10px
+  text-wrap: true
+  text-align: bottomLeft
+  editable: false
+  change-cursor-image: false
+  cursor-visible: false
+  selection-color: black
+  selection-background-color: white
+  color: alpha
+  focusable: false
+  auto-scroll: false
 
 UIWindow
   id: terminalWindow
@@ -10,35 +24,57 @@ UIWindow
   clipping: true
   anchors.fill: parent
 
-  Panel
-    id: terminalBuffer
-    layout:
-      type: verticalBox
-      fit-children: true
+  ScrollablePanel
+    id: terminalScrollArea
     focusable: false
     anchors.left: parent.left
-    anchors.right: parent.right
+    anchors.right: terminalScroll.left
+    anchors.top: parent.top
     anchors.bottom: commandSymbolLabel.top
+    vertical-scrollbar: terminalScroll
+    inverted-scroll: true
     margin-left: 2
 
+    Panel
+      anchors.bottom: parent.bottom
+      anchors.left: parent.left
+      anchors.right: parent.right
+      id: terminalBuffer
+      layout:
+        type: verticalBox
+        fit-children: true
+      focusable: false
+
+    TerminalSelectText
+      id: terminalSelectText
+      anchors.fill: terminalBuffer
+
+  VerticalScrollBar
+    id: terminalScroll
+    anchors.top: parent.top
+    anchors.bottom: parent.bottom
+    anchors.right: parent.right
+    step: 48
+    pixels-scroll: true
+
   UILabel
     id: commandSymbolLabel
-    size: 12 16
+    size: 12 12
     fixed-size: true
     anchors.bottom: parent.bottom
     anchors.left: parent.left
     margin-left: 2
-    font: terminus-14px-bold
+    font: terminus-10px
     text: >
 
   UITextEdit
     id: commandTextEdit
-    height: 16
+    height: 12
     anchors.bottom: parent.bottom
     anchors.left: commandSymbolLabel.right
     anchors.right: parent.right
-    margin-left: 5
-    font: terminus-14px-bold
+    margin-left: 1
+    font: terminus-10px
     selection-color: black
     selection-background-color: white
 
diff --git a/modules/client_topmenu/topmenu.lua b/modules/client_topmenu/topmenu.lua
index cd53486b..e4a06c4b 100644
--- a/modules/client_topmenu/topmenu.lua
+++ b/modules/client_topmenu/topmenu.lua
@@ -68,11 +68,13 @@ end
 function online()
   showGameButtons()
 
-  if g_settings.getBoolean('showPing') and (g_game.getFeature(GameClientPing) or g_game.getFeature(GameExtendedClientPing)) then
-    pingLabel:show()
-  else
-    pingLabel:hide()
-  end
+  addEvent(function()
+    if modules.client_options.getOption('showPing') and (g_game.getFeature(GameClientPing) or g_game.getFeature(GameExtendedClientPing)) then
+      pingLabel:show()
+    else
+      pingLabel:hide()
+    end
+  end)
 end
 
 function offline()
diff --git a/modules/corelib/ui/uiminiwindowcontainer.lua b/modules/corelib/ui/uiminiwindowcontainer.lua
index 7c00d67c..37cdfb7d 100644
--- a/modules/corelib/ui/uiminiwindowcontainer.lua
+++ b/modules/corelib/ui/uiminiwindowcontainer.lua
@@ -127,7 +127,7 @@ end
 function UIMiniWindowContainer:scheduleInsert(widget, index)
   if index - 1 > self:getChildCount() then
     if self.scheduledWidgets[index] then
-      pwarning('replacing scheduled widget id ' .. widget:getId())
+      pdebug('replacing scheduled widget id ' .. widget:getId())
     end
     self.scheduledWidgets[index] = widget
   else
diff --git a/modules/game_console/violationwindow.otui b/modules/game_console/violationwindow.otui
index 0b6c14d8..ad2df92a 100644
--- a/modules/game_console/violationwindow.otui
+++ b/modules/game_console/violationwindow.otui
@@ -10,6 +10,7 @@ MainWindow
     anchors.left: parent.left
     anchors.right: parent.right
     anchors.top: parent.top
+    max-length: 255
 
   TextEdit
     id: text
diff --git a/modules/game_minimap/minimap.otui b/modules/game_minimap/minimap.otui
index 437bb68e..fa156c7b 100644
--- a/modules/game_minimap/minimap.otui
+++ b/modules/game_minimap/minimap.otui
@@ -10,7 +10,7 @@ MiniWindow
     text: ?
     text-align: center
     phantom: false
-    !tooltip: tr('Hold left mouse button to navigate\nScroll mouse middle button to zoom\nRight mouse button to create map marks\nPress Ctrl+Shift+M to view the entire game map')')
+    !tooltip: tr('Hold left mouse button to navigate\nScroll mouse middle button to zoom\nRight mouse button to create map marks\nPress Ctrl+Shift+M to view the entire game map')
     anchors.top: minimizeButton.top
     anchors.right: minimizeButton.left
     margin-right: 3
diff --git a/src/client/tile.cpp b/src/client/tile.cpp
index f659729b..8daaf189 100644
--- a/src/client/tile.cpp
+++ b/src/client/tile.cpp
@@ -542,8 +542,15 @@ bool Tile::isLookPossible()
 
 bool Tile::isClickable()
 {
+    bool hasGround = false;
+    bool hasOnBottom = false;
+    bool hasIgnoreLook = false;
     for(const ThingPtr& thing : m_things) {
-        if(!thing->isOnTop() && !thing->isIgnoreLook())
+        if(thing->isGround())
+            hasGround = true;
+        if(thing->isOnBottom())
+            hasOnBottom = true;
+        if((hasGround || hasOnBottom) && !hasIgnoreLook)
             return true;
     }
     return false;
diff --git a/src/framework/ui/uitextedit.cpp b/src/framework/ui/uitextedit.cpp
index 232da156..91b4f09f 100644
--- a/src/framework/ui/uitextedit.cpp
+++ b/src/framework/ui/uitextedit.cpp
@@ -41,6 +41,7 @@ UITextEdit::UITextEdit()
     m_maxLength = 0;
     m_editable = true;
     m_selectable = true;
+    m_autoScroll = false;
     m_changeCursorImage = true;
     m_selectionReference = 0;
     m_selectionStart = 0;
@@ -164,7 +165,7 @@ void UITextEdit::update(bool focusCursor)
 
     // readjust start view area based on cursor position
     m_cursorInRange = false;
-    if(focusCursor) {
+    if(focusCursor && m_autoScroll) {
         if(m_cursorPos > 0 && textLength > 0) {
                 assert(m_cursorPos <= textLength);
                 Rect virtualRect(m_textVirtualOffset, m_rect.size() - Size(m_padding.left+m_padding.right, 0)); // previous rendered virtual rect
@@ -628,6 +629,8 @@ void UITextEdit::onStyleApply(const std::string& styleName, const OTMLNodePtr& s
             setCursorVisible(node->value<bool>());
         else if(node->tag() == "change-cursor-image")
             setChangeCursorImage(node->value<bool>());
+        else if(node->tag() == "auto-scroll")
+            setAutoScroll(node->value<bool>());
     }
 }
 
diff --git a/src/framework/ui/uitextedit.h b/src/framework/ui/uitextedit.h
index de253d78..7a456bf9 100644
--- a/src/framework/ui/uitextedit.h
+++ b/src/framework/ui/uitextedit.h
@@ -51,6 +51,7 @@ public:
     void setSelectable(bool selectable) { m_selectable = selectable; }
     void setSelectionColor(const Color& color) { m_selectionColor = color; }
     void setSelectionBackgroundColor(const Color& color) { m_selectionBackgroundColor = color; }
+    void setAutoScroll(bool autoScroll) { m_autoScroll = autoScroll; }
 
     void moveCursorHorizontally(bool right);
     void moveCursorVertically(bool up);
@@ -87,6 +88,7 @@ public:
     bool isMultiline() { return m_multiline; }
     bool isEditable() { return m_editable; }
     bool isSelectable() { return m_selectable; }
+    bool isAutoScrolling() { return m_autoScroll; }
 
 protected:
     void updateText();
@@ -123,6 +125,7 @@ private:
     std::string m_validCharacters;
     uint m_maxLength;
     bool m_updatesEnabled;
+    bool m_autoScroll;
 
     bool m_selectable;
     int m_selectionReference;