diff --git a/game/engines/default/engine/ui/EquipDoll.lua b/game/engines/default/engine/ui/EquipDoll.lua
index 0fbca59dd191d54187b1516561ae4089931fed4e..f1fb7b68730373306981f97195571ff289e74ab7 100644
--- a/game/engines/default/engine/ui/EquipDoll.lua
+++ b/game/engines/default/engine/ui/EquipDoll.lua
@@ -123,13 +123,34 @@ function _M:generateEquipDollFrames()
 		if inven then
 			for item, def in ipairs(v) do
 				if item > inven.max then break end
-				local frame = EquipDollFrame.new{actor=self.actor, inven=inven, name_pos=def.text, 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=self.drag_enable, subobject=self.subobject}
+
+				local frame = EquipDollFrame.new{actor=self.actor, inven=inven, name_pos=def.text, 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=self.drag_enable}
 				frame.doll_select = true
 				frame.actorWear = function(_, ...) if self.actorWear then self.actorWear(frame, ...) end end
 				frame.fct=function(button, event) if frame:getItem() and self.fct then self.fct({inven=inven, item=item, object=frame:getItem()}, button, event) end end
 				frame.filter = self.filter
 				frame.on_focus_change=function(status) local ui = self.focus_ui if self.on_select and ui then self.on_select(ui, ui.ui.inven, ui.ui.item, ui.ui:getItem()) end end
 				uis[#uis+1] = {x=def.x, y=def.y, ui=frame, _weight=def.weight}
+				
+				if self.subobject then
+					local frame = EquipDollFrame.new{actor=self.actor, inven=inven, name_pos=def.text, item=item, w=math.ceil(doll.w/2), h=math.ceil(doll.h/2), iw=math.ceil(doll.iw/2), ih=math.ceil(doll.ih/2), ix=math.floor(doll.ix/2), iy=math.floor(doll.iy/2), 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=self.drag_enable, subobject=self.subobject}
+					frame.doll_select = true
+					frame.secondary = true
+					frame.no_name = true
+					frame.actorWear = function(_, ...) if self.actorWear then self.actorWear(frame, ...) end end
+					frame.fct=function(button, event) if frame:getItem() and self.fct then self.fct({inven=inven, item=item, object=frame:getItem()}, button, event) end end
+					frame.filter = self.filter
+					frame.on_focus_change=function(status) local ui = self.focus_ui if self.on_select and ui then self.on_select(ui, ui.ui.inven, ui.ui.item, ui.ui:getItem()) end end
+
+					local dsx, dsy = doll.w + 3, 0
+					if def.subshift == "up" then dsx, dsy = 0, -math.ceil(doll.h/2) - 3
+					elseif def.subshift == "bottom" then dsx, dsy = 0, doll.h + 3
+					elseif def.subshift == "left" then dsx, dsy = -math.ceil(doll.w/2) - 3, 0
+					end
+
+					uis[#uis+1] = {x=def.x + dsx, y=def.y + dsy, ui=frame, _weight=def.weight}
+				end
+
 				max_w = math.max(def.x, max_w)
 				max_h = math.max(def.y, max_h)
 			end
diff --git a/game/engines/default/engine/ui/EquipDollFrame.lua b/game/engines/default/engine/ui/EquipDollFrame.lua
index 144a8023a3b013968b61b61d3276df8b21e65586..d23de2f9ad4ae85e467bd07e69aa0fec1de6998f 100644
--- a/game/engines/default/engine/ui/EquipDollFrame.lua
+++ b/game/engines/default/engine/ui/EquipDollFrame.lua
@@ -105,6 +105,7 @@ end
 
 function _M:drawItemShortName(o, x, y)
 	if not o then return end
+	if self.no_name then return end
 
 	local t = nil
 	if self.last_o == o then
@@ -137,16 +138,16 @@ 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)
+		self.bg_sel.t:toScreenPrecise(x, y, self.w, self.h, 0, self.bg_sel.w/self.bg_sel.tw, 0, self.bg_sel.h/self.bg_sel.th)
 	else
-		self.bg.t:toScreenFull(x, y, self.w, self.h, self.bg.tw, self.bg.th)
+		self.bg.t:toScreenPrecise(x, y, self.w, self.h, 0, self.bg.w/self.bg.tw, 0, self.bg.h/self.bg.th)
 	end
 
 	local o = self:getItem()
 	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)
+		self.bg_empty.t:toScreenPrecise(x + self.f_ix, y + self.f_iy, self.f_iw, self.f_ih, 0, self.bg_empty.w/self.bg_empty.tw, 0, self.bg_empty.h/self.bg_empty.th)
 	end
 
 	self:drawItemShortName(o, x, y)
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index d3b24df6fdb158fe947fd7b226e2a67d40fa463c..c45fa9efaee8adb28c2ab93352f17f3ef10bcd90 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -6078,3 +6078,76 @@ end
 function _M:transmoGetWord()
 	return "transmogrify"
 end
+
+function _M:doTakeoffTinker(base_o, oldo)
+	if base_o.tinker ~= oldo then return end
+
+	local mustwear = base_o.wielded
+	if mustwear then self:onTakeoff(base_o, true) end
+	base_o.tinker = nil
+	local forbid = oldo:check("on_untinker", base_o, self)
+	if oldo.tinkered then
+		for k, id in pairs(oldo.tinkered) do
+			if type(id) == "table" then base_o:removeTemporaryValue(id[1], id[2])
+			else base_o:removeTemporaryValue(k, id)
+			end
+		end
+	end
+	oldo.tinkered = nil
+	if mustwear then self:onWear(base_o, true) end
+
+	self:addObject(self.INVEN_INVEN, oldo)
+	game.logPlayer(self, "You detach %s from your %s.", oldo:getName{do_color=true}, base_o:getName{do_color=true})
+
+	return true
+end
+
+function _M:doWearTinker(wear_inven, wear_item, wear_o, base_inven, base_item, base_o, can_remove)
+	if not base_o then
+		game.logPlayer(self, "You can not use a tinker without the corresponding item.")
+		return
+	end
+	if not wear_o.is_tinker then
+		game.logPlayer(self, "This item is not a tinker.")
+		return
+	end
+	if wear_o.on_type and wear_o.on_type ~= rawget(base_o, "type") then
+		game.logPlayer(self, "This tinker can not be applied to this item.")
+		return
+	end
+	if wear_o.on_slot and wear_o.on_slot ~= base_o.slot then
+		game.logPlayer(self, "This tinker can not be applied to this item.")
+		return
+	end
+	if base_o.tinker then
+		if not can_remove then
+			game.logPlayer(self, "You already have a tinker on this item.")
+			return
+		else
+			self:doTakeoffTinker(base_o, base_o.tinker)
+		end
+	end
+
+	local mustwear = base_o.wielded
+	if mustwear then self:onTakeoff(base_o, true) end
+
+	wear_o.tinkered = {}
+	local forbid = wear_o:check("on_tinker", base_o, self)
+	if wear_o.object_tinker then
+		for k, e in pairs(wear_o.object_tinker) do
+			table.print(e)
+			wear_o.tinkered[k] = base_o:addTemporaryValue(k, e)
+		end
+	end
+
+	if mustwear then self:onWear(base_o, true) end
+
+	if not forbid then
+		base_o.tinker = wear_o
+		game.logPlayer(self, "You attach %s to your %s.", wear_o:getName{do_color=true}, base_o:getName{do_color=true})
+
+		self:removeObject(wear_inven, wear_item)
+	else
+		game.logPlayer(self, "You fail to attach %s to %s.", wear_o:getName{do_color=true}, base_o:getName{do_color=true})
+	end
+end
diff --git a/game/modules/tome/class/Object.lua b/game/modules/tome/class/Object.lua
index 9a6b3edd7a86127a2416758ff95a9c74e496697d..ecef6c607d27a9727b9ca5e82241e8d2ec7ce11c 100644
--- a/game/modules/tome/class/Object.lua
+++ b/game/modules/tome/class/Object.lua
@@ -1921,3 +1921,15 @@ function _M:addedToLevel(level, x, y)
 		self.__price_level_mod = util.getval(level.data.objects_cost_modifier, self)
 	end
 end
+
+function _M:getTinker()
+	return self.tinker
+end
+
+function _M:canAttachTinker(tinker, override)
+	if not tinker.is_tinker then return end
+	if tinker.on_type and tinker.on_type ~= rawget(self, "type") then return end
+	if tinker.on_slot and tinker.on_slot ~= self.slot then return end
+	if self.tinker and not override then return end
+	return true
+end
diff --git a/game/modules/tome/dialogs/ShowEquipInven.lua b/game/modules/tome/dialogs/ShowEquipInven.lua
index 3452c734703ef4ccb91019f0e837b670c94cf443..b1511703c3e0fb3a59621ea4eda9ad438b18ba2e 100644
--- a/game/modules/tome/dialogs/ShowEquipInven.lua
+++ b/game/modules/tome/dialogs/ShowEquipInven.lua
@@ -52,19 +52,31 @@ function _M:init(title, equip_actor, filter, action, on_select, inven_actor)
 		end
 	end
 
-	self.c_doll = EquipDoll.new{actor=equip_actor, drag_enable=true, filter=filter,
+	self.c_doll = EquipDoll.new{subobject=equip_actor:attr("can_tinker") and "getTinker" or nil, actor=equip_actor, drag_enable=true, filter=filter,
 		fct = function(item, button, event) self:use(item, button, event) end,
 		on_select = function(ui, inven, item, o) if ui.ui.last_display_x then self:select{last_display_x=ui.ui.last_display_x+ui.ui.w, last_display_y=ui.ui.last_display_y, object=o} end end,
 		actorWear = function(ui, wear_inven, wear_item, wear_o)
-			if ui:getItem() then 
-				local bi = self.equip_actor:getInven(ui.inven)
-				local ws = self.equip_actor:getInven(wear_o:wornInven())
-				local os = self.equip_actor:getObjectOffslot(wear_o)
-				if bi and ((ws and ws.id == bi.id) or (os and self.equip_actor:getInven(os).id == bi.id)) then
-					self.equip_actor:doTakeoff(ui.inven, ui.item, ui:getItem(), true, self.inven_actor)
+			if wear_o.is_tinker then
+				local base_inven, base_item = ui.inven, ui.item
+				-- Find appropriate slot
+				if not base_inven or not base_item then
+					base_inven, base_item = self:findTinkerSpot(wear_o)
 				end
+				if base_inven and base_item then
+					local base_o = base_inven[base_item]
+					self.equip_actor:doWearTinker(wear_inven, wear_item, wear_o, base_inven, base_item, base_o, true)
+				end
+			else
+				if ui:getItem() then
+					local bi = self.equip_actor:getInven(ui.inven)
+					local ws = self.equip_actor:getInven(wear_o:wornInven())
+					local os = self.equip_actor:getObjectOffslot(wear_o)
+					if bi and ((ws and ws.id == bi.id) or (os and self.equip_actor:getInven(os).id == bi.id)) then
+						self.equip_actor:doTakeoff(ui.inven, ui.item, ui:getItem(), true, self.inven_actor)
+					end
+				end
+				self.equip_actor:doWear(wear_inven, wear_item, wear_o, self.inven_actor)
 			end
-			self.equip_actor:doWear(wear_inven, wear_item, wear_o, self.inven_actor)
 			self.c_inven:generateList()
 		end
 	}
@@ -261,7 +273,12 @@ end
 
 function _M:onDragTakeoff()
 	local drag = game.mouse.dragged.payload
-	if drag.kind == "inventory" and drag.inven and self.equip_actor:getInven(drag.inven) and self.equip_actor:getInven(drag.inven).worn then
+
+	if drag.kind == "inventory" and drag.inven and self.equip_actor:getInven(drag.inven) and self.equip_actor:getInven(drag.inven).worn and drag.object.is_tinker then
+		self.equip_actor:doTakeoffTinker(self.equip_actor:getInven(drag.inven)[drag.item_idx], drag.object)
+		self.c_inven:generateList()
+		game.mouse:usedDrag()
+	elseif drag.kind == "inventory" and drag.inven and self.equip_actor:getInven(drag.inven) and self.equip_actor:getInven(drag.inven).worn then
 		self.equip_actor:doTakeoff(drag.inven, drag.item_idx, drag.object, nil, self.inven_actor)
 		self.c_inven:generateList()
 		game.mouse:usedDrag()
@@ -280,3 +297,24 @@ end
 function _M:generateList()
 	self.c_inven:generateList()
 end
+
+function _M:findTinkerSpot(tinker)
+	local possible = {}
+	self.equip_actor:inventoryApplyAll(function(inven, item, o)
+		if not inven.worn then return end
+		if o:canAttachTinker(tinker, true) then
+			possible[#possible+1] = {inven=inven, item=item, free=o.tinker and 1 or 0}
+		end
+	end)
+	if #possible == 0 then return end
+
+	-- Find one that has no atatchement if possible, if not replace one
+	table.sort(possible, function(a, b)
+		if a.free ~= b.free then return a.free < b.free
+		elseif a.inven.id ~= b.inven.id then return a.inven.id < b.inven.id
+		else return a.item < b.item
+		end
+	end)
+	table.print(possible)
+	return possible[1].inven, possible[1].item
+end
diff --git a/game/modules/tome/load.lua b/game/modules/tome/load.lua
index 9e79676c20041846a485ea2ffade00c9208608b9..8dacb8096e62168dc46349c52eba5ab103499b00 100644
--- a/game/modules/tome/load.lua
+++ b/game/modules/tome/load.lua
@@ -183,20 +183,20 @@ ActorInventory:defineInventory("QS_PSIONIC_FOCUS", "Second weapon set: psionic f
 ActorInventory:defineInventory("QS_QUIVER", "Second weapon set: Quiver", false, "Weapon Set 2: Your readied ammo.", 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={
-		PSIONIC_FOCUS = {{weight=1, x=48, y=48}},
-		MAINHAND = {{weight=2, x=48, y=120}},
-		OFFHAND = {{weight=3, x=48, y=192}},
-		BODY = {{weight=4, x=48, y=264}},
-		QUIVER = {{weight=5, x=48, y=336}},
-		FINGER = {{weight=6, x=48, y=408}, {weight=7, x=120, y=408, text="bottom"}},
-		LITE = {{weight=8, x=192, y=408}},
-		TOOL = {{weight=9, x=264, y=408, text="bottom"}},
+		PSIONIC_FOCUS = {{weight=1, x=48, y=48, subshift="left"}},
+		MAINHAND = {{weight=2, x=48, y=120, subshift="left"}},
+		OFFHAND = {{weight=3, x=48, y=192, subshift="left"}},
+		BODY = {{weight=4, x=48, y=264, subshift="left"}},
+		QUIVER = {{weight=5, x=48, y=336, subshift="left"}},
+		FINGER = {{weight=6, x=48, y=408, subshift="bottom"}, {weight=7, x=120, y=408, text="bottom", subshift="bottom"}},
+		LITE = {{weight=8, x=192, y=408, subshift="bottom"}},
+		TOOL = {{weight=9, x=264, y=408, subshift="bottom", text="bottom"}},
 		FEET = {{weight=10, x=264, y=336}},
 		BELT = {{weight=11, x=264, y=264}},
 		HANDS = {{weight=12, x=264, y=192}},
 		CLOAK = {{weight=13, x=264, y=120}},
-		NECK = {{weight=14, x=192, y=48, text="topright"}},
-		HEAD = {{weight=15, x=120, y=48, text="topleft"}},
+		NECK = {{weight=14, x=192, y=48, subshift="bottom", text="topright"}},
+		HEAD = {{weight=15, x=120, y=48, subshift="bottom", text="topleft"}},
 	}},
 	alchemist_golem = { 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 = {{weight=1, x=48, y=120}},