diff --git a/game/engines/default/engine/Mouse.lua b/game/engines/default/engine/Mouse.lua
index 7121c90dc51ca0347037afd04f6baab3c9f884b3..dd27ff40bb7215b841659e96ac4faba04246b3ee 100644
--- a/game/engines/default/engine/Mouse.lua
+++ b/game/engines/default/engine/Mouse.lua
@@ -40,7 +40,7 @@ function _M:receiveMouse(button, x, y, isup, force_name, extra)
 	if not isup then return end
 
 	if _M.drag then
-		if _M.drag.prestart then _M.drag = nil print("====?????")
+		if _M.drag.prestart then _M.drag = nil
 		else return self:endDrag(x, y) end
 	end
 
@@ -145,22 +145,27 @@ function _M:reset()
 	self.areas = {}
 end
 
-function _M:startDrag(x, y, cursor, payload, on_done, on_move)
+function _M:startDrag(x, y, cursor, payload, on_done, on_move, no_prestart)
+	local start = function()
+		_M.drag.prestart = nil
+		if _M.drag.cursor then
+			local w, h = _M.drag.cursor:getSize()
+			_M.drag.cursor = _M.drag.cursor:glTexture()
+			core.display.setMouseDrag(_M.drag.cursor, w, h)
+		end
+		print("[MOUSE] enabling drag from predrag")
+	end
+
 	if _M.drag then
 		if _M.drag.prestart and math.max(math.abs(_M.drag.start_x - x), math.abs(_M.drag.start_y - y)) > 6 then
-			_M.drag.prestart = nil
-			if _M.drag.cursor then
-				local w, h = _M.drag.cursor:getSize()
-				_M.drag.cursor = _M.drag.cursor:glTexture()
-				core.display.setMouseDrag(_M.drag.cursor, w, h)
-			end
-			print("[MOUSE] enabling drag from predrag")
+			start()
 		end
 		return
 	end
 
 	_M.drag = {start_x=x, start_y=y, payload=payload, on_done=on_done, on_move=on_move, prestart=true, cursor=cursor}
 	print("[MOUSE] pre starting drag'n'drop")
+	if no_prestart then start() end
 end
 
 function _M:endDrag(x, y)
diff --git a/game/modules/tome/class/uiset/Minimalist.lua b/game/modules/tome/class/uiset/Minimalist.lua
index 43e85bd809859e5aee69b1d8fa1d05fc8d55ef4a..0cc437b7c9dea213c9b024d3f88c29041a45c361 100644
--- a/game/modules/tome/class/uiset/Minimalist.lua
+++ b/game/modules/tome/class/uiset/Minimalist.lua
@@ -34,6 +34,108 @@ local Map = require "engine.Map"
 
 module(..., package.seeall, class.inherit(UISet, TooltipsData))
 
+local move_handle = {core.display.loadImage("/data/gfx/ui/move_handle.png"):glTexture()}
+
+-- Load the various shaders used to display resources
+air_c = {0x92/255, 0xe5, 0xe8}
+air_sha = Shader.new("resources", {color=air_c, speed=100, amp=0.8, distort={2,2.5}})
+life_c = {0xc0/255, 0, 0}
+life_sha = Shader.new("resources", {color=life_c, speed=1000, distort={1.5,1.5}})
+shield_c = {0.5, 0.5, 0.5}
+shield_sha = Shader.new("resources", {color=shield_c, speed=5000, a=0.8, distort={0.5,0.5}})
+stam_c = {0xff/255, 0xcc/255, 0x80/255}
+stam_sha = Shader.new("resources", {color=stam_c, speed=700, distort={1,1.4}})
+mana_c = {106/255, 146/255, 222/255}
+mana_sha = Shader.new("resources", {color=mana_c, speed=1000, distort={0.4,0.4}})
+soul_c = {128/255, 128/255, 128/255}
+soul_sha = Shader.new("resources", {color=soul_c, speed=1200, distort={0.4,-0.4}})
+equi_c = {0x00/255, 0xff/255, 0x74/255}
+equi_sha = Shader.new("resources", {color=equi_c, amp=0.8, speed=20000, distort={0.3,0.25}})
+paradox_c = {0x2f/255, 0xa0/255, 0xb4/255}
+paradox_sha = Shader.new("resources", {color=paradox_c, amp=0.8, speed=20000, distort={0.1,0.25}})
+pos_c = {colors.GOLD.r/255, colors.GOLD.g/255, colors.GOLD.b/255}
+pos_sha = Shader.new("resources", {color=pos_c, speed=1000, distort={1.6,0.2}})
+neg_c = {colors.DARK_GREY.r/255, colors.DARK_GREY.g/255, colors.DARK_GREY.b/255}
+neg_sha = Shader.new("resources", {color=neg_c, speed=1000, distort={1.6,-0.2}})
+vim_c = {0x90/255, 0x40/255, 0x10/255}
+vim_sha = Shader.new("resources", {color=vim_c, speed=1000, distort={0.4,0.4}})
+hate_c = {colors.GREY.r/255, colors.GREY.g/255, colors.GREY.b/255}
+hate_sha = Shader.new("resources", {color=hate_c, speed=1000, distort={0.4,0.4}})
+psi_c = {colors.BLUE.r/255, colors.BLUE.g/255, colors.BLUE.b/255}
+psi_sha = Shader.new("resources", {color=psi_c, speed=2000, distort={0.4,0.4}})
+save_c = pos_c
+save_sha = pos_sha
+
+sshat = {core.display.loadImage("/data/gfx/ui/resources/shadow.png"):glTexture()}
+bshat = {core.display.loadImage("/data/gfx/ui/resources/back.png"):glTexture()}
+shat = {core.display.loadImage("/data/gfx/ui/resources/fill.png"):glTexture()}
+fshat = {core.display.loadImage("/data/gfx/ui/resources/front.png"):glTexture()}
+fshat_life = {core.display.loadImage("/data/gfx/ui/resources/front_life.png"):glTexture()}
+fshat_life_dark = {core.display.loadImage("/data/gfx/ui/resources/front_life_dark.png"):glTexture()}
+fshat_shield = {core.display.loadImage("/data/gfx/ui/resources/front_life_armored.png"):glTexture()}
+fshat_shield_dark = {core.display.loadImage("/data/gfx/ui/resources/front_life_armored_dark.png"):glTexture()}
+fshat_stamina = {core.display.loadImage("/data/gfx/ui/resources/front_stamina.png"):glTexture()}
+fshat_stamina_dark = {core.display.loadImage("/data/gfx/ui/resources/front_stamina_dark.png"):glTexture()}
+fshat_mana = {core.display.loadImage("/data/gfx/ui/resources/front_mana.png"):glTexture()}
+fshat_mana_dark = {core.display.loadImage("/data/gfx/ui/resources/front_mana_dark.png"):glTexture()}
+fshat_soul = {core.display.loadImage("/data/gfx/ui/resources/front_souls.png"):glTexture()}
+fshat_soul_dark = {core.display.loadImage("/data/gfx/ui/resources/front_souls_dark.png"):glTexture()}
+fshat_equi = {core.display.loadImage("/data/gfx/ui/resources/front_nature.png"):glTexture()}
+fshat_equi_dark = {core.display.loadImage("/data/gfx/ui/resources/front_nature_dark.png"):glTexture()}
+fshat_paradox = {core.display.loadImage("/data/gfx/ui/resources/front_paradox.png"):glTexture()}
+fshat_paradox_dark = {core.display.loadImage("/data/gfx/ui/resources/front_paradox_dark.png"):glTexture()}
+fshat_hate = {core.display.loadImage("/data/gfx/ui/resources/front_hate.png"):glTexture()}
+fshat_hate_dark = {core.display.loadImage("/data/gfx/ui/resources/front_hate_dark.png"):glTexture()}
+fshat_positive = {core.display.loadImage("/data/gfx/ui/resources/front_positive.png"):glTexture()}
+fshat_positive_dark = {core.display.loadImage("/data/gfx/ui/resources/front_positive_dark.png"):glTexture()}
+fshat_negative = {core.display.loadImage("/data/gfx/ui/resources/front_negative.png"):glTexture()}
+fshat_negative_dark = {core.display.loadImage("/data/gfx/ui/resources/front_negative_dark.png"):glTexture()}
+fshat_vim = {core.display.loadImage("/data/gfx/ui/resources/front_vim.png"):glTexture()}
+fshat_vim_dark = {core.display.loadImage("/data/gfx/ui/resources/front_vim_dark.png"):glTexture()}
+fshat_psi = {core.display.loadImage("/data/gfx/ui/resources/front_psi.png"):glTexture()}
+fshat_psi_dark = {core.display.loadImage("/data/gfx/ui/resources/front_psi_dark.png"):glTexture()}
+fshat_air = {core.display.loadImage("/data/gfx/ui/resources/front_air.png"):glTexture()}
+fshat_air_dark = {core.display.loadImage("/data/gfx/ui/resources/front_air_dark.png"):glTexture()}
+
+ammo_shadow_default = {core.display.loadImage("/data/gfx/ui/resources/ammo_shadow_default.png"):glTexture()}
+ammo_default = {core.display.loadImage("/data/gfx/ui/resources/ammo_default.png"):glTexture()}
+ammo_shadow_arrow = {core.display.loadImage("/data/gfx/ui/resources/ammo_shadow_arrow.png"):glTexture()}
+ammo_arrow = {core.display.loadImage("/data/gfx/ui/resources/ammo_arrow.png"):glTexture()}
+ammo_shadow_shot = {core.display.loadImage("/data/gfx/ui/resources/ammo_shadow_shot.png"):glTexture()}
+ammo_shot = {core.display.loadImage("/data/gfx/ui/resources/ammo_shot.png"):glTexture()}
+_M['ammo_shadow_alchemist-gem'] = {core.display.loadImage("/data/gfx/ui/resources/ammo_shadow_alchemist-gem.png"):glTexture()}
+_M['ammo_alchemist-gem'] = {core.display.loadImage("/data/gfx/ui/resources/ammo_alchemist-gem.png"):glTexture()}
+
+font_sha = core.display.newFont("/data/font/USENET_.ttf", 14, true)
+font_sha:setStyle("bold")
+sfont_sha = core.display.newFont("/data/font/USENET_.ttf", 12, true)
+sfont_sha:setStyle("bold")
+
+icon_green = { core.display.loadImage("/data/gfx/ui/talent_frame_ok.png"):glTexture() }
+icon_yellow = { core.display.loadImage("/data/gfx/ui/talent_frame_sustain.png"):glTexture() }
+icon_red = { core.display.loadImage("/data/gfx/ui/talent_frame_cooldown.png"):glTexture() }
+
+local portrait = {core.display.loadImage("/data/gfx/ui/party-portrait.png"):glTexture()}
+local portrait_unsel = {core.display.loadImage("/data/gfx/ui/party-portrait-unselect.png"):glTexture()}
+
+local pf_bg = {core.display.loadImage("/data/gfx/ui/playerframe/back.png"):glTexture()}
+local pf_shadow = {core.display.loadImage("/data/gfx/ui/playerframe/shadow.png"):glTexture()}
+local pf_defend = {core.display.loadImage("/data/gfx/ui/playerframe/defend.png"):glTexture()}
+local pf_attack = {core.display.loadImage("/data/gfx/ui/playerframe/attack.png"):glTexture()}
+local pf_levelup = {core.display.loadImage("/data/gfx/ui/playerframe/levelup.png"):glTexture()}
+local pf_encumber = {core.display.loadImage("/data/gfx/ui/playerframe/encumber.png"):glTexture()}
+local pf_exp = {core.display.loadImage("/data/gfx/ui/playerframe/exp.png"):glTexture()}
+local pf_exp_levelup = {core.display.loadImage("/data/gfx/ui/playerframe/exp_levelup.png"):glTexture()}
+
+local mm_bg = {core.display.loadImage("/data/gfx/ui/minimap/back.png"):glTexture()}
+local mm_comp = {core.display.loadImage("/data/gfx/ui/minimap/compass.png"):glTexture()}
+local mm_shadow = {core.display.loadImage("/data/gfx/ui/minimap/shadow.png"):glTexture()}
+local mm_transp = {core.display.loadImage("/data/gfx/ui/minimap/transp.png"):glTexture()}
+
+local sep = {core.display.loadImage("/data/gfx/ui/hotkeys/separator.png"):glTexture()}
+local sep_vines = {core.display.loadImage("/data/gfx/ui/hotkeys/separator_vines.png"):glTexture()}
+
+
 function _M:init()
 	UISet.init(self)
 
@@ -43,24 +145,37 @@ function _M:init()
 	self.tbuff = {}
 	self.pbuff = {}
 
+	self.mhandle_pos = {
+		player = {x=296, y=73},
+		resources = {x=fshat[6] - move_handle[6], y=0},
+		minimap = {x=208, y=176},
+	}
+
+	local w, h = core.display.size()
 	self.places = {
 		player = {x=0, y=0, scale=1},
 		resources = {x=0, y=111, scale=1},
+		minimap = {x=w - 239, y=0, scale=1},
 	}
 	table.merge(self.places, config.settings.tome.uiset_minimalist and config.settings.tome.uiset_minimalist.places or {}, true)
-	self:boundPlaces()
 
 	self.side_4 = 0
 	self.side_6 = 0
 	self.side_8 = 0
 end
 
-function _M:boundPlaces()
-	for w, d in pairs(self.places) do
+function _M:boundPlaces(w, h)
+	w = w or game.w
+	h = h or game.h
+
+	for what, d in pairs(self.places) do
 		if d.x then
 			d.x = math.floor(d.x)
 			d.y = math.floor(d.y)
 			d.scale = util.bound(d.scale, 0.5, 2)
+
+			d.x = util.bound(d.x, -self.mhandle_pos[what].x * d.scale, w - self.mhandle_pos[what].x * d.scale - move_handle[6] * d.scale)
+			d.y = util.bound(d.y, -self.mhandle_pos[what].y * d.scale, self.map_h_stop - self.mhandle_pos[what].y * d.scale - move_handle[7] * d.scale)
 		end
 	end
 end
@@ -135,6 +250,8 @@ function _M:activate()
 	end
 	game.logSeen = function(e, style, ...) if e and e.x and e.y and game.level.map.seens(e.x, e.y) then game.log(style, ...) end end
 	game.logPlayer = function(e, style, ...) if e == game.player or e == game.party then game.log(style, ...) end end
+
+	self:boundPlaces()
 end
 
 function _M:setupMinimap(level)
@@ -145,8 +262,8 @@ function _M:resizeIconsHotkeysToolbar()
 	local h = 52
 	if config.settings.tome.hotkey_icons then h = (4 + config.settings.tome.hotkey_icons_size) * config.settings.tome.hotkey_icons_rows end
 
-	local oldstop = self.map_h_stop or (game.h - h)
-	self.map_h_stop = game.h - h
+	local oldstop = self.map_h_stop or (game.h - h - sep[7])
+	self.map_h_stop = game.h - h - sep[7]
 
 	self.hotkeys_display_icons = HotkeysIconsDisplay.new(nil, 0, game.h - h, game.w, h, "/data/gfx/ui/hotkeys/back.png", self.init_font_mono, self.init_size_mono, config.settings.tome.hotkey_icons_size, config.settings.tome.hotkey_icons_size)
 	self.hotkeys_display_icons:enableShadow(0.6)
@@ -168,110 +285,38 @@ function _M:getMapSize()
 	return 0, 0, w, (self.map_h_stop or 80) - 16
 end
 
-local move_handle = {core.display.loadImage("/data/gfx/ui/move_handle.png"):glTexture()}
-
--- Load the various shaders used to display resources
-air_c = {0x92/255, 0xe5, 0xe8}
-air_sha = Shader.new("resources", {color=air_c, speed=100, amp=0.8, distort={2,2.5}})
-life_c = {0xc0/255, 0, 0}
-life_sha = Shader.new("resources", {color=life_c, speed=1000, distort={1.5,1.5}})
-shield_c = {0.5, 0.5, 0.5}
-shield_sha = Shader.new("resources", {color=shield_c, speed=5000, a=0.8, distort={0.5,0.5}})
-stam_c = {0xff/255, 0xcc/255, 0x80/255}
-stam_sha = Shader.new("resources", {color=stam_c, speed=700, distort={1,1.4}})
-mana_c = {106/255, 146/255, 222/255}
-mana_sha = Shader.new("resources", {color=mana_c, speed=1000, distort={0.4,0.4}})
-soul_c = {128/255, 128/255, 128/255}
-soul_sha = Shader.new("resources", {color=soul_c, speed=1200, distort={0.4,-0.4}})
-equi_c = {0x00/255, 0xff/255, 0x74/255}
-equi_sha = Shader.new("resources", {color=equi_c, amp=0.8, speed=20000, distort={0.3,0.25}})
-paradox_c = {0x2f/255, 0xa0/255, 0xb4/255}
-paradox_sha = Shader.new("resources", {color=paradox_c, amp=0.8, speed=20000, distort={0.1,0.25}})
-pos_c = {colors.GOLD.r/255, colors.GOLD.g/255, colors.GOLD.b/255}
-pos_sha = Shader.new("resources", {color=pos_c, speed=1000, distort={1.6,0.2}})
-neg_c = {colors.DARK_GREY.r/255, colors.DARK_GREY.g/255, colors.DARK_GREY.b/255}
-neg_sha = Shader.new("resources", {color=neg_c, speed=1000, distort={1.6,-0.2}})
-vim_c = {0x90/255, 0x40/255, 0x10/255}
-vim_sha = Shader.new("resources", {color=vim_c, speed=1000, distort={0.4,0.4}})
-hate_c = {colors.GREY.r/255, colors.GREY.g/255, colors.GREY.b/255}
-hate_sha = Shader.new("resources", {color=hate_c, speed=1000, distort={0.4,0.4}})
-psi_c = {colors.BLUE.r/255, colors.BLUE.g/255, colors.BLUE.b/255}
-psi_sha = Shader.new("resources", {color=psi_c, speed=2000, distort={0.4,0.4}})
-save_c = pos_c
-save_sha = pos_sha
-
-sshat = {core.display.loadImage("/data/gfx/ui/resources/shadow.png"):glTexture()}
-bshat = {core.display.loadImage("/data/gfx/ui/resources/back.png"):glTexture()}
-shat = {core.display.loadImage("/data/gfx/ui/resources/fill.png"):glTexture()}
-fshat = {core.display.loadImage("/data/gfx/ui/resources/front.png"):glTexture()}
-fshat_life = {core.display.loadImage("/data/gfx/ui/resources/front_life.png"):glTexture()}
-fshat_life_dark = {core.display.loadImage("/data/gfx/ui/resources/front_life_dark.png"):glTexture()}
-fshat_shield = {core.display.loadImage("/data/gfx/ui/resources/front_life_armored.png"):glTexture()}
-fshat_shield_dark = {core.display.loadImage("/data/gfx/ui/resources/front_life_armored_dark.png"):glTexture()}
-fshat_stamina = {core.display.loadImage("/data/gfx/ui/resources/front_stamina.png"):glTexture()}
-fshat_stamina_dark = {core.display.loadImage("/data/gfx/ui/resources/front_stamina_dark.png"):glTexture()}
-fshat_mana = {core.display.loadImage("/data/gfx/ui/resources/front_mana.png"):glTexture()}
-fshat_mana_dark = {core.display.loadImage("/data/gfx/ui/resources/front_mana_dark.png"):glTexture()}
-fshat_soul = {core.display.loadImage("/data/gfx/ui/resources/front_souls.png"):glTexture()}
-fshat_soul_dark = {core.display.loadImage("/data/gfx/ui/resources/front_souls_dark.png"):glTexture()}
-fshat_equi = {core.display.loadImage("/data/gfx/ui/resources/front_nature.png"):glTexture()}
-fshat_equi_dark = {core.display.loadImage("/data/gfx/ui/resources/front_nature_dark.png"):glTexture()}
-fshat_paradox = {core.display.loadImage("/data/gfx/ui/resources/front_paradox.png"):glTexture()}
-fshat_paradox_dark = {core.display.loadImage("/data/gfx/ui/resources/front_paradox_dark.png"):glTexture()}
-fshat_hate = {core.display.loadImage("/data/gfx/ui/resources/front_hate.png"):glTexture()}
-fshat_hate_dark = {core.display.loadImage("/data/gfx/ui/resources/front_hate_dark.png"):glTexture()}
-fshat_positive = {core.display.loadImage("/data/gfx/ui/resources/front_positive.png"):glTexture()}
-fshat_positive_dark = {core.display.loadImage("/data/gfx/ui/resources/front_positive_dark.png"):glTexture()}
-fshat_negative = {core.display.loadImage("/data/gfx/ui/resources/front_negative.png"):glTexture()}
-fshat_negative_dark = {core.display.loadImage("/data/gfx/ui/resources/front_negative_dark.png"):glTexture()}
-fshat_vim = {core.display.loadImage("/data/gfx/ui/resources/front_vim.png"):glTexture()}
-fshat_vim_dark = {core.display.loadImage("/data/gfx/ui/resources/front_vim_dark.png"):glTexture()}
-fshat_psi = {core.display.loadImage("/data/gfx/ui/resources/front_psi.png"):glTexture()}
-fshat_psi_dark = {core.display.loadImage("/data/gfx/ui/resources/front_psi_dark.png"):glTexture()}
-fshat_air = {core.display.loadImage("/data/gfx/ui/resources/front_air.png"):glTexture()}
-fshat_air_dark = {core.display.loadImage("/data/gfx/ui/resources/front_air_dark.png"):glTexture()}
-
-ammo_shadow_default = {core.display.loadImage("/data/gfx/ui/resources/ammo_shadow_default.png"):glTexture()}
-ammo_default = {core.display.loadImage("/data/gfx/ui/resources/ammo_default.png"):glTexture()}
-ammo_shadow_arrow = {core.display.loadImage("/data/gfx/ui/resources/ammo_shadow_arrow.png"):glTexture()}
-ammo_arrow = {core.display.loadImage("/data/gfx/ui/resources/ammo_arrow.png"):glTexture()}
-ammo_shadow_shot = {core.display.loadImage("/data/gfx/ui/resources/ammo_shadow_shot.png"):glTexture()}
-ammo_shot = {core.display.loadImage("/data/gfx/ui/resources/ammo_shot.png"):glTexture()}
-_M['ammo_shadow_alchemist-gem'] = {core.display.loadImage("/data/gfx/ui/resources/ammo_shadow_alchemist-gem.png"):glTexture()}
-_M['ammo_alchemist-gem'] = {core.display.loadImage("/data/gfx/ui/resources/ammo_alchemist-gem.png"):glTexture()}
-
-font_sha = core.display.newFont("/data/font/USENET_.ttf", 14, true)
-font_sha:setStyle("bold")
-sfont_sha = core.display.newFont("/data/font/USENET_.ttf", 12, true)
-sfont_sha:setStyle("bold")
+function _M:uiMoveResize(what, button, mx, my, xrel, yrel, bx, by, event)
+	game.tooltip_x, game.tooltip_y = 1, 1; game:tooltipDisplayAtMap(game.w, game.h, "Left mouse drag&drop to move the frame\nRight mouse drag&drop to scale up/down\nMiddle click to reset to default scale")
+	if event == "button" and button == "middle" then self.places[what].scale = 1 self:saveSettings()
+	elseif event == "motion" and button == "left" then
+		game.mouse:startDrag(mx, my, s, {kind="ui:move", id=what, dx=bx*self.places[what].scale, dy=by*self.places[what].scale},
+			function(drag, used) self:saveSettings() end,
+			function(drag, _, x, y) if self.places[drag.payload.id] then self.places[drag.payload.id].x = x-drag.payload.dx self.places[drag.payload.id].y = y-drag.payload.dy self:boundPlaces() end end,
+			true
+		)
+	elseif event == "motion" and button == "right" then
+		game.mouse:startDrag(mx, my, s, {kind="ui:resize", id=what, bx=bx, by=by},
+			function(drag, used) self:saveSettings() end,
+--			function(drag, _, x, y) if self.places[drag.payload.id] then self.places[drag.payload.id].scale = util.bound(math.max((x-self.places[drag.payload.id].x)/drag.payload.bx, (y-self.places[drag.payload.id].y)/drag.payload.by), 0.5, 2) self:boundPlaces() end end,
+			function(drag, _, x, y) if self.places[drag.payload.id] then self.places[drag.payload.id].scale = util.bound(math.max((x-self.places[drag.payload.id].x)/drag.payload.bx), 0.5, 2) self:boundPlaces() end end,
+			true
+		)
+	end
+end
 
 function _M:showResourceTooltip(x, y, w, h, id, desc, is_first)
-	if not game.mouse:updateZone(id, x, y, w, h) then
+	if not game.mouse:updateZone(id, x, y, w, h, nil, self.places.resources.scale) then
 		game.mouse:registerZone(x, y, w, h, function(button, mx, my, xrel, yrel, bx, by, event)
 			if is_first then
 				if event == "out" then self.mhandle.resources = nil return
 				else self.mhandle.resources = true end
 
 				-- Move handle
-				if bx >= fshat[6] - move_handle[6] and bx <= fshat[6] and by >= 0 and by <= move_handle[7] then
-					game.tooltip_x, game.tooltip_y = 1, 1; game:tooltipDisplayAtMap(game.w, game.h, "Left mouse drag&drop to move the frame\nRight mouse drag&drop to scale up/down\nMiddle click to reset to default scale")
-					if event == "button" and button == "middle" then self.places.resources.scale = 1 self:saveSettings()
-					elseif event == "motion" and button == "left" then
-						game.mouse:startDrag(mx, my, s, {kind="ui:move", id="resources", dx=bx*self.places.resources.scale, dy=by*self.places.resources.scale},
-							function(drag, used) self:saveSettings() end,
-							function(drag, _, x, y) if self.places[drag.payload.id] then self.places[drag.payload.id].x = x-drag.payload.dx self.places[drag.payload.id].y = y-drag.payload.dy self:boundPlaces() end end
-						)
-					elseif event == "motion" and button == "right" then
-						game.mouse:startDrag(mx, my, s, {kind="ui:resize", id="resources", bx=bx, by=by},
-							function(drag, used) self:saveSettings() end,
-							function(drag, _, x, y) if self.places[drag.payload.id] then self.places[drag.payload.id].scale = util.bound(math.max((x-self.places[drag.payload.id].x)/drag.payload.bx, (y-self.places[drag.payload.id].y)/drag.payload.by), 0.5, 2) self:boundPlaces() end end
-						)
-					end
-				end
+				if bx >= self.mhandle_pos.resources.x and bx <= self.mhandle_pos.resources.x + move_handle[6] and by >= self.mhandle_pos.resources.y and by <= self.mhandle_pos.resources.y + move_handle[7] then self:uiMoveResize("resources", button, mx, my, xrel, yrel, bx, by, event) end
 			end
 
 			game.tooltip_x, game.tooltip_y = 1, 1; game:tooltipDisplayAtMap(game.w, game.h, desc)
-		end, nil, id, true)
+		end, nil, id, true, self.places.resources.scale)
 	end
 end
 
@@ -308,7 +353,7 @@ function _M:displayResources(scale, bx, by)
 			local front = fshat_air_dark
 			if player.air >= player.max_air * 0.5 then front = fshat_air end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:air", self.TOOLTIP_AIR)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:air", self.TOOLTIP_AIR)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:air") then game.mouse:unregisterZone("res:air") end
@@ -356,10 +401,10 @@ function _M:displayResources(scale, bx, by)
 			dt[1]:toScreenFull(2+x+64, 2+y+10 + (shat[7]-dt[7])/2, dt[6], dt[7], dt[2], dt[3], 0, 0, 0, 0.7)
 			dt[1]:toScreenFull(x+64, y+10 + (shat[7]-dt[7])/2, dt[6], dt[7], dt[2], dt[3])
 
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:shield", self.TOOLTIP_DAMAGE_SHIELD.."\n---\n"..self.TOOLTIP_LIFE, true)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:shield", self.TOOLTIP_DAMAGE_SHIELD.."\n---\n"..self.TOOLTIP_LIFE, true)
 			if game.mouse:getZone("res:life") then game.mouse:unregisterZone("res:life") end
 		else
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:life", self.TOOLTIP_LIFE, true)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:life", self.TOOLTIP_LIFE, true)
 			if game.mouse:getZone("res:shield") then game.mouse:unregisterZone("res:shield") end
 		end
 
@@ -404,7 +449,7 @@ function _M:displayResources(scale, bx, by)
 			if player.stamina >= player.max_stamina then front = fshat_stamina end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
 
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:stamina", self.TOOLTIP_STAMINA)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:stamina", self.TOOLTIP_STAMINA)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:stamina") then game.mouse:unregisterZone("res:stamina") end
@@ -436,7 +481,7 @@ function _M:displayResources(scale, bx, by)
 			local front = fshat_mana_dark
 			if player.mana >= player.max_mana then front = fshat_mana end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:mana", self.TOOLTIP_MANA)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:mana", self.TOOLTIP_MANA)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:mana") then game.mouse:unregisterZone("res:mana") end
@@ -466,7 +511,7 @@ function _M:displayResources(scale, bx, by)
 			local front = fshat_soul_dark
 			if pt.souls >= pt.souls_max then front = fshat_soul end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:necrotic", self.TOOLTIP_NECROTIC_AURA)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:necrotic", self.TOOLTIP_NECROTIC_AURA)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:necrotic") then game.mouse:unregisterZone("res:necrotic") end
@@ -503,7 +548,7 @@ function _M:displayResources(scale, bx, by)
 			local front = fshat_equi
 			if chance <= 85 then front = fshat_equi_dark end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:equi", self.TOOLTIP_EQUILIBRIUM)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:equi", self.TOOLTIP_EQUILIBRIUM)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:equi") then game.mouse:unregisterZone("res:equi") end
@@ -535,7 +580,7 @@ function _M:displayResources(scale, bx, by)
 			local front = fshat_positive_dark
 			if player.positive >= player.max_positive * 0.7 then front = fshat_positive end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:positive", self.TOOLTIP_POSITIVE)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:positive", self.TOOLTIP_POSITIVE)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:positive") then game.mouse:unregisterZone("res:positive") end
@@ -567,7 +612,7 @@ function _M:displayResources(scale, bx, by)
 			local front = fshat_negative_dark
 			if player.negative >= player.max_negative * 0.7  then front = fshat_negative end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:negative", self.TOOLTIP_NEGATIVE)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:negative", self.TOOLTIP_NEGATIVE)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:negative") then game.mouse:unregisterZone("res:negative") end
@@ -604,7 +649,7 @@ function _M:displayResources(scale, bx, by)
 			local front = fshat_paradox
 			if chance <= 10 then front = fshat_paradox_dark end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:paradox", self.TOOLTIP_PARADOX)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:paradox", self.TOOLTIP_PARADOX)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:paradox") then game.mouse:unregisterZone("res:paradox") end
@@ -636,7 +681,7 @@ function _M:displayResources(scale, bx, by)
 			local front = fshat_vim_dark
 			if player.vim >= player.max_vim then front = fshat_vim end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:vim", self.TOOLTIP_VIM)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:vim", self.TOOLTIP_VIM)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:vim") then game.mouse:unregisterZone("res:vim") end
@@ -668,7 +713,7 @@ function _M:displayResources(scale, bx, by)
 			local front = fshat_hate_dark
 			if player.hate >= 10 then front = fshat_hate end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:hate", self.TOOLTIP_HATE)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:hate", self.TOOLTIP_HATE)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:hate") then game.mouse:unregisterZone("res:hate") end
@@ -700,7 +745,7 @@ function _M:displayResources(scale, bx, by)
 			local front = fshat_psi_dark
 			if player.psi >= player.max_psi then front = fshat_psi end
 			front[1]:toScreenFull(x, y, front[6], front[7], front[2], front[3])
-			self:showResourceTooltip(bx+x, by+y, fshat[6], fshat[7], "res:psi", self.TOOLTIP_PSI)
+			self:showResourceTooltip(bx+x*scale, by+y*scale, fshat[6], fshat[7], "res:psi", self.TOOLTIP_PSI)
 			y = y + fshat[7]
 			if y > stop then x = x + fshat[6] y = 0 end
 		elseif game.mouse:getZone("res:psi") then game.mouse:unregisterZone("res:psi") end
@@ -765,10 +810,6 @@ function _M:displayResources(scale, bx, by)
 	end
 end
 
-icon_green = { core.display.loadImage("/data/gfx/ui/talent_frame_ok.png"):glTexture() }
-icon_yellow = { core.display.loadImage("/data/gfx/ui/talent_frame_sustain.png"):glTexture() }
-icon_red = { core.display.loadImage("/data/gfx/ui/talent_frame_cooldown.png"):glTexture() }
-
 function _M:handleEffect(player, eff_id, e, p, x, y, hs, bx, by)
 	local dur = p.dur + 1
 
@@ -876,9 +917,6 @@ function _M:displayBuffs(scale, bx, by)
 	end
 end
 
-local portrait = {core.display.loadImage("/data/gfx/ui/party-portrait.png"):glTexture()}
-local portrait_unsel = {core.display.loadImage("/data/gfx/ui/party-portrait-unselect.png"):glTexture()}
-
 function _M:displayParty(scale, bx, by)
 	if game.player.changed and next(self.party) then
 		for a, d in pairs(self.party) do if not game.party:hasMember(a) then game.mouse:unregisterZone(d[2]) print("==UNREG part ", d[1].name, d[2]) end end
@@ -931,15 +969,6 @@ function _M:displayParty(scale, bx, by)
 	end
 end
 
-local pf_bg = {core.display.loadImage("/data/gfx/ui/playerframe/back.png"):glTexture()}
-local pf_shadow = {core.display.loadImage("/data/gfx/ui/playerframe/shadow.png"):glTexture()}
-local pf_defend = {core.display.loadImage("/data/gfx/ui/playerframe/defend.png"):glTexture()}
-local pf_attack = {core.display.loadImage("/data/gfx/ui/playerframe/attack.png"):glTexture()}
-local pf_levelup = {core.display.loadImage("/data/gfx/ui/playerframe/levelup.png"):glTexture()}
-local pf_encumber = {core.display.loadImage("/data/gfx/ui/playerframe/encumber.png"):glTexture()}
-local pf_exp = {core.display.loadImage("/data/gfx/ui/playerframe/exp.png"):glTexture()}
-local pf_exp_levelup = {core.display.loadImage("/data/gfx/ui/playerframe/exp_levelup.png"):glTexture()}
-
 function _M:displayPlayer(scale, bx, by)
 	local player = game.player
 	if not game.player then return end
@@ -1010,7 +1039,7 @@ function _M:displayPlayer(scale, bx, by)
 	end
 
 	if self.mhandle.player then
-		move_handle[1]:toScreenFull(296, 73, move_handle[6], move_handle[7], move_handle[2], move_handle[3])
+		move_handle[1]:toScreenFull(self.mhandle_pos.player.x, self.mhandle_pos.player.y, move_handle[6], move_handle[7], move_handle[2], move_handle[3])
 	end
 
 	if not game.mouse:updateZone("pframe", bx, by, pf_bg[6], pf_bg[7], nil, scale) then
@@ -1033,33 +1062,57 @@ function _M:displayPlayer(scale, bx, by)
 				game.tooltip_x, game.tooltip_y = 1, 1; game:tooltipDisplayAtMap(game.w, game.h, "Click to assign stats and talents!")
 				if event == "button" and button == "left" then game.key:triggerVirtual("LEVELUP") end
 			-- Move handle
-			elseif bx >= 296 and bx <= 296 + move_handle[6] and by >= 73 and by <= 73 + move_handle[7] then
-				game.tooltip_x, game.tooltip_y = 1, 1; game:tooltipDisplayAtMap(game.w, game.h, "Left mouse drag&drop to move the frame\nRight mouse drag&drop to scale up/down\nMiddle click to reset to default scale")
-				if event == "button" and button == "middle" then self.places.player.scale = 1 self:saveSettings()
-				elseif event == "motion" and button == "left" then
-					game.mouse:startDrag(mx, my, s, {kind="ui:move", id="player", dx=bx*self.places.player.scale, dy=by*self.places.player.scale},
-						function(drag, used) self:saveSettings() end,
-						function(drag, _, x, y) if self.places[drag.payload.id] then self.places[drag.payload.id].x = x-drag.payload.dx self.places[drag.payload.id].y = y-drag.payload.dy self:boundPlaces() end end
-					)
-				elseif event == "motion" and button == "right" then
-					game.mouse:startDrag(mx, my, s, {kind="ui:resize", id="player", bx=bx, by=by},
-						function(drag, used) self:saveSettings() end,
-						function(drag, _, x, y) if self.places[drag.payload.id] then self.places[drag.payload.id].scale = util.bound(math.max((x-self.places[drag.payload.id].x)/drag.payload.bx, (y-self.places[drag.payload.id].y)/drag.payload.by), 0.5, 2) self:boundPlaces() end end
-					)
-				end
+			elseif bx >= self.mhandle_pos.player.x and bx <= self.mhandle_pos.player.x + move_handle[6] and by >= self.mhandle_pos.player.y and by <= self.mhandle_pos.player.y + move_handle[7] then
+				self:uiMoveResize("player", button, mx, my, xrel, yrel, bx, by, event)
 			end
 		end
 		game.mouse:registerZone(bx, by, pf_bg[6], pf_bg[7], desc_fct, nil, "pframe", true, scale)
 	end
 end
 
-local mm_bg = {core.display.loadImage("/data/gfx/ui/minimap/back.png"):glTexture()}
-local mm_comp = {core.display.loadImage("/data/gfx/ui/minimap/compass.png"):glTexture()}
-local mm_shadow = {core.display.loadImage("/data/gfx/ui/minimap/shadow.png"):glTexture()}
-local mm_transp = {core.display.loadImage("/data/gfx/ui/minimap/transp.png"):glTexture()}
+function _M:displayMinimap(scale, bx, by)
+	local map = game.level.map
 
-local sep = {core.display.loadImage("/data/gfx/ui/hotkeys/separator.png"):glTexture()}
-local sep_vines = {core.display.loadImage("/data/gfx/ui/hotkeys/separator_vines.png"):glTexture()}
+	mm_shadow[1]:toScreenFull(0, 2, mm_shadow[6], mm_shadow[7], mm_shadow[2], mm_shadow[3])
+	mm_bg[1]:toScreenFull(0, 0, mm_bg[6], mm_bg[7], mm_bg[2], mm_bg[3])
+	if game.player.x then game.minimap_scroll_x, game.minimap_scroll_y = util.bound(game.player.x - 25, 0, map.w - 50), util.bound(game.player.y - 25, 0, map.h - 50)
+	else game.minimap_scroll_x, game.minimap_scroll_y = 0, 0 end
+
+	mm_comp[1]:toScreenFull(169, 178, mm_comp[6], mm_comp[7], mm_comp[2], mm_comp[3])
+
+	map:minimapDisplay(50, 30, game.minimap_scroll_x, game.minimap_scroll_y, 50, 50, 0.85)
+	mm_transp[1]:toScreenFull(50, 30, mm_transp[6], mm_transp[7], mm_transp[2], mm_transp[3])
+
+	if self.mhandle.minimap then
+		move_handle[1]:toScreenFull(self.mhandle_pos.minimap.x, self.mhandle_pos.minimap.y, move_handle[6], move_handle[7], move_handle[2], move_handle[3])
+	end
+
+	if not game.mouse:updateZone("minimap", bx, by, mm_bg[6], mm_bg[7], nil, scale) then
+		game.mouse:unregisterZone("minimap")
+
+		local desc_fct = function(button, mx, my, xrel, yrel, bx, by, event)
+			if event == "out" then self.mhandle.minimap = nil return
+			else self.mhandle.minimap = true end
+
+			-- Move handle
+			if bx >= self.mhandle_pos.minimap.x and bx <= self.mhandle_pos.minimap.x + move_handle[6] and by >= self.mhandle_pos.minimap.y and by <= self.mhandle_pos.minimap.y + move_handle[7] then
+				self:uiMoveResize("minimap", button, mx, my, xrel, yrel, bx, by, event)
+				return
+			end
+
+			if bx >= 50 and bx <= 50 + 150 and by >= 30 and by <= 30 + 150 then
+				if button == "left" and not xrel and not yrel and event == "button" then
+					local tmx, tmy = math.floor((bx-50) / 3), math.floor((by-30) / 3)
+					game.player:mouseMove(tmx + game.minimap_scroll_x, tmy + game.minimap_scroll_y)
+				elseif button == "right" then
+					local tmx, tmy = math.floor((bx-50) / 3), math.floor((by-30) / 3)
+					game.level.map:moveViewSurround(tmx + game.minimap_scroll_x, tmy + game.minimap_scroll_y, 1000, 1000)
+				end
+			end
+		end
+		game.mouse:registerZone(bx, by, pf_bg[6], pf_bg[7], desc_fct, nil, "minimap", true, scale)
+	end
+end
 
 function _M:display(nb_keyframes)
 	local d = core.display
@@ -1069,21 +1122,11 @@ function _M:display(nb_keyframes)
 
 	-- Minimap display
 	if game.level and game.level.map then
-		local map = game.level.map
-		local x, y = game.w - mm_bg[6], 0
-		d.glTranslate(x, y, 0)
-
-		mm_shadow[1]:toScreenFull(0, 2, mm_shadow[6], mm_shadow[7], mm_shadow[2], mm_shadow[3])
-		mm_bg[1]:toScreenFull(0, 0, mm_bg[6], mm_bg[7], mm_bg[2], mm_bg[3])
-		if game.player.x then game.minimap_scroll_x, game.minimap_scroll_y = util.bound(game.player.x - 25, 0, map.w - 50), util.bound(game.player.y - 25, 0, map.h - 50)
-		else game.minimap_scroll_x, game.minimap_scroll_y = 0, 0 end
-
-		mm_comp[1]:toScreenFull(225, 211, mm_comp[6], mm_comp[7], mm_comp[2], mm_comp[3])
-
-		map:minimapDisplay(50, 30, game.minimap_scroll_x, game.minimap_scroll_y, 50, 50, 0.85)
-		mm_transp[1]:toScreenFull(50, 30, mm_transp[6], mm_transp[7], mm_transp[2], mm_transp[3])
-
-		d.glTranslate(-x, -y, 0)
+		d.glTranslate(self.places.minimap.x, self.places.minimap.y, 0)
+		d.glScale(self.places.minimap.scale, self.places.minimap.scale, self.places.minimap.scale)
+		self:displayMinimap(self.places.minimap.scale, self.places.minimap.x, self.places.minimap.y)
+		d.glScale()
+		d.glTranslate(-self.places.minimap.x, -self.places.minimap.y, -0)
 
 		self.side_8 = mm_bg[7]
 	end
@@ -1122,8 +1165,8 @@ function _M:display(nb_keyframes)
 		self.hotkeys_display:toScreen()
 	end
 
-	sep[1]:toScreenFull(0, self.map_h_stop - sep[7], game.w, sep[7], sep[2], sep[3])
-	sep_vines[1]:toScreenFull(0, self.map_h_stop - sep_vines[7] + 6, game.w, sep_vines[7], sep_vines[2], sep_vines[3])
+	sep[1]:toScreenFull(0, self.map_h_stop, game.w, sep[7], sep[2], sep[3])
+	sep_vines[1]:toScreenFull(0, self.map_h_stop, game.w, sep_vines[7], sep_vines[2], sep_vines[3])
 end
 
 function _M:setupMouse(mouse)
@@ -1151,18 +1194,7 @@ function _M:setupMouse(mouse)
 			end
 		)
 	end, nil, "hotkeys", true)
---[[
-	-- Move using the minimap
-	mouse:registerZone(0, 0, 200, 200, function(button, mx, my, xrel, yrel, bx, by, event)
-		if button == "left" and not xrel and not yrel and event == "button" then
-			local tmx, tmy = math.floor(bx / 4), math.floor(by / 4)
-			game.player:mouseMove(tmx + self.minimap_scroll_x, tmy + self.minimap_scroll_y)
-		elseif button == "right" then
-			local tmx, tmy = math.floor(bx / 4), math.floor(by / 4)
-			self.level.map:moveViewSurround(tmx + self.minimap_scroll_x, tmy + self.minimap_scroll_y, 1000, 1000)
-		end
-	end)
-]]
+
 	-- Chat tooltips
 	profile.chat:onMouse(function(user, item, button, event, x, y, xrel, yrel, bx, by)
 		local mx, my = core.mouse.get()
diff --git a/game/modules/tome/data/gfx/ui/hotkeys/separator.png b/game/modules/tome/data/gfx/ui/hotkeys/separator.png
index fd827b225cfff3749af9afd0c7653037c2269724..7336dd47ebf6569ca3a34e9ca8924f3b28210460 100644
Binary files a/game/modules/tome/data/gfx/ui/hotkeys/separator.png and b/game/modules/tome/data/gfx/ui/hotkeys/separator.png differ