diff --git a/game/engines/default/data/gfx/metal-ui/equipdoll/itemframe-sel48.png b/game/engines/default/data/gfx/metal-ui/equipdoll/itemframe-sel48.png
new file mode 100644
index 0000000000000000000000000000000000000000..d5a5a849ee9003acaf551878201f1e902aefbeec
Binary files /dev/null and b/game/engines/default/data/gfx/metal-ui/equipdoll/itemframe-sel48.png differ
diff --git a/game/engines/default/data/gfx/metal-ui/equipdoll/itemframe48.png b/game/engines/default/data/gfx/metal-ui/equipdoll/itemframe48.png
new file mode 100644
index 0000000000000000000000000000000000000000..954804eb655ef18861655b3606a9166bf7c65f0c
Binary files /dev/null and b/game/engines/default/data/gfx/metal-ui/equipdoll/itemframe48.png differ
diff --git a/game/engines/default/engine/KeyBind.lua b/game/engines/default/engine/KeyBind.lua
index 764be6d0c24b06f74dec479cbd323aa80b91fac5..ad91fa21db71f94f29bd2ea2f8308081a5084ed6 100644
--- a/game/engines/default/engine/KeyBind.lua
+++ b/game/engines/default/engine/KeyBind.lua
@@ -214,7 +214,7 @@ function _M:receiveKey(sym, ctrl, shift, alt, meta, unicode, isup, key, ismouse)
 
 	if self.any_key then self.any_key(sym, ctrl, shift, alt, meta, unicode, isup, key) end
 
-	local ks, us
+	local ks, kks, us
 	if not ismouse then ks, kks, us = self:makeKeyString(sym, ctrl, shift, alt, meta, unicode, key)
 	else ks = self:makeMouseString(sym, ctrl, shift, alt, meta) end
 --	print(self, "[BIND]", sym, ctrl, shift, alt, meta, unicode, " :=: ", ks, kks, us, " ?=? ", self.binds[ks], kks and self.binds[kks], us and self.binds[us])
diff --git a/game/engines/default/engine/interface/ActorInventory.lua b/game/engines/default/engine/interface/ActorInventory.lua
index 0cd774e26e32f29d212ebe37a74edce1c1d22963..f53228efa6be0a91ee864485fde6c06d80c76d4e 100644
--- a/game/engines/default/engine/interface/ActorInventory.lua
+++ b/game/engines/default/engine/interface/ActorInventory.lua
@@ -31,7 +31,7 @@ _M.inven_def = {}
 
 --- Defines stats
 -- Static!
-function _M:defineInventory(short_name, name, is_worn, desc, show_equip)
+function _M:defineInventory(short_name, name, is_worn, desc, show_equip, infos)
 	assert(name, "no inventory slot name")
 	assert(short_name, "no inventory slot short_name")
 	assert(desc, "no inventory slot desc")
@@ -41,6 +41,7 @@ function _M:defineInventory(short_name, name, is_worn, desc, show_equip)
 		description = desc,
 		is_worn = is_worn,
 		is_shown_equip = show_equip,
+		infos = infos,
 	})
 	self.inven_def[#self.inven_def].id = #self.inven_def
 	self.inven_def[short_name] = self.inven_def[#self.inven_def]
@@ -60,7 +61,7 @@ end
 function _M:initBody()
 	if self.body then
 		for inven, max in pairs(self.body) do
-			self.inven[self["INVEN_"..inven]] = {max=max, worn=self.inven_def[self["INVEN_"..inven]].is_worn, id=self["INVEN_"..inven]}
+			self.inven[self["INVEN_"..inven]] = {max=max, worn=self.inven_def[self["INVEN_"..inven]].is_worn, id=self["INVEN_"..inven], name=inven}
 		end
 		self.body = nil
 	end
diff --git a/game/engines/default/engine/ui/Dialog.lua b/game/engines/default/engine/ui/Dialog.lua
index fbf3f6adafbb9e50474e1ffab7fa7a27b5ca66b8..80ea2226b8f987221e69ba03affc668f312c5a8e 100644
--- a/game/engines/default/engine/ui/Dialog.lua
+++ b/game/engines/default/engine/ui/Dialog.lua
@@ -446,11 +446,11 @@ function _M:setFocus(id)
 		for i = 1, #self.uis do
 			if self.uis[i].ui == id then id = i break end
 		end
-		if type(id) == "table" then return end
+		if type(id) == "table" then self:no_focus() return end
 	end
 
 	local ui = self.uis[id]
-	if not ui.ui.can_focus then return end
+	if not ui.ui.can_focus then self:no_focus() return end
 	self.focus_ui = ui
 	self.focus_ui_id = id
 	ui.ui:setFocus(true)
@@ -504,6 +504,8 @@ end
 
 function _M:on_focus(id, ui)
 end
+function _M:no_focus()
+end
 
 function _M:mouseEvent(button, x, y, xrel, yrel, bx, by, event)
 	-- Look for focus
@@ -514,9 +516,10 @@ function _M:mouseEvent(button, x, y, xrel, yrel, bx, by, event)
 
 			-- Pass the event
 			ui.ui.mouse:delegate(button, bx, by, xrel, yrel, bx, by, event)
-			break
+			return
 		end
 	end
+	self:no_focus()
 end
 
 function _M:keyEvent(...)
@@ -570,6 +573,8 @@ function _M:drawFrame(x, y, r, g, b, a)
 	end
 end
 
+function _M:innerDisplayBack(x, y, nb_keyframes)
+end
 function _M:innerDisplay(x, y, nb_keyframes)
 end
 
@@ -617,6 +622,8 @@ function _M:toScreen(x, y, nb_keyframes)
 		self.title_tex[1]:toScreenFull(x + (self.w - self.title_tex.w) / 2 + self.frame.title_x, y + self.frame.title_y, self.title_tex.w, self.title_tex.h, self.title_tex[2], self.title_tex[3])
 	end
 
+	self:innerDisplayBack(x, y, nb_keyframes, tx, ty)
+
 	-- UI elements
 	for i = 1, #self.uis do
 		local ui = self.uis[i]
diff --git a/game/engines/default/engine/ui/EquipDollFrame.lua b/game/engines/default/engine/ui/EquipDollFrame.lua
new file mode 100644
index 0000000000000000000000000000000000000000..f27e97b9e0258dc88aab2800ade040a82ac30e5b
--- /dev/null
+++ b/game/engines/default/engine/ui/EquipDollFrame.lua
@@ -0,0 +1,108 @@
+-- TE4 - T-Engine 4
+-- Copyright (C) 2009, 2010, 2011 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+require "engine.class"
+local Base = require "engine.ui.Base"
+local Focusable = require "engine.ui.Focusable"
+
+module(..., package.seeall, class.inherit(Base, Focusable))
+
+function _M:init(t)
+	self.actor = assert(t.actor, "no equipdollframe actor")
+	self.inven = assert(t.inven, "no equipdollframe inven")
+	self.item = assert(t.item, "no equipdollframe item")
+	self.w = assert(t.w, "no equipdollframe w")
+	self.h = assert(t.h, "no equipdollframe h")
+	self.f_iw = assert(t.iw, "no equipdollframe iw")
+	self.f_ih = assert(t.ih, "no equipdollframe ih")
+	self.f_ix = assert(t.ix, "no equipdollframe ix")
+	self.f_iy = assert(t.iy, "no equipdollframe iy")
+	self.bg = assert(t.bg, "no equipdollframe bg")
+	self.bg_sel = assert(t.bg_sel, "no equipdollframe bg_sel")
+	self.bg_empty = t.bg_empty
+	self.drag_enable = t.drag_enable
+	self.fct = t.fct
+
+	Base.init(self, t)
+end
+
+function _M:generate()
+	self.mouse:reset()
+	self.key:reset()
+
+	self.bg = self:getUITexture(self.bg)
+	self.bg_sel = self:getUITexture(self.bg_sel)
+	if self.bg_empty then self.bg_empty = self:getUITexture(self.bg_empty) end
+
+	self.mouse:registerZone(0, 0, self.w, self.h, function(button, x, y, xrel, yrel, bx, by, event)
+		if button == "left" and event == "button" then self:onUse(button, event) end
+		if event == "motion" and button == "left" and self.inven[self.item] then self:onDrag(self.inven, self.item, self.inven[self.item])
+		elseif button == "drag-end" and self.drag_enable then
+			local drag = game.mouse.dragged.payload
+			print(table.serialize(drag,nil,true))
+			if drag.kind == "inventory" and drag.inven and self.actor:getInven(drag.inven) and not self.actor:getInven(drag.inven).worn then
+				self:actorWear(drag.inven, drag.item_idx, drag.object)
+				game.mouse:usedDrag()
+			end
+		end
+	end)
+	self.key:addBinds{
+		ACCEPT = function() self:onUse("left", "key") end,
+	}
+end
+
+function _M:onUse(...)
+	if not self.fct then return end
+	self:sound("button")
+	self.fct(...)
+end
+
+-- Overload to do as you need
+function _M:actorWear(inven, item, o)
+end
+
+function _M:onDrag(inven, item, o)
+	if not self.drag_enable then return end
+	if o then
+		local s = o:getEntityFinalSurface(nil, 64, 64)
+		local x, y = core.mouse.get()
+		game.mouse:startDrag(x, y, s, {kind="inventory", item_idx=item, inven=inven, object=o, id=o:getName{no_add_name=true, force_id=true, no_count=true}}, function(drag, used)
+			local x, y = core.mouse.get()
+			game.mouse:receiveMouse("drag-end", x, y, true, nil, {drag=drag})
+		end)
+	end
+end
+
+function _M:display(x, y, nb_keyframes, ox, oy)
+	if self.focused then
+		self.bg_sel.t:toScreenFull(x, y, self.w, self.h, self.bg_sel.tw, self.bg_sel.th)
+	else
+		self.bg.t:toScreenFull(x, y, self.w, self.h, self.bg.tw, self.bg.th)
+	end
+
+	local o = self.inven[self.item]
+	if o and o.toScreen then
+		o:toScreen(nil, x + self.f_ix, y + self.f_iy, self.f_iw, self.f_ih)
+	elseif self.bg_empty then
+		self.bg_empty.t:toScreenFull(x + self.f_ix, y + self.f_iy, self.f_iw, self.f_ih, self.bg_empty.tw, self.bg_empty.th)
+	end
+
+	self.last_display_x = ox
+	self.last_display_y = oy
+end
diff --git a/game/engines/default/engine/ui/ListColumns.lua b/game/engines/default/engine/ui/ListColumns.lua
index a922192a0f632a8ef8e6749b19e1ada33fc07ec2..b5a1000b3d97ebe3a1520235c486afbdaf5afa29 100644
--- a/game/engines/default/engine/ui/ListColumns.lua
+++ b/game/engines/default/engine/ui/ListColumns.lua
@@ -37,6 +37,7 @@ function _M:init(t)
 	self.fct = t.fct
 	self.select = t.select
 	self.on_drag = t.on_drag
+	self.on_drag_end = t.on_drag_end
 	self.all_clicks = t.all_clicks
 	self.hide_columns = t.hide_columns
 
@@ -186,6 +187,7 @@ function _M:generate()
 		self:onSelect()
 		if (self.all_clicks or button == "left") and event == "button" then self:onUse(button, event) end
 		if event == "motion" and button == "left" and self.on_drag then self.on_drag(self.list[self.sel], self.sel) end
+		if button == "drag-end" and self.on_drag_end then self.on_drag_end(self.list[self.sel], self.sel) end
 	end)
 	self.key:addBinds{
 		ACCEPT = function() self:onUse("left", "key") end,
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 7a9a4cfe76fcbc3e87000ebbe657080fe254673b..b13317b9b7badc76c2e6091cb40fead18b8c6231 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -1177,17 +1177,6 @@ function _M:onTakeHit(value, src)
 		value = t.do_onTakeHit(self, t, self:isTalentActive(self.T_DEFLECTION), value)
 	end
 
-	-- Mount takes some damage ?
-	local mount = self:hasMount()
-	if mount and mount.mount.share_damage then
-		mount.mount.actor:takeHit(value * mount.mount.share_damage / 100, src)
-		value = value * (100 - mount.mount.share_damage) / 100
-		-- Remove the dead mount
-		if mount.mount.actor.dead and mount.mount.effect then
-			self:removeEffect(mount.mount.effect)
-		end
-	end
-
 	-- Achievements
 	if not self.no_take_hit_achievements and src and src.resolveSource and src:resolveSource().player and value >= 600 then
 		local rsrc = src:resolveSource()
diff --git a/game/modules/tome/class/GameState.lua b/game/modules/tome/class/GameState.lua
index 6506c2a4efaa1386fbd9e086914006a04defbfb5..eac86b7b8f6786e196882df15efd0c950759074f 100644
--- a/game/modules/tome/class/GameState.lua
+++ b/game/modules/tome/class/GameState.lua
@@ -1341,7 +1341,7 @@ function _M:createRandomBoss(base, data)
 	-- All bosses have alll body parts .. yes snake bosses can use archery and so on ..
 	-- This is to prevent them from having unusable talents
 	b.inven = {}
-	b.body = { INVEN = 1000, QS_MAINHAND = 1, QS_OFFHAND = 1, MAINHAND = 1, OFFHAND = 1, FINGER = 2, NECK = 1, LITE = 1, BODY = 1, HEAD = 1, CLOAK = 1, HANDS = 1, BELT = 1, FEET = 1, TOOL = 1, QUIVER = 1, MOUNT = 1 }
+	b.body = { INVEN = 1000, QS_MAINHAND = 1, QS_OFFHAND = 1, MAINHAND = 1, OFFHAND = 1, FINGER = 2, NECK = 1, LITE = 1, BODY = 1, HEAD = 1, CLOAK = 1, HANDS = 1, BELT = 1, FEET = 1, TOOL = 1, QUIVER = 1 }
 	b:initBody()
 
 	b:resolve()
diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua
index 233469fea306b2c92ba713966206d2cd379a5eeb..b4c23acbb6536c03a0eb6f8887f46f4744de3699 100644
--- a/game/modules/tome/class/interface/Combat.lua
+++ b/game/modules/tome/class/interface/Combat.lua
@@ -150,12 +150,6 @@ function _M:attackTarget(target, damtype, mult, noenergy)
 		if not self.combat.no_stealth_break then break_stealth = true end
 	end
 
-	-- Mount attack ?
-	local mount = self:hasMount()
-	if mount and mount.mount.attack_with_rider and core.fov.distance(self.x, self.y, target.x, target.y) <= 1 then
-		mount.mount.actor:attackTarget(target, nil, nil, nil)
-	end
-
 	-- We use up our own energy
 	if speed and not noenergy then
 		self:useEnergy(game.energy_to_act * speed)
@@ -214,7 +208,7 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
 
 	-- Does the blow connect? yes .. complex :/
 	local atk, def = self:combatAttack(weapon), target:combatDefense()
-	
+
 	-- add stalker damage and attack bonus
 	local effStalker = self:hasEffect(self.EFF_STALKER)
 	if effStalker and effStalker.target == target then
@@ -222,7 +216,7 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
 		atk = atk + t.getAttackChange(self, t, effStalker.bonus)
 		mult = mult * t.getStalkedDamageMultiplier(self, t, effStalker.bonus)
 	end
-	
+
 	if not self:canSee(target) then atk = atk / 3 end
 	local dam, apr, armor = force_dam or self:combatDamage(weapon), self:combatAPR(weapon), target:combatArmor()
 	print("[ATTACK] to ", target.name, " :: ", dam, apr, armor, def, "::", mult)
@@ -238,7 +232,7 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
 		local t = target:getTalentFromId(target.T_REPEL)
 		repelled = t.isRepelled(target, t)
 	end
-	
+
 	-- If hit is over 0 it connects, if it is 0 we still have 50% chance
 	local hitted = false
 	local crit = false
@@ -276,7 +270,7 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
 		local srcname = game.level.map.seens(self.x, self.y) and self.name:capitalize() or "Something"
 		game.logSeen(target, "%s misses %s.", srcname, target.name)
 	end
-	
+
 	-- handle stalk targeting for hits (also handled in Actor for turn end effects)
 	if hitted and target ~= self then
 		if effStalker then
@@ -284,7 +278,7 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
 			effStalker.hit = effStalker.hit or effStalker.target == target
 		elseif self:isTalentActive(self.T_STALK) then
 			local stalk = self:isTalentActive(self.T_STALK)
-		
+
 			if not stalk.hit then
 				-- mark a new target
 				stalk.hit = true
@@ -348,7 +342,7 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
 		local dam = t.getDamage(self, t)
 		DamageType:get(DamageType.DRAINLIFE).projector(self, target.x, target.y, DamageType.DRAINLIFE, dam)
 	end
-	
+
 	-- Autospell cast
 	if hitted and not target.dead and self:knowTalent(self.T_ARCANE_COMBAT) and self:isTalentActive(self.T_ARCANE_COMBAT) then
 		local t = self:getTalentFromId(self.T_ARCANE_COMBAT)
@@ -750,7 +744,7 @@ function _M:getOffHandMult(mult)
 	elseif self:knowTalent(Talents.T_CORRUPTED_STRENGTH) then
 		offmult = (mult or 1) / (2 - (math.min(self:getTalentLevel(Talents.T_CORRUPTED_STRENGTH), 8) / 9))
 	end
-	
+
 	return offmult
 end
 
@@ -1078,20 +1072,9 @@ function _M:hasMassiveArmor()
 	return armor
 end
 
---- Check if the actor has a mount
-function _M:hasMount()
-	if not self:getInven("MOUNT") then return end
-	local mount = self:getInven("MOUNT")[1]
-	if not mount or mount.type ~= "mount" then
-		return nil
-	end
-	return mount
-end
-
 -- Unarmed Combat; this handles grapple checks and building combo points
 -- Builds Comob; reduces the cooldown on all unarmed abilities on cooldown by one
 function _M:buildCombo()
-
 	local duration = 3
 	local power = 1
 	-- Combo String bonuses
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/ammo_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/ammo_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..6ff9ec401357e965a3b8a6e044299a4417e32ce4
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/ammo_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/amulet_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/amulet_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..67bb5c82556ed019e384b73e1a077731f2e8120b
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/amulet_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/belt_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/belt_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..7b5864f57067fc878d8d48af96b693e96369ab6e
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/belt_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/body_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/body_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..b192aeba995410536add30d4436df6e3f9ee7cab
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/body_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/boots_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/boots_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..4cdf184834ecabb79be8b5c2879e038c19477e86
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/boots_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/cloak_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/cloak_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..11e3ea1e331b383cc964f81d48f55905f9f36d65
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/cloak_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/gem_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/gem_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..fbd548d3acff6c94504cb00e3f1e9639bef89f66
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/gem_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/hands_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/hands_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..96cb98e610f469428bee5f128da0a949de3fafcb
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/hands_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/head_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/head_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..5286fd69010dd38cc566cbadc3cc973a8f287da7
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/head_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/light_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/light_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..11942c6c3295fa49397f503bcde8fe3b3ba56f92
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/light_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/mainhand_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/mainhand_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..f8c75a34a171177123094d56239b30e605fafcae
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/mainhand_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/offhand_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/offhand_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..66cd06fa8718e62c0b6deaccbea3d9b72e69eaf4
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/offhand_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/psionic_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/psionic_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..a7a00d1f047789797e9f3f28948eb3ef29c80d5a
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/psionic_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/ring_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/ring_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..5605da377c9ebf0d07d964aa494298c0bc17ebbb
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/ring_inv.png differ
diff --git a/game/modules/tome/data/gfx/metal-ui/equipdoll/tool_inv.png b/game/modules/tome/data/gfx/metal-ui/equipdoll/tool_inv.png
new file mode 100644
index 0000000000000000000000000000000000000000..b79291ef0c5321ae284e41ac0a9026f6f200ecb1
Binary files /dev/null and b/game/modules/tome/data/gfx/metal-ui/equipdoll/tool_inv.png differ
diff --git a/game/modules/tome/dialogs/Birther.lua b/game/modules/tome/dialogs/Birther.lua
index 9749e651f70b5a58425b8aa6ddf4a7e724e87435..a009352a221033844b8e195e86238c503d52bd58 100644
--- a/game/modules/tome/dialogs/Birther.lua
+++ b/game/modules/tome/dialogs/Birther.lua
@@ -864,7 +864,7 @@ function _M:fakeEquip(v)
 		self.actor.inven = {}
 	else
 		self.actor.inven = {}
-		local fake_body = { INVEN = 1000, QS_MAINHAND = 1, QS_OFFHAND = 1, MAINHAND = 1, OFFHAND = 1, FINGER = 2, NECK = 1, LITE = 1, BODY = 1, HEAD = 1, CLOAK = 1, HANDS = 1, BELT = 1, FEET = 1, TOOL = 1, QUIVER = 1, MOUNT = 1 }
+		local fake_body = { INVEN = 1000, QS_MAINHAND = 1, QS_OFFHAND = 1, MAINHAND = 1, OFFHAND = 1, FINGER = 2, NECK = 1, LITE = 1, BODY = 1, HEAD = 1, CLOAK = 1, HANDS = 1, BELT = 1, FEET = 1, TOOL = 1, QUIVER = 1 }
 		self.actor.body = fake_body
 		self.actor:initBody()
 
diff --git a/game/modules/tome/dialogs/ShowEquipInven.lua b/game/modules/tome/dialogs/ShowEquipInven.lua
index 4ae362577ab511eee20da056f473ea36aea958a5..5e2d2f3ea40e8118f7ca543a189b3b75ce3ea3c3 100644
--- a/game/modules/tome/dialogs/ShowEquipInven.lua
+++ b/game/modules/tome/dialogs/ShowEquipInven.lua
@@ -18,12 +18,110 @@
 -- darkgod@te4.org
 
 require "engine.class"
-local Base = require "engine.dialogs.ShowEquipInven"
+local Dialog = require "engine.ui.Dialog"
+local ListColumns = require "engine.ui.ListColumns"
+local Textzone = require "engine.ui.Textzone"
+local TextzoneList = require "engine.ui.TextzoneList"
+local Separator = require "engine.ui.Separator"
+local EquipDollFrame = require "engine.ui.EquipDollFrame"
 
-module(..., package.seeall, class.inherit(Base))
+module(..., package.seeall, class.inherit(Dialog))
 
-function _M:init(...)
-	Base.init(self, ...)
+function _M:init(title, actor, filter, action, on_select)
+	self.action = action
+	self.filter = filter
+	self.actor = actor
+	self.on_select = on_select
+
+	Dialog.init(self, title or "Inventory", math.max(800, game.w * 0.8), math.max(600, game.h * 0.8))
+
+	self.max_h = 0
+
+	self.c_inven = ListColumns.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.max_h*self.font_h - 10, sortable=true, scrollbar=true, columns={
+		{name="", width={20,"fixed"}, display_prop="char", sort="id"},
+		{name="", width={24,"fixed"}, display_prop="object", sort="sortname", direct_draw=function(item, x, y) if item.object then item.object:toScreen(nil, x+4, y, 16, 16) end end},
+		{name="Inventory", width=72, display_prop="name", sort="sortname"},
+		{name="Category", width=20, display_prop="cat", sort="cat"},
+		{name="Enc.", width=8, display_prop="encumberance", sort="encumberance"},
+	}, list={}, fct=function(item, sel, button, event) self:use(item, button, event) end, select=function(item, sel) self:select(item) end, on_drag=function(item) self:onDrag(item) end, on_drag_end=function() self:onDragTakeoff() end}
+
+	local uis = self:generateEquipDollFrames()
+	self:generateList()
+
+	uis[#uis+1] = {right=0, top=0, ui=self.c_inven}
+	uis[#uis+1] = {hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}}
+
+	self:loadUI(uis)
+	self:setFocus(self.c_inven)
+	self:setupUI()
+
+	self.key:addCommands{
+		__TEXTINPUT = function(c)
+			local list
+			if self.focus_ui and self.focus_ui.ui == self.c_inven then list = self.c_inven.list
+			elseif self.focus_ui and self.focus_ui.ui == self.c_equip then list = self.c_equip.list
+			end
+			if list and list.chars[c] then
+				self:use(list[list.chars[c]])
+			end
+		end,
+	}
+	self.key:addBinds{
+		HOTKEY_1 = function() self:defineHotkey(1) end,
+		HOTKEY_2 = function() self:defineHotkey(2) end,
+		HOTKEY_3 = function() self:defineHotkey(3) end,
+		HOTKEY_4 = function() self:defineHotkey(4) end,
+		HOTKEY_5 = function() self:defineHotkey(5) end,
+		HOTKEY_6 = function() self:defineHotkey(6) end,
+		HOTKEY_7 = function() self:defineHotkey(7) end,
+		HOTKEY_8 = function() self:defineHotkey(8) end,
+		HOTKEY_9 = function() self:defineHotkey(9) end,
+		HOTKEY_10 = function() self:defineHotkey(10) end,
+		HOTKEY_11 = function() self:defineHotkey(11) end,
+		HOTKEY_12 = function() self:defineHotkey(12) end,
+		HOTKEY_SECOND_1 = function() self:defineHotkey(13) end,
+		HOTKEY_SECOND_2 = function() self:defineHotkey(14) end,
+		HOTKEY_SECOND_3 = function() self:defineHotkey(15) end,
+		HOTKEY_SECOND_4 = function() self:defineHotkey(16) end,
+		HOTKEY_SECOND_5 = function() self:defineHotkey(17) end,
+		HOTKEY_SECOND_6 = function() self:defineHotkey(18) end,
+		HOTKEY_SECOND_7 = function() self:defineHotkey(19) end,
+		HOTKEY_SECOND_8 = function() self:defineHotkey(20) end,
+		HOTKEY_SECOND_9 = function() self:defineHotkey(21) end,
+		HOTKEY_SECOND_10 = function() self:defineHotkey(22) end,
+		HOTKEY_SECOND_11 = function() self:defineHotkey(23) end,
+		HOTKEY_SECOND_12 = function() self:defineHotkey(24) end,
+		HOTKEY_THIRD_1 = function() self:defineHotkey(25) end,
+		HOTKEY_THIRD_2 = function() self:defineHotkey(26) end,
+		HOTKEY_THIRD_3 = function() self:defineHotkey(27) end,
+		HOTKEY_THIRD_4 = function() self:defineHotkey(28) end,
+		HOTKEY_THIRD_5 = function() self:defineHotkey(29) end,
+		HOTKEY_THIRD_6 = function() self:defineHotkey(30) end,
+		HOTKEY_THIRD_7 = function() self:defineHotkey(31) end,
+		HOTKEY_THIRD_8 = function() self:defineHotkey(32) end,
+		HOTKEY_THIRD_9 = function() self:defineHotkey(33) end,
+		HOTKEY_THIRD_10 = function() self:defineHotkey(34) end,
+		HOTKEY_THIRD_11 = function() self:defineHotkey(35) end,
+		HOTKEY_THIRD_12 = function() self:defineHotkey(36) end,
+		HOTKEY_FOURTH_1 = function() self:defineHotkey(37) end,
+		HOTKEY_FOURTH_2 = function() self:defineHotkey(38) end,
+		HOTKEY_FOURTH_3 = function() self:defineHotkey(39) end,
+		HOTKEY_FOURTH_4 = function() self:defineHotkey(40) end,
+		HOTKEY_FOURTH_5 = function() self:defineHotkey(41) end,
+		HOTKEY_FOURTH_6 = function() self:defineHotkey(42) end,
+		HOTKEY_FOURTH_7 = function() self:defineHotkey(43) end,
+		HOTKEY_FOURTH_8 = function() self:defineHotkey(44) end,
+		HOTKEY_FOURTH_9 = function() self:defineHotkey(45) end,
+		HOTKEY_FOURTH_10 = function() self:defineHotkey(46) end,
+		HOTKEY_FOURTH_11 = function() self:defineHotkey(47) end,
+		HOTKEY_FOURTH_12 = function() self:defineHotkey(48) end,
+		ACCEPT = function()
+			if self.focus_ui and self.focus_ui.ui == self.c_inven then self:use(self.c_inven.list[self.c_inven.sel])
+			elseif self.focus_ui and self.focus_ui.ui == self.c_equip then self:use(self.c_equip.list[self.c_equip.sel])
+			end
+		end,
+		EXIT = function() game:unregisterDialog(self) end,
+	}
 
 	-- Add tooltips
 	self.on_select = function(item)
@@ -31,44 +129,125 @@ function _M:init(...)
 			game:tooltipDisplayAtMap(item.last_display_x, item.last_display_y, item.object:getDesc({do_color=true}, self.actor:getInven(item.object:wornInven())))
 		end
 	end
-	self.on_drag = function(item) self:onDrag(item) end
 	self.key.any_key = function(sym)
 		-- Control resets the tooltip
 		if sym == self.key._LCTRL or sym == self.key._RCTRL then local i = self.cur_item self.cur_item = nil self:select(i) end
 	end
 end
 
-function _M:unload()
-	for i, item in ipairs(self.equip_list or {}) do if item.object then item.object.__new_pickup = nil end end
-	for i, item in ipairs(self.inven_list or {}) do if item.object then item.object.__new_pickup = nil end end
+function _M:on_register()
+	game:onTickEnd(function() self.key:unicodeInput(true) end)
 end
 
-function _M:onDrag(item)
-	if item and item.object then
-		local s = item.object:getEntityFinalSurface(nil, 64, 64)
-		local x, y = core.mouse.get()
-		game.mouse:startDrag(x, y, s, {kind="inventory", id=item.object:getName{no_add_name=true, force_id=true, no_count=true}}, function(drag, used)
-			local x, y = core.mouse.get()
-			game.mouse:receiveMouse("drag-end", x, y, true, nil, {drag=drag})
-		end)
+function _M:defineHotkey(id)
+	if not self.actor or not self.actor.hotkey then return end
+
+	local item = nil
+	if self.focus_ui and self.focus_ui.ui == self.c_inven then item = self.c_inven.list[self.c_inven.sel]
+	elseif self.focus_ui and self.focus_ui.ui == self.c_equip then item = self.c_equip.list[self.c_equip.sel]
+	end
+	if not item or not item.object then return end
+
+	self.actor.hotkey[id] = {"inventory", item.object:getName{no_add_name=true, no_count=true}}
+	self:simplePopup("Hotkey "..id.." assigned", item.object:getName{no_add_name=true, no_count=true}:capitalize().." assigned to hotkey "..id)
+	self.actor.changed = true
+end
+
+function _M:select(item)
+	if self.cur_item == item then return end
+	if item then
+		if self.on_select then self.on_select(item) end
+	end
+	self.cur_item = item
+end
+
+function _M:on_focus(id, ui)
+	if self.focus_ui and self.focus_ui.ui == self.c_inven then self:select(self.c_inven.list[self.c_inven.sel])
+	elseif self.focus_ui and self.focus_ui.ui and self.focus_ui.ui.doll_select and self.focus_ui.ui.inven[self.focus_ui.ui.item] then
+		self:select{last_display_x=self.focus_ui.ui.last_display_x+self.focus_ui.ui.w, last_display_y=self.focus_ui.ui.last_display_y, object=self.focus_ui.ui.inven[self.focus_ui.ui.item]}
+	else
+		game.tooltip_x = nil
+	end
+end
+function _M:no_focus()
+	game.tooltip_x = nil
+end
+
+function _M:use(item, button, event)
+	if item then
+		if self.action(item.object, item.inven, item.item, button, event) then
+			game:unregisterDialog(self)
+		end
+	end
+end
+
+function _M:generateEquipDollFrames()
+	local doll = self.actor.equipdolls[self.actor.equipdoll or "default"]
+	if not doll then return end
+
+	local uis = {}
+
+	for k, v in pairs(doll.list) do
+		local inven = self.actor:getInven(k)
+		if inven then
+			for item, def in ipairs(v) do
+				local frame = EquipDollFrame.new{actor=self.actor, inven=inven, item=item, w=doll.w, h=doll.h, iw=doll.iw, ih=doll.ih, ix=doll.ix, iy=doll.iy, bg=doll.itemframe, bg_sel=doll.itemframe_sel, bg_empty=self.actor.inven_def[inven.name].infos and self.actor.inven_def[inven.name].infos.equipdoll_back, drag_enable=true}
+				frame.doll_select = true
+				frame.actorWear = function(_, ...)
+					if inven[item] then self.actor:doTakeoff(inven, item, inven[item]) end
+					self.actor:doWear(...)
+					self:generateList()
+				end
+				frame.fct=function(button, event) if inven[item] then self:use({inven=inven, item=item, object=inven[item]}, button, event) end end
+				uis[#uis+1] = {left=def.x, top=def.y, ui=frame}
+			end
+		end
 	end
+	return uis
+end
+
+function _M:innerDisplayBack(x, y, nb_keyframes)
+	local doll = self.actor.equipdolls[self.actor.equipdoll or "default"]
+	if not doll then return end
+
+	self.actor:toScreen(nil, x + doll.doll_x, y + doll.doll_y, 128, 128)
 end
 
-function _M:generateList()
-	Base.generateList(self, true)
-	for i, item in ipairs(self.inven_list or {}) do if item.object and item.object.__new_pickup then
-			item.name = ("#{bold}#"..item.name.."#{normal}#"):toTString()
-	end end
-	for i, item in ipairs(self.equip_list or {}) do if item.object and item.object.__new_pickup then
-			item.name = ("#{bold}#"..item.name.."#{normal}#"):toTString()
-	end end
+function _M:generateList(no_update)
+	-- Makes up the list
+	self.inven_list = {}
+	local list = self.inven_list
+	local chars = {}
+	local i = 1
+	for item, o in ipairs(self.actor:getInven("INVEN") or {}) do
+		if not self.filter or self.filter(o) then
+			local char = self:makeKeyChar(i)
+
+			local enc = 0
+			o:forAllStack(function(o) enc=enc+o.encumber end)
+
+			list[#list+1] = { id=#list+1, char=char, name=o:getName(), sortname=o:getName():toString():removeColorCodes(), color=o:getDisplayColor(), object=o, inven=self.actor.INVEN_INVEN, item=item, cat=o.subtype, encumberance=enc, desc=o:getDesc() }
+			chars[char] = #list
+			i = i + 1
+		end
+	end
+	list.chars = chars
+
+	if not no_update then
+		self.c_inven:setList(self.inven_list)
+	end
+end
 
-	self.c_inven:setList(self.inven_list)
-	self.c_equip:setList(self.equip_list)
+function _M:on_recover_focus()
+	self:generateList()
+end
+
+function _M:unload()
+	for inven_id = 1, #self.actor.inven_def do if self.actor.inven[inven_id] then for item, o in ipairs(self.actor.inven[inven_id]) do o.__new_pickup = nil end end end
 end
 
 function _M:updateTitle(title)
-	Base.updateTitle(self, title)
+	Dialog.updateTitle(self, title)
 
 	local green = colors.LIGHT_GREEN
 	local red = colors.LIGHT_RED
@@ -83,8 +262,31 @@ function _M:updateTitle(title)
 	}
 end
 
+function _M:onDrag(item)
+	if item and item.object then
+		local s = item.object:getEntityFinalSurface(nil, 64, 64)
+		local x, y = core.mouse.get()
+		game.mouse:startDrag(x, y, s, {kind="inventory", item_idx=item.item, inven=item.inven, object=item.object, id=item.object:getName{no_add_name=true, force_id=true, no_count=true}}, function(drag, used)
+			if not used then
+				local x, y = core.mouse.get()
+				game.mouse:receiveMouse("drag-end", x, y, true, nil, {drag=drag})
+			end
+		end)
+	end
+end
+
+function _M:onDragTakeoff()
+	local drag = game.mouse.dragged.payload
+	print(table.serialize(drag,nil,true))
+	if drag.kind == "inventory" and drag.inven and self.actor:getInven(drag.inven) and self.actor:getInven(drag.inven).worn then
+		self.actor:doTakeoff(drag.inven, drag.item_idx, drag.object)
+		self:generateList()
+		game.mouse:usedDrag()
+	end
+end
+
 function _M:drawFrame(x, y, r, g, b, a)
-	Base.drawFrame(self, x, y, r, g, b, a)
+	Dialog.drawFrame(self, x, y, r, g, b, a)
 	if r == 0 then return end -- Drawing the shadow
 	if self.ui ~= "metal" then return end
 	if not self.title_fill then return end
diff --git a/game/modules/tome/load.lua b/game/modules/tome/load.lua
index b325ccfbf97bd3c532796cae6685bb295dac0df6..85e403daabc460205ed3548365ab56a16829f63a 100644
--- a/game/modules/tome/load.lua
+++ b/game/modules/tome/load.lua
@@ -119,26 +119,42 @@ KeyBind:load("move,hotkeys,inventory,actions,interface,tome,debug")
 dofile("/mod/resolvers.lua")
 
 -- Body parts
-ActorInventory:defineInventory("MAINHAND", "In main hand", true, "Most weapons are wielded in the main hand.")
-ActorInventory:defineInventory("OFFHAND", "In off hand", true, "You can use shields or a second weapon in your off-hand, if you have the talents for it.")
-ActorInventory:defineInventory("PSIONIC_FOCUS", "Psionic focus", true, "Object held in your telekinetic grasp. It can be a weapon or some other item to provide a benefit to your psionic powers.")
-ActorInventory:defineInventory("FINGER", "On fingers", true, "Rings are worn on fingers.")
-ActorInventory:defineInventory("NECK", "Around neck", true, "Amulets are worn around the neck.")
-ActorInventory:defineInventory("LITE", "Light source", true, "A light source allows you to see in the dark places of the world.")
-ActorInventory:defineInventory("BODY", "Main armor", true, "Armor protects you from physical attacks. The heavier the armor the more it hinders the use of talents and spells.")
-ActorInventory:defineInventory("CLOAK", "Cloak", true, "A cloak can simply keep you warm or grant you wondrous powers should you find a magical one.")
-ActorInventory:defineInventory("HEAD", "On head", true, "You can wear helmets or crowns on your head.")
-ActorInventory:defineInventory("BELT", "Around waist", true, "Belts are worn around your waist.")
-ActorInventory:defineInventory("INBELT", "In your belt", true, "Put small objects in your belt. Using them from the belt only uses 60% of the normal time.")
-ActorInventory:defineInventory("HANDS", "On hands", true, "Various gloves can be worn on your hands.")
-ActorInventory:defineInventory("FEET", "On feet", true, "Sandals or boots can be worn on your feet.")
-ActorInventory:defineInventory("TOOL", "Tool", true, "This is your readied tool, always available immediately.")
-ActorInventory:defineInventory("QUIVER", "Quiver", true, "Your readied ammo.")
-ActorInventory:defineInventory("GEM", "Socketed Gems", true, "Socketed gems.")
-ActorInventory:defineInventory("MOUNT", "Mount", false, "Your mount.")
+ActorInventory:defineInventory("MAINHAND", "In main hand", true, "Most weapons are wielded in the main hand.", nil, {equipdoll_back="ui/equipdoll/mainhand_inv.png"})
+ActorInventory:defineInventory("OFFHAND", "In off hand", true, "You can use shields or a second weapon in your off-hand, if you have the talents for it.", nil, {equipdoll_back="ui/equipdoll/offhand_inv.png"})
+ActorInventory:defineInventory("PSIONIC_FOCUS", "Psionic focus", true, "Object held in your telekinetic grasp. It can be a weapon or some other item to provide a benefit to your psionic powers.", nil, {equipdoll_back="ui/equipdoll/psionic_inv.png"})
+ActorInventory:defineInventory("FINGER", "On fingers", true, "Rings are worn on fingers.", nil, {equipdoll_back="ui/equipdoll/ring_inv.png"})
+ActorInventory:defineInventory("NECK", "Around neck", true, "Amulets are worn around the neck.", nil, {equipdoll_back="ui/equipdoll/amulet_inv.png"})
+ActorInventory:defineInventory("LITE", "Light source", true, "A light source allows you to see in the dark places of the world.", nil, {equipdoll_back="ui/equipdoll/light_inv.png"})
+ActorInventory:defineInventory("BODY", "Main armor", true, "Armor protects you from physical attacks. The heavier the armor the more it hinders the use of talents and spells.", nil, {equipdoll_back="ui/equipdoll/body_inv.png"})
+ActorInventory:defineInventory("CLOAK", "Cloak", true, "A cloak can simply keep you warm or grant you wondrous powers should you find a magical one.", nil, {equipdoll_back="ui/equipdoll/cloak_inv.png"})
+ActorInventory:defineInventory("HEAD", "On head", true, "You can wear helmets or crowns on your head.", nil, {equipdoll_back="ui/equipdoll/head_inv.png"})
+ActorInventory:defineInventory("BELT", "Around waist", true, "Belts are worn around your waist.", nil, {equipdoll_back="ui/equipdoll/belt_inv.png"})
+ActorInventory:defineInventory("HANDS", "On hands", true, "Various gloves can be worn on your hands.", nil, {equipdoll_back="ui/equipdoll/hands_inv.png"})
+ActorInventory:defineInventory("FEET", "On feet", true, "Sandals or boots can be worn on your feet.", nil, {equipdoll_back="ui/equipdoll/boots_inv.png"})
+ActorInventory:defineInventory("TOOL", "Tool", true, "This is your readied tool, always available immediately.", nil, {equipdoll_back="ui/equipdoll/tool_inv.png"})
+ActorInventory:defineInventory("QUIVER", "Quiver", true, "Your readied ammo.", nil, {equipdoll_back="ui/equipdoll/ammo_inv.png"})
+ActorInventory:defineInventory("GEM", "Socketed Gems", true, "Socketed gems.", nil, {equipdoll_back="ui/equipdoll/gem_inv.png"})
 ActorInventory:defineInventory("QS_MAINHAND", "Second weapon set: In main hand", false, "Weapon Set 2: Most weapons are wielded in the main hand. Press 'x' to switch weapon sets.", true)
 ActorInventory:defineInventory("QS_OFFHAND", "Second weapon set: In off hand", false, "Weapon Set 2: You can use shields or a second weapon in your off-hand, if you have the talents for it. Press 'x' to switch weapon sets.", true)
 ActorInventory:defineInventory("QS_PSIONIC_FOCUS", "Second weapon set: psionic focus", false, "Weapon Set 2: Object held in your telekinetic grasp. It can be a weapon or some other item to provide a benefit to your psionic powers. Press 'x' to switch weapon sets.", true)
+ActorInventory.equipdolls = {
+	default = { w=48, h=48, itemframe="ui/equipdoll/itemframe48.png", itemframe_sel="ui/equipdoll/itemframe-sel48.png", ix=3, iy=3, iw=42, ih=42, doll_x=116, doll_y=168+64, list={
+		MAINHAND = {{x=48, y=120}},
+		OFFHAND = {{x=48, y=192}},
+		PSIONIC_FOCUS = {{x=48, y=48}},
+		FINGER = {{x=48, y=408}, {x=120, y=408}},
+		NECK = {{x=192, y=48}},
+		LITE = {{x=192, y=408}},
+		BODY = {{x=48, y=264}},
+		CLOAK = {{x=264, y=120}},
+		HEAD = {{x=120, y=48}},
+		BELT = {{x=264, y=264}},
+		HANDS = {{x=264, y=192}},
+		FEET = {{x=264, y=336}},
+		TOOL = {{x=264, y=408}},
+		QUIVER = {{x=48, y=336}},
+	}},
+}
 
 -- Damage types
 DamageType:loadDefinition("/data/damage_types.lua")