From b95d74ccab5deb21a2f90460e867f89556358639 Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Sat, 2 Oct 2010 20:11:26 +0000
Subject: [PATCH] plop

git-svn-id: http://svn.net-core.org/repos/t-engine4@1361 51575b47-30f0-44d4-a5cc-537603b46e54
---
 .../default/data/gfx/ui/varsel-1-sel.png      | Bin 0 -> 383 bytes
 .../default/data/gfx/ui/varsel-2-sel.png      | Bin 0 -> 234 bytes
 .../default/data/gfx/ui/varsel-3-sel.png      | Bin 0 -> 378 bytes
 .../default/data/gfx/ui/varsel-4-sel.png      | Bin 0 -> 193 bytes
 .../default/data/gfx/ui/varsel-6-sel.png      | Bin 0 -> 193 bytes
 .../default/data/gfx/ui/varsel-7-sel.png      | Bin 0 -> 313 bytes
 .../default/data/gfx/ui/varsel-8-sel.png      | Bin 0 -> 221 bytes
 .../default/data/gfx/ui/varsel-9-sel.png      | Bin 0 -> 342 bytes
 .../default/data/gfx/ui/varsel-repeat-sel.png | Bin 0 -> 182 bytes
 game/engines/default/engine/Quest.lua         |   7 +
 game/engines/default/engine/Store.lua         |   4 +-
 game/engines/default/engine/dialogs/Chat.lua  |  83 +++++------
 .../engine/dialogs/DisplayResolution.lua      |  48 ++-----
 .../default/engine/dialogs/GameMenu.lua       |  53 +++----
 .../engine/dialogs/ShowAchievements.lua       |  71 ++++------
 .../default/engine/dialogs/ShowInventory.lua  |  13 +-
 .../engine/dialogs/ShowPickupFloor.lua        |  32 -----
 .../default/engine/dialogs/ShowQuests.lua     | 101 +++++--------
 .../default/engine/dialogs/ShowStore.lua      | 134 +++++++++---------
 .../default/engine/dialogs/ShowText.lua       |  43 ++----
 .../default/engine/dialogs/SoundMusic.lua     |   2 -
 .../default/engine/dialogs/UseTalents.lua     | 133 +++++++----------
 game/engines/default/engine/ui/Dialog.lua     |   3 +-
 .../engines/default/engine/ui/ListColumns.lua |   2 +-
 game/engines/default/engine/ui/Textzone.lua   |   8 +-
 .../default/engine/ui/VariableList.lua        | 122 ++++++++++++++++
 game/modules/tome/data/birth/races/hobbit.lua |   2 +-
 .../data/talents/techniques/bloodthirst.lua   |   2 +-
 28 files changed, 408 insertions(+), 455 deletions(-)
 create mode 100644 game/engines/default/data/gfx/ui/varsel-1-sel.png
 create mode 100644 game/engines/default/data/gfx/ui/varsel-2-sel.png
 create mode 100644 game/engines/default/data/gfx/ui/varsel-3-sel.png
 create mode 100644 game/engines/default/data/gfx/ui/varsel-4-sel.png
 create mode 100644 game/engines/default/data/gfx/ui/varsel-6-sel.png
 create mode 100644 game/engines/default/data/gfx/ui/varsel-7-sel.png
 create mode 100644 game/engines/default/data/gfx/ui/varsel-8-sel.png
 create mode 100644 game/engines/default/data/gfx/ui/varsel-9-sel.png
 create mode 100644 game/engines/default/data/gfx/ui/varsel-repeat-sel.png
 create mode 100644 game/engines/default/engine/ui/VariableList.lua

diff --git a/game/engines/default/data/gfx/ui/varsel-1-sel.png b/game/engines/default/data/gfx/ui/varsel-1-sel.png
new file mode 100644
index 0000000000000000000000000000000000000000..624de9e5f2a40ad732ce7736ed326c4eafaffcb8
GIT binary patch
literal 383
zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJz0U{m4&*%Xu#^NA%Cx&(BWL|<~lf2zs82*Fc
zg1yTp14TFsJR*x37`TN&n2}-D90{Nxdx@v7EBh@jCSe5yeW#cPppa~4NJNQqer|4R
zUI~!Pz~Eeznpl#WqEMb$lA+-4=^K!um&y(lzvk)U7$R}mw|B4Kp#Yhq_0RA9-@4UT
z^O(a7**g+GbLQTuVPK1Exc-5&hQ(aio$b)OS4WN+@95mP#rLaK#1!MXbNpL>+A#Ix
zuj~ACm~YAI*Yh}!DnHJ?|8MD1<;T_czht#II}6?Y&Hm5C)j=icnXR8ws4qv`qxb!D
zXH1+SxJSK2keg8@Cq(4D->g(mr=+%GKFjvqbN%L@(s{f`Lt?t><cAYZCN$K?>h1oT
zR;>8a$Uk^e-7UtukCO`Pr#*kYDs#oHY3G+Y8Xgl2V7PfWtMu!pyXWqe?cOcFG~en_
T;dbBUAg_D6`njxgN@xNAE!vbF

literal 0
HcmV?d00001

diff --git a/game/engines/default/data/gfx/ui/varsel-2-sel.png b/game/engines/default/data/gfx/ui/varsel-2-sel.png
new file mode 100644
index 0000000000000000000000000000000000000000..d5d82b61a9bd0822ec7eb12f72c1ddb5623fca28
GIT binary patch
literal 234
zcmeAS@N?(olHy`uVBq!ia0vp^j6lr8!3HE3cV7<&2^0spJ29*~C-V}>VN3FMcVYMs
zf(!O8p9~b?EbxddW?<kJ24O~qS#u<Sg6t)pzOL-IxR`_$SX10Ojst~cGeaUuobz*Y
zQ}arITm}Z`qSVBa)D(sC%#sWRcTeAd6une-ptz%_i(?4Kb=HHsxf%=vTn|3(6;hlL
z_Hp^zzfQMJCdEd`8^3w}@ujJ|zq!b>j1LaMEK!#_uI*f~HY}vbZM*fZsK@Mg8%2E<
S++;lpvc=QY&t;ucLK6VLBS@J5

literal 0
HcmV?d00001

diff --git a/game/engines/default/data/gfx/ui/varsel-3-sel.png b/game/engines/default/data/gfx/ui/varsel-3-sel.png
new file mode 100644
index 0000000000000000000000000000000000000000..583db9da49d6dc9548f23a1c6e8c75546c70786c
GIT binary patch
literal 378
zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJz0U{m4&*%Xu#^NA%Cx&(BWL|<~lf2zs82*Fc
zg1yTp14TFsJR*x37`TN&n2}-D90{Nxdx@v7EBh@jCSe5)#hF(&0fl5ULn2C?^K)}k
z^GbkR1_tM%)Wnk16ovB4k_-iRPv3wPy;OFf_(e|_#}J9jzTJji%#IRn_f!5APB`kw
z!J-&@YRkL>R?9go8{I#!i8Vc7Z28jVE0xWy{a&HTl`FlxEdAMb&zSw6YTwWNd3f!o
z{P^|x3?A=<o-s_lIPsR61(Wf#pk41ArbNB2n84Ool^fk-xI*hu(ZPfj8meU$*(+C_
zxaq=hV&!q)mV?s2=Il>><{ucks&t94zfb3?U6Z>70u-h@y{VqFHAYXY|58Oile6Xu
ztwgKZUH5bBo~SR~|7cHU+`DV9r4>#;uRE{P@_hBvqUya-dzl40_8-!UzkH8>wGqhI
Mp00i_>zopr04CRtB>(^b

literal 0
HcmV?d00001

diff --git a/game/engines/default/data/gfx/ui/varsel-4-sel.png b/game/engines/default/data/gfx/ui/varsel-4-sel.png
new file mode 100644
index 0000000000000000000000000000000000000000..69db8e0ef7c4d122142336fad50402592691b784
GIT binary patch
literal 193
zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJ&0V4mNy#E2jDGqXXVpw-h<|Rlr$=lt9;Xep2
z*t>i(P=vF<BeIx*fm;}a85w5HkpK#^mw5WRvftuj64p`t@<CY&C?uO15>euupPQSS
zR|4cRFgO>bCYGe8D3oWGWGJ|M`Ua%vrLqIXB|KdmLpZJ{r}XyyJAdH70fn^mLm%cd
ZGBQ{uF+10F+^+^{_H^}gS?83{1OPBvHQ4|F

literal 0
HcmV?d00001

diff --git a/game/engines/default/data/gfx/ui/varsel-6-sel.png b/game/engines/default/data/gfx/ui/varsel-6-sel.png
new file mode 100644
index 0000000000000000000000000000000000000000..8f70f2704d67c5ebc910e37a41a318bc95a092e6
GIT binary patch
literal 193
zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJ&0V4mNy#E2jDGqXXVpw-h<|Rlr$=lt9;Xep2
z*t>i(P=vF<BeIx*fm;}a85w5HkpK#^mw5WRvftuj64nvD>T-QOP)Ig2B%;JQKQ}iu
zuLQ_tU~n!<O)N=GQ7F$W$xv|j^bJVSOJxU&OL)3ChHzX@o-)t$p*^!uMd=3<W)NU-
WHeuQG=y}!(kY-O;KbLh*2~7a-e=`gK

literal 0
HcmV?d00001

diff --git a/game/engines/default/data/gfx/ui/varsel-7-sel.png b/game/engines/default/data/gfx/ui/varsel-7-sel.png
new file mode 100644
index 0000000000000000000000000000000000000000..f16fb6f67c400a962458e196780c88dd0396d8e9
GIT binary patch
literal 313
zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJz0U{m4&*%Xu#^NA%Cx&(BWL|<~lf2zs82*Fc
zg1yTp14TFsJR*x37`TN&n2}-D90{Nxdx@v7EBh@jCSiGlEbX)#Kq1-8kcblJ{M_8s
zyb>Unfx)>bHL)Z$MWH;iBtya7(>EYRFO?lAKEcz)F+}2WY_B2LAqN51`wlF-6DEkr
zDYPayD4L1wY%uo_TvM#v|D?4n{ET0h&!b71Z9nt>{dY3jy)L_f@x{>=o(ksmPc=91
z?P|Y0^YP=Z8Go4zs<utZZJw3Pa6(`7=-#xFL{?jexNsNo8)X~xx$Ep0x|Sq-iFXlY
uxgcWEucP&e{UWQ7ZSST>xgjPCtnLf1f2qr7>aG6{<Y-S<KbLh*2~7Y+<7b%w

literal 0
HcmV?d00001

diff --git a/game/engines/default/data/gfx/ui/varsel-8-sel.png b/game/engines/default/data/gfx/ui/varsel-8-sel.png
new file mode 100644
index 0000000000000000000000000000000000000000..755ba689d10eb841069e171c7c7b03e7e266b436
GIT binary patch
literal 221
zcmeAS@N?(olHy`uVBq!ia0vp^j6lr8!3HE3cV7<&2^0spJ29*~C-V}>VN3FMcVYMs
zf(!O8p9~b?EbxddW?<kJ24O~qS#u<Sg6t)pzOL-IxR`_$geOe0ody(=%?ybsan8@p
zP0cF-av2z$i&7IyQd1PlGfOfQ+&z5*QuI>Uf#N2fE{-7_*JDrY<YiD0VA*)l>xzrv
z6<^6C-#eI#WM-Ki6J@A9ySmFIwXcs=%Rl5@M)aTW41QPSm4nVN;RYG(>FVdQ&MBb@
E0FgvK=Kufz

literal 0
HcmV?d00001

diff --git a/game/engines/default/data/gfx/ui/varsel-9-sel.png b/game/engines/default/data/gfx/ui/varsel-9-sel.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a21f105f6a4e6de4acfef9b9f031add6c09a316
GIT binary patch
literal 342
zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJz0U{m4&*%Xu#^NA%Cx&(BWL|<~lf2zs82*Fc
zg1yTp14TFsJR*x37`TN&n2}-D90{Nxdx@v7EBh@jCSe8R)qiB;fI_mFArU3c`MJ5N
zc_lzD1A}u>YGO%hib8p2Nrr;Er*A-tUMf3Ke66R8V~E7%)U&pHjE)k=KK_5hx3s^2
zkyWTuQ^lf1*=Om+4(`m5yax<lgj$Ui3K^`MZw9{UyZd3v`}dWfkBF|VT@#`n_G;3+
z^WmR5x~|Prl1}(|EAM__`O?fjHpXWMq#Szk)%)9jKRi*ooxj!diBPxkv}+-O^52+r
zl+%qodY3Nz#h7+!ruu}p;n7<ea$m}^DKd#pJZB^L^Jce^3fsZ@CB|YOFSAX^O!~QR
aN}F9gx5KTM`O~k0eB|ls=d#Wzp$Py|`Fel=

literal 0
HcmV?d00001

diff --git a/game/engines/default/data/gfx/ui/varsel-repeat-sel.png b/game/engines/default/data/gfx/ui/varsel-repeat-sel.png
new file mode 100644
index 0000000000000000000000000000000000000000..bacddd7f3ef55f9479eefa57d597297f91c50e13
GIT binary patch
literal 182
zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k92}K#X;^)4C~IxyaaOClDyqr82*Fc
zg1yTp14TFsJR*x37`TN&n2}-D90{Nxdx@v7EBh@jCSiF=jhX+p0EJ{TLn2C?^K)}k
z^GbkR1_tM%)Wnk16ovB4k_-iRPv3wPy;OFfIIpLRV+hCf<O#EG{+DN931xB$d>sED
Oq{GwI&t;ucLK6UyRxULF

literal 0
HcmV?d00001

diff --git a/game/engines/default/engine/Quest.lua b/game/engines/default/engine/Quest.lua
index 2b92781aa9..9434989773 100644
--- a/game/engines/default/engine/Quest.lua
+++ b/game/engines/default/engine/Quest.lua
@@ -27,6 +27,13 @@ COMPLETED = 1
 DONE = 100
 FAILED = 101
 
+status_text = {
+	[PENDING] = "active",
+	[COMPLETED] = "completed",
+	[DONE] = "done",
+	[FAILED] = "failed",
+}
+
 function _M:init(q, who)
 	for k, e in pairs(q) do
 		self[k] = e
diff --git a/game/engines/default/engine/Store.lua b/game/engines/default/engine/Store.lua
index da0d5e1233..e3e12ee30e 100644
--- a/game/engines/default/engine/Store.lua
+++ b/game/engines/default/engine/Store.lua
@@ -113,7 +113,7 @@ function _M:doBuy(who, o, item, nb, store_dialog)
 			who.changed = true
 			self:onBuy(who, o, item, nb)
 			if store_dialog then store_dialog:updateStore() end
-		end end)
+		end end, "Buy", "Cancel")
 	end
 end
 
@@ -134,7 +134,7 @@ function _M:doSell(who, o, item, nb, store_dialog)
 			who.changed = true
 			self:onSell(who, o, item, nb)
 			if store_dialog then store_dialog:updateStore() end
-		end end)
+		end end, "Sell", "Cancel")
 	end
 end
 
diff --git a/game/engines/default/engine/dialogs/Chat.lua b/game/engines/default/engine/dialogs/Chat.lua
index d3e8e460c4..183cb5b56e 100644
--- a/game/engines/default/engine/dialogs/Chat.lua
+++ b/game/engines/default/engine/dialogs/Chat.lua
@@ -18,51 +18,47 @@
 -- darkgod@te4.org
 
 require "engine.class"
-require "engine.Dialog"
+local Dialog = require "engine.ui.Dialog"
+local VariableList = require "engine.ui.VariableList"
+local Textzone = require "engine.ui.Textzone"
+local Separator = require "engine.ui.Separator"
 
-module(..., package.seeall, class.inherit(engine.Dialog))
+module(..., package.seeall, class.inherit(Dialog))
 
 function _M:init(chat, id)
 	self.cur_id = id
 	self.chat = chat
 	self.npc = chat.npc
 	self.player = chat.player
-	engine.Dialog.init(self, self.npc.name, 500, 400)
+	Dialog.init(self, self.npc.name, 500, 400)
 
 	self:generateList()
 
-	self.sel = 1
-	self.scroll = 1
-	self.max = math.floor((self.ih - 5) / self.font_h) - 1
+	self.c_desc = Textzone.new{width=self.iw - 10, height=1, auto_height=true, no_color_bleed=true, text=self.text.."\n"}
 
-	self:keyCommands({
+	self:generateList()
+
+	self.c_list = VariableList.new{width=self.iw - 10, list=self.list, fct=function(item) self:use(item) end}
+
+	self:loadUI{
+		{left=0, top=0, ui=self.c_desc},
+		{left=0, bottom=0, ui=self.c_list},
+		{left=5, top=self.c_desc.h - 10, ui=Separator.new{dir="vertical", size=self.iw - 10}},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI(false, true)
+
+	self.key:addCommands{
 		__TEXTINPUT = function(c)
-			if c:find("^[a-z]$") then
-				self.sel = util.bound(1 + string.byte(c) - string.byte('a'), 1, #self.list)
-				self:use()
+			if self.list and self.list.chars[c] then
+				self:use(self.list[self.list.chars[c]])
 			end
 		end,
-	},{
-		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		ACCEPT = function() self:use() end,
-	})
-	self:mouseZones{
-		{ x=0, y=0, w=self.w, h=self.h, fct=function(button, x, y, xrel, yrel, tx, ty, event)
-			if self.start_answer_y and y >= self.start_answer_y then
-				ty = ty - self.start_answer_y
-				self.sel = util.bound(self.scroll + math.floor(ty / self.font_h), 1, #self.list)
-				self.changed = true
-				if button == "left" and event == "button" then self:use()
-				elseif button == "right" and event == "button" then
-				end
-			end
-		end },
 	}
 end
 
-function _M:use(a)
-	a = a or self.chat:get(self.cur_id).answers[self.list[self.sel].answer]
+function _M:use(item, a)
+	a = a or self.chat:get(self.cur_id).answers[item.answer]
 	if not a then return end
 
 	self.changed = true
@@ -86,17 +82,17 @@ function _M:use(a)
 end
 
 function _M:regen()
-	self.changed = true
-	self:generateList()
-	self.sel = 1
-	self.scroll = 1
+	game:unregisterDialog(self)
+	local d = new(self.chat, self.cur_id)
+	d.__showup = false
+	game:registerDialog(d)
 end
 
 function _M:resolveAuto()
 	if not self.chat:get(self.cur_id).auto then return end
 	for i, a in ipairs(self.chat:get(self.cur_id).answers) do
 		if not a.cond or a.cond(self.npc, self.player) then
-			if not self:use(a) then return
+			if not self:use(nil, a) then return
 			else return self:resolveAuto()
 			end
 		end
@@ -107,31 +103,18 @@ function _M:generateList()
 	self:resolveAuto()
 
 	-- Makes up the list
-	local list = {}
+	local list = { chars={} }
 	local nb = 1
 	for i, a in ipairs(self.chat:get(self.cur_id).answers) do
 		if not a.cond or a.cond(self.npc, self.player) then
 			list[#list+1] = { name=string.char(string.byte('a')+nb-1)..") "..a[1], answer=i, color=a.color}
+			list.chars[string.char(string.byte('a')+nb-1)] = #list
 			nb = nb + 1
 		end
 	end
 	self.list = list
-	return true
-end
 
-function _M:drawDialog(s)
-	local h = 5
-	local lines = self.chat:replace(self.chat:get(self.cur_id).text):splitLines(self.iw - 10, self.font)
-	local r, g, b
-	for i = 1, #lines do
-		r, g, b = s:drawColorStringBlended(self.font, lines[i], 5, 2 + h, r, g, b)
-		h = h + self.font:lineSkip()
-	end
-
-	self:drawWBorder(s, 5, h + 0.5 * self.font:lineSkip(), self.iw - 10)
+	self.text = self.chat:replace(self.chat:get(self.cur_id).text)
 
-	-- Answers
-	self.start_answer_y = h + 1.5 * self.font:lineSkip()
-	self:drawSelectionList(s, 5, h + 1.5 * self.font:lineSkip(), self.font_h, self.list, self.sel, "name", self.scroll, self.max, nil, nil, self.iw - 10)
-	self.changed = false
+	return true
 end
diff --git a/game/engines/default/engine/dialogs/DisplayResolution.lua b/game/engines/default/engine/dialogs/DisplayResolution.lua
index 82f4a01158..d434c38b0f 100644
--- a/game/engines/default/engine/dialogs/DisplayResolution.lua
+++ b/game/engines/default/engine/dialogs/DisplayResolution.lua
@@ -18,46 +18,31 @@
 -- darkgod@te4.org
 
 require "engine.class"
-require "engine.Dialog"
+local Dialog = require "engine.ui.Dialog"
+local List = require "engine.ui.List"
 
-module(..., package.seeall, class.inherit(engine.Dialog))
+module(..., package.seeall, class.inherit(Dialog))
 
 function _M:init()
 	self:generateList()
 
-	engine.Dialog.init(self, "Switch Resolution", 300, #self.list * 30 + 20)
+	Dialog.init(self, "Switch Resolution", 300, 20)
 
-	self.sel = 1
-	self.scroll = 1
-	self.max = math.floor((self.ih - 5) / self.font_h) - 1
+	self.c_list = List.new{width=self.iw, nb_items=#self.list, list=self.list, fct=function(item) self:use(item) end}
 
-	self:keyCommands({
-		__TEXTINPUT = function(c)
-			if c:find("^[a-z]$") then
-				self.sel = util.bound(1 + string.byte(c) - string.byte('a'), 1, #self.list)
-				self:use()
-			end
-		end,
-	},{
-		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		ACCEPT = function() self:use() end,
+	self:loadUI{
+		{left=0, top=0, ui=self.c_list},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI(false, true)
+
+	self.key:addBinds{
 		EXIT = function() game:unregisterDialog(self) end,
-	})
-	self:mouseZones{
-		{ x=0, y=0, w=game.w, h=game.h, mode={button=true}, norestrict=true, fct=function(button) if button == "left" then game:unregisterDialog(self) end end},
-		{ x=2, y=5, w=350, h=self.font_h*self.max, fct=function(button, x, y, xrel, yrel, tx, ty, event)
-			self.changed = true
-			self.sel = util.bound(self.scroll + math.floor(ty / self.font_h), 1, #self.list)
-			if button == "left" and event == "button" then self:use()
-			elseif button == "right" and event == "button" then
-			end
-		end },
 	}
 end
 
-function _M:use()
-	game:setResolution(self.list[self.sel].r)
+function _M:use(item)
+	game:setResolution(item.r)
 	game:unregisterDialog(self)
 end
 
@@ -85,8 +70,3 @@ function _M:generateList()
 	end
 	self.list = list
 end
-
-function _M:drawDialog(s)
-	self:drawSelectionList(s, 2, 5, self.font_h, self.list, self.sel, "name", self.scroll, self.max)
-	self.chanegd = false
-end
diff --git a/game/engines/default/engine/dialogs/GameMenu.lua b/game/engines/default/engine/dialogs/GameMenu.lua
index 26f087735a..f23d6defc1 100644
--- a/game/engines/default/engine/dialogs/GameMenu.lua
+++ b/game/engines/default/engine/dialogs/GameMenu.lua
@@ -18,47 +18,31 @@
 -- darkgod@te4.org
 
 require "engine.class"
-require "engine.Dialog"
+local Dialog = require "engine.ui.Dialog"
+local List = require "engine.ui.List"
 
-module(..., package.seeall, class.inherit(engine.Dialog))
+module(..., package.seeall, class.inherit(Dialog))
 
 function _M:init(actions)
 	self:generateList(actions)
 
-	engine.Dialog.init(self, "Game Menu", 300, #self.list * 30 + 20)
+	Dialog.init(self, "Game Menu", 300, 20)
 
-	self.sel = 1
-	self.scroll = 1
-	self.max = math.floor((self.ih - 5) / self.font_h) - 1
+	self.c_list = List.new{width=self.iw, nb_items=#self.list, list=self.list, fct=function(item) self:use(item) end}
 
-	self:keyCommands({
-		__TEXTINPUT = function(c)
-			if c:find("^[a-z]$") then
-				self.sel = util.bound(1 + string.byte(c) - string.byte('a'), 1, #self.list)
-				self.changed = true
-				self:use()
-			end
-		end,
-	},{
-		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		ACCEPT = function() self:use() end,
+	self:loadUI{
+		{left=0, top=0, ui=self.c_list},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI(false, true)
+
+	self.key:addBinds{
 		EXIT = function() game:unregisterDialog(self) end,
-	})
-	self:mouseZones{
-		{ x=0, y=0, w=game.w, h=game.h, mode={button=true}, norestrict=true, fct=function(button) if button == "left" then game:unregisterDialog(self) end end},
-		{ x=2, y=5, w=350, h=self.font_h*self.max, fct=function(button, x, y, xrel, yrel, tx, ty, event)
-			self.changed = true
-			self.sel = util.bound(self.scroll + math.floor(ty / self.font_h), 1, #self.list)
-			if button == "left" and event == "button" then self:use()
-			elseif button == "right" and event == "button" then
-			end
-		end },
 	}
 end
 
-function _M:use()
-	self.list[self.sel].fct()
+function _M:use(item)
+	item.fct()
 end
 
 function _M:generateList(actions)
@@ -94,18 +78,13 @@ function _M:generateList(actions)
 	for _, act in ipairs(actions) do
 		if type(act) == "string" then
 			local a = default_actions[act]
-			list[#list+1] = { name=string.char(string.byte('a') + i)..")  "..a[1], fct=a[2] }
+			list[#list+1] = { name=a[1], fct=a[2] }
 			i = i + 1
 		else
 			local a = act
-			list[#list+1] = { name=string.char(string.byte('a') + i)..")  "..a[1], fct=a[2] }
+			list[#list+1] = { name=a[1], fct=a[2] }
 			i = i + 1
 		end
 	end
 	self.list = list
 end
-
-function _M:drawDialog(s)
-	self:drawSelectionList(s, 2, 5, self.font_h, self.list, self.sel, "name", self.scroll, self.max)
-	self.changed = false
-end
diff --git a/game/engines/default/engine/dialogs/ShowAchievements.lua b/game/engines/default/engine/dialogs/ShowAchievements.lua
index 33a6482552..4f5b37cc16 100644
--- a/game/engines/default/engine/dialogs/ShowAchievements.lua
+++ b/game/engines/default/engine/dialogs/ShowAchievements.lua
@@ -18,70 +18,59 @@
 -- darkgod@te4.org
 
 require "engine.class"
-require "engine.Dialog"
+local Dialog = require "engine.ui.Dialog"
+local ListColumns = require "engine.ui.ListColumns"
+local Textzone = require "engine.ui.Textzone"
+local Separator = require "engine.ui.Separator"
 
-module(..., package.seeall, class.inherit(engine.Dialog))
+module(..., package.seeall, class.inherit(Dialog))
 
 function _M:init(title)
 	local total = #world.achiev_defs
 	local nb = 0
 	for id, data in pairs(world.achieved) do nb = nb + 1 end
 
-	engine.Dialog.init(self, (title or "Achievements").." ("..nb.."/"..total..")", game.w * 0.8, game.h * 0.8, nil, nil, nil, core.display.newFont("/data/font/VeraMono.ttf", 12))
+	Dialog.init(self, (title or "Achievements").." ("..nb.."/"..total..")", game.w * 0.8, game.h * 0.8)
+
+	self.c_desc = Textzone.new{width=math.floor(self.iw / 2 - 10), height=self.ih, text=""}
 
 	self:generateList()
 
-	self.sel = 1
-	self.scroll = 1
-	self.max = math.floor((self.ih - 5) / self.font_h) - 1
+	self.c_list = ListColumns.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, scrollbar=true, sortable=true, columns={
+		{name="Achievement", width=60, display_prop="name", sort="name"},
+		{name="When", width=20, display_prop="when", sort="when"},
+		{name="Who", width=20, display_prop="who", sort="who"},
+	}, list=self.list, fct=function(item) end, select=function(item, sel) self:select(item) end}
+
+	self:loadUI{
+		{left=0, top=0, ui=self.c_list},
+		{right=0, top=0, ui=self.c_desc},
+		{hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI()
 
-	self:keyCommands({},{
-		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
+	self.key:addBinds{
 		EXIT = function() game:unregisterDialog(self) end,
-	})
-	self:mouseZones{
-		{ x=0, y=0, w=game.w, h=game.h, mode={button=true}, norestrict=true, fct=function(button) if button == "left" then game:unregisterDialog(self) end end},
-		{ x=2, y=5, w=350, h=self.font_h*self.max, fct=function(button, x, y, xrel, yrel, tx, ty)
-			self.changed = true
-			self.sel = util.bound(self.scroll + math.floor(ty / self.font_h), 1, #self.list)
-			if button == "wheelup" and event == "button" then self.key:triggerVirtual("MOVE_UP")
-			elseif button == "wheeldown" and event == "button" then self.key:triggerVirtual("MOVE_DOWN")
-			end
-		end },
 	}
 end
 
+function _M:select(item)
+	if item then
+		self.uis[2].ui = item.zone
+	end
+end
+
 function _M:generateList()
 	-- Makes up the list
 	local list = {}
 	local i = 0
 	for id, data in pairs(world.achieved) do
 		local a = world:getAchievementFromId(id)
-		list[#list+1] = { name=a.name,  desc=a.desc, when=data.when, who=data.who, order=a.order }
+		local zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text=("#GOLD#Achieved on:#LAST# %s\n#GOLD#Achieved by:#LAST# %s\n\n#GOLD#Description:#LAST# %s"):format(data.when, data.who, a.desc)}
+		list[#list+1] = { zone=zone, name=a.name,  desc=a.desc, when=data.when, who=data.who, order=a.order }
 		i = i + 1
 	end
 	table.sort(list, function(a, b) return a.name < b.name end)
 	self.list = list
 end
-
-function _M:drawDialog(s)
-	-- Description part
-	self:drawHBorder(s, self.iw / 2, 2, self.ih - 4)
-
-	local h = 2
-	if self.list[self.sel] then
-		local str = ("#GOLD#Achieved on:#LAST# %s\n#GOLD#Achieved by:#LAST# %s\n\n#GOLD#Description:#LAST# %s"):format(self.list[self.sel].when, self.list[self.sel].who, self.list[self.sel].desc)
-		lines = str:splitLines(self.iw / 2 - 10, self.font)
-	else
-		lines = {}
-	end
-	self:drawWBorder(s, self.iw / 2 + self.iw / 6, h - 0.5 * self.font:lineSkip(), self.iw / 6)
-	for i = 1, #lines do
-		s:drawColorStringBlended(self.font, lines[i], self.iw / 2 + 5, 2 + h)
-		h = h + self.font:lineSkip()
-	end
-
-	self:drawSelectionList(s, 2, 5, self.font_h, self.list, self.sel, "name", self.scroll, self.max, nil, nil, nil, self.iw / 2 - 5, true)
-	self.changed = false
-end
diff --git a/game/engines/default/engine/dialogs/ShowInventory.lua b/game/engines/default/engine/dialogs/ShowInventory.lua
index 3195ccb74b..b57c8420c0 100644
--- a/game/engines/default/engine/dialogs/ShowInventory.lua
+++ b/game/engines/default/engine/dialogs/ShowInventory.lua
@@ -36,11 +36,11 @@ function _M:init(title, inven, filter, action, actor)
 
 	self:generateList()
 
-	self.c_list = ListColumns.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, scrollbar=true, columns={
-		{name="", width=4, display_prop="char"},
-		{name="Inventory", width=68, display_prop="name"},
-		{name="Category", width=20, display_prop="cat"},
-		{name="Enc.", width=8, display_prop="encumberance"},
+	self.c_list = ListColumns.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, sortable=true, scrollbar=true, columns={
+		{name="", width=4, display_prop="char", sort="id"},
+		{name="Inventory", width=68, display_prop="name", sort="name"},
+		{name="Category", width=20, display_prop="cat", sort="cat"},
+		{name="Enc.", width=8, display_prop="encumberance", sort="encumberance"},
 	}, list=self.list, fct=function(item) self:use(item) end, select=function(item, sel) self:select(item) end}
 
 	self:loadUI{
@@ -59,9 +59,6 @@ function _M:init(title, inven, filter, action, actor)
 		end,
 	}
 	self.key:addBinds{
-		ACCEPT = function()
-			self:use(self.c_list.list[self.c_list.sel])
-		end,
 		EXIT = function() game:unregisterDialog(self) end,
 	}
 end
diff --git a/game/engines/default/engine/dialogs/ShowPickupFloor.lua b/game/engines/default/engine/dialogs/ShowPickupFloor.lua
index 5c6100629d..081030e688 100644
--- a/game/engines/default/engine/dialogs/ShowPickupFloor.lua
+++ b/game/engines/default/engine/dialogs/ShowPickupFloor.lua
@@ -122,35 +122,3 @@ function _M:generateList()
 		self.c_list:generate()
 	end
 end
-
-function _M:drawDialog(s)
-	-- Description part
-	self:drawHBorder(s, self.iw / 2, 2, self.ih - 4)
-
-	local talentshelp = ([[Keyboard: #00FF00#up key/down key#FFFFFF# to select an object; #00FF00#enter#FFFFFF# to use.
-Mouse: #00FF00#Left click#FFFFFF# to pickup.
-]]):splitLines(self.iw / 2 - 10, self.font)
-
-	local lines = {}
-	local h = 2
-	for i = 1, #talentshelp do
-		s:drawColorStringBlended(self.font, talentshelp[i], self.iw / 2 + 5, h)
-		h = h + self.font:lineSkip()
-	end
-
-	h = h + self.font:lineSkip()
-	if self.list[self.sel] then
-		lines = self.list[self.sel].object:getDesc():splitLines(self.iw / 2 - 10, self.font)
-	else
-		lines = {}
-	end
-	self:drawWBorder(s, self.iw / 2 + self.iw / 6, h - 0.5 * self.font:lineSkip(), self.iw / 6)
-	for i = 1, #lines do
-		s:drawColorStringBlended(self.font, lines[i], self.iw / 2 + 5, 2 + h)
-		h = h + self.font:lineSkip()
-	end
-
-	-- Talents
-	self:drawSelectionList(s, 2, 5, self.font_h, self.list, self.sel, "name", self.scroll, self.max, nil, nil, nil, self.iw / 2 - 5, true)
-	self.changed = false
-end
diff --git a/game/engines/default/engine/dialogs/ShowQuests.lua b/game/engines/default/engine/dialogs/ShowQuests.lua
index 8f447424d4..89b65c1f48 100644
--- a/game/engines/default/engine/dialogs/ShowQuests.lua
+++ b/game/engines/default/engine/dialogs/ShowQuests.lua
@@ -18,53 +18,60 @@
 -- darkgod@te4.org
 
 require "engine.class"
-require "engine.Dialog"
+local Dialog = require "engine.ui.Dialog"
+local ListColumns = require "engine.ui.ListColumns"
+local Textzone = require "engine.ui.Textzone"
+local Separator = require "engine.ui.Separator"
 
-module(..., package.seeall, class.inherit(engine.Dialog))
+module(..., package.seeall, class.inherit(Dialog))
 
 function _M:init(actor)
 	self.actor = actor
-	actor.hotkey = actor.hotkey or {}
-	engine.Dialog.init(self, "Quest Log for "..actor.name, game.w, game.h)
+	Dialog.init(self, "Quest Log for "..actor.name, game.w, game.h)
+
+	self.c_desc = Textzone.new{width=math.floor(self.iw / 2 - 10), height=self.ih, text=""}
 
 	self:generateList()
 
-	self.sel = 1
-	self.scroll = 1
-	self.max = math.floor((self.ih - 5) / self.font_h) - 1
+	self.c_list = ListColumns.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, scrollbar=true, sortable=true, columns={
+		{name="Quest", width=70, display_prop="name", sort="name"},
+		{name="Status", width=30, display_prop="status", sort="status_order"},
+	}, list=self.list, fct=function(item) end, select=function(item, sel) self:select(item) end}
 
-	self:keyCommands({
-		_t = function()
-			self.sel = 1
-			self.scroll = 1
-			self.show_ended = not self.show_ended
-			self:generateList()
-			self.changed = true        print("plop")
-		end,
-	},{
-		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		EXIT = function() game:unregisterDialog(self) end,
-	})
-	self:mouseZones{
-		{ x=0, y=0, w=game.w, h=game.h, mode={button=true}, norestrict=true, fct=function(button) if button == "left" then game:unregisterDialog(self) end end},
-		{ x=2, y=5, w=350, h=self.font_h*self.max, fct=function(button, x, y, xrel, yrel, tx, ty, event)
-			self.changed = true
-			self.sel = util.bound(self.scroll + math.floor(ty / self.font_h), 1, #self.list)
+	self:loadUI{
+		{left=0, top=0, ui=self.c_list},
+		{right=0, top=0, ui=self.c_desc},
+		{hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI()
+	self:select(self.list[1])
+	self.c_list:selectColumn(2)
 
-			if button == "wheelup" and event == "button" then self.key:triggerVirtual("MOVE_UP")
-			elseif button == "wheeldown" and event == "button" then self.key:triggerVirtual("MOVE_DOWN")
-			end
-		end },
+	self.key:addBinds{
+		EXIT = function() game:unregisterDialog(self) end,
 	}
 end
 
+function _M:select(item)
+	if item then
+		self.uis[2].ui = item.zone
+	end
+end
+
 function _M:generateList()
 	-- Makes up the list
 	local list = {}
 	for id, q in pairs(self.actor.quests or {}) do
-		if not q:isEnded() or self.show_ended then
-			list[#list+1] = { name=q.name, quest=q, color = q:isCompleted() and {0,255,0} or nil }
+		if true then
+			local zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text=q:desc(self.actor)}
+			local color = nil
+			if q:isStatus(q.COMPLETED) then color = colors.simple(colors.LIGHT_GREEN)
+			elseif q:isStatus(q.DONE) then color = colors.simple(colors.GREEN)
+			elseif q:isStatus(q.FAILED) then color = colors.simple(colors.RED)
+			end
+
+			list[#list+1] = { zone=zone, name=q.name, quest=q, color = color, status=q.status_text[q.status], status_order=q.status }
 		end
 	end
 	if game.turn then
@@ -74,35 +81,3 @@ function _M:generateList()
 	end
 	self.list = list
 end
-
-function _M:drawDialog(s)
-	-- Description part
-	self:drawHBorder(s, self.iw / 2, 2, self.ih - 4)
-
-	local r, g, b
-	local help = [[Keyboard: #00FF00#up key/down key#FFFFFF# to select a quest; #00FF00#t#FFFFFF# to toggle finished quests.
-]]
-	local talentshelp = help:splitLines(self.iw / 2 - 10, self.font)
-
-	local lines = {}
-	if self.list[self.sel] then
-		lines = self.list[self.sel].quest:desc(self.actor):splitLines(self.iw / 2 - 10, self.font)
-	end
-
-	local h = 2
-	for i = 1, #talentshelp do
-		s:drawColorStringBlended(self.font, talentshelp[i], self.iw / 2 + 5, h)
-		h = h + self.font:lineSkip()
-	end
-
-	h = h + self.font:lineSkip()
-	self:drawWBorder(s, self.iw / 2 + self.iw / 6, h - 0.5 * self.font:lineSkip(), self.iw / 6)
-	for i = 1, #lines do
-		r, g, b = s:drawColorStringBlended(self.font, lines[i], self.iw / 2 + 5, 2 + h, r, g, b)
-		h = h + self.font:lineSkip()
-	end
-
-	-- Talents
-	self:drawSelectionList(s, 2, 5, self.font_h, self.list, self.sel, "name", self.scroll, self.max, nil, nil, nil, self.iw / 2 - 5, true)
-	self.changed = false
-end
diff --git a/game/engines/default/engine/dialogs/ShowStore.lua b/game/engines/default/engine/dialogs/ShowStore.lua
index 0199a1ce8b..6e583b47f7 100644
--- a/game/engines/default/engine/dialogs/ShowStore.lua
+++ b/game/engines/default/engine/dialogs/ShowStore.lua
@@ -18,9 +18,12 @@
 -- darkgod@te4.org
 
 require "engine.class"
-require "engine.Dialog"
+local Dialog = require "engine.ui.Dialog"
+local ListColumns = require "engine.ui.ListColumns"
+local Textzone = require "engine.ui.Textzone"
+local Separator = require "engine.ui.Separator"
 
-module(..., package.seeall, class.inherit(engine.Dialog))
+module(..., package.seeall, class.inherit(Dialog))
 
 function _M:init(title, store_inven, actor_inven, store_filter, actor_filter, action, desc)
 	self.action = action
@@ -29,67 +32,69 @@ function _M:init(title, store_inven, actor_inven, store_filter, actor_filter, ac
 	self.actor_inven = actor_inven
 	self.store_filter = store_filter
 	self.actor_filter = actor_filter
-	engine.Dialog.init(self, title or "Store", game.w * 0.8, game.h * 0.8, nil, nil, nil, core.display.newFont("/data/font/VeraMono.ttf", 12))
+	Dialog.init(self, title or "Store", game.w * 0.8, game.h * 0.8)
 
 	self:generateList()
 
-	self.list = self.store_list
-	self.sel = 1
-	self.scroll = 1
---	self.max = math.floor((self.ih * 0.8 - 5) / self.font_h) - 1
+	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=4, display_prop="char", sort="id"},
+		{name="Inventory", width=68, display_prop="name", sort="name"},
+		{name="Category", width=20, display_prop="cat", sort="cat"},
+		{name="Price", width=8, display_prop="cost", sort="cost"},
+	}, list=self.actor_list, fct=function(item, sel) self:use(item) end, select=function(item, sel) self:select(item) end}
 
-	self:keyCommands({
+	self.c_store = 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=4, display_prop="char", sort="id"},
+		{name="Store", width=68, display_prop="name"},
+		{name="Category", width=20, display_prop="cat"},
+		{name="Price", width=8, display_prop="cost", sort="cost"},
+	}, list=self.store_list, fct=function(item) self:use(item) end, select=function(item, sel) self:select(item) end}
+
+	self.c_desc = Textzone.new{width=self.iw, height=self.max_h*self.font_h, no_color_bleed=true, text=""}
+
+	self:loadUI{
+		{left=0, top=0, ui=self.c_store},
+		{right=0, top=0, ui=self.c_inven},
+		{left=0, bottom=0, ui=self.c_desc},
+		{hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - self.c_desc.h - 10}},
+		{left=5, bottom=self.c_desc.h, ui=Separator.new{dir="vertical", size=self.iw - 10}},
+	}
+	self:setFocus(self.c_inven)
+	self:setupUI()
+
+	self.key:addCommands{
 		__TEXTINPUT = function(c)
-			if c:find("^[a-z]$") then
-				self.sel = util.bound(1 + string.byte(c) - string.byte('a'), 1, #self.list)
-				self:use()
+			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,
-	},{
-		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		MOVE_LEFT = function() self.list = self.store_list self.sel = util.bound(self.sel, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		MOVE_RIGHT = function() self.list = self.actor_list self.sel = util.bound(self.sel, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		ACCEPT = function() self:use() end,
+	}
+	self.key:addBinds{
 		EXIT = function() game:unregisterDialog(self) end,
-	})
-	self:mouseZones{
-		{ x=0, y=0, w=game.w, h=game.h, mode={button=true}, norestrict=true, fct=function(button) if button == "left" then game:unregisterDialog(self) end end},
-		{ x=2, y=5, w=self.iw, h=self.font_h*self.max, fct=function(button, x, y, xrel, yrel, tx, ty, event)
-			if tx < self.iw / 2 then
-				self.list = self.store_list
-			else
-				self.list = self.actor_list
-			end
-			if button ~= "wheelup" and button ~= "wheeldown" then
-				self.sel = util.bound(self.scroll + math.floor(ty / self.font_h), 1, #self.list)
-			end
-			self.changed = true
-
-			if button == "left" and event == "button" then self:use()
-			elseif button == "right" and event == "button" then
-			elseif button == "wheelup" and event == "button" then self.key:triggerVirtual("MOVE_UP")
-			elseif button == "wheeldown" and event == "button" then self.key:triggerVirtual("MOVE_DOWN")
-			end
-		end },
 	}
 end
 
 function _M:updateStore()
 	self:generateList()
-	self.list = #self.store_list > 0 and self.store_list or self.actor_list
-	self.sel = util.bound(self.sel, 1, #self.list)
-	self.scroll = util.scroll(self.sel, self.scroll, self.max)
-	self.changed = true
 end
 
-function _M:use()
-	if self.list[self.sel] then
-		if self.list == self.store_list then
-			self.action("buy", self.list[self.sel].object, self.list[self.sel].item)
+function _M:select(item)
+	if item then
+		self.uis[3].ui = item.zone
+	end
+end
+
+function _M:use(item)
+	if item and item.object then
+		if self.focus_ui and self.focus_ui.ui == self.c_store then
+			self.action("buy", item.object, item.item)
 			self:updateStore()
 		else
-			self.action("sell", self.list[self.sel].object, self.list[self.sel].item)
+			self.action("sell", item.object, item.item)
 			self:updateStore()
 		end
 	end
@@ -98,12 +103,16 @@ end
 function _M:generateList()
 	-- Makes up the list
 	local list = {}
+	list.chars = {}
 	local i = 0
 	self.max_h = 0
 	for item, o in ipairs(self.store_inven) do
 		if not self.store_filter or self.store_filter(o) then
-			list[#list+1] = { name=string.char(string.byte('a') + i)..") "..o:getDisplayString()..o:getName(), color=o:getDisplayColor(), object=o, item=item }
+			local char = string.char(string.byte('a') + i)
+			local zone = Textzone.new{width=self.iw, height=self.ih, text=o:getDesc()}
+			list[#list+1] = { zone=zone, id=#list+1, char=char, name=o:getDisplayString()..o:getName(), color=o:getDisplayColor(), object=o, item=item, cat=o.subtype, cost=o.cost }
 			self.max_h = math.max(self.max_h, #o:getDesc():splitLines(self.iw - 10, self.font))
+			list.chars[char] = #list
 			i = i + 1
 		end
 	end
@@ -111,35 +120,24 @@ function _M:generateList()
 
 	-- Makes up the list
 	local list = {}
+	list.chars = {}
 	local i = 0
 	for item, o in ipairs(self.actor_inven) do
 		if not self.actor_filter or self.actor_filter(o) then
-			list[#list+1] = { name=string.char(string.byte('a') + i)..") "..o:getDisplayString()..o:getName(), color=o:getDisplayColor(), object=o, item=item }
+			local char = string.char(string.byte('a') + i)
+			local zone = Textzone.new{width=self.iw, height=self.ih, text=o:getDesc()}
+			list[#list+1] = { zone=zone, id=#list+1, char=char, name=o:getDisplayString()..o:getName(), color=o:getDisplayColor(), object=o, item=item, cat=o.subtype, cost=o.cost }
 			self.max_h = math.max(self.max_h, #o:getDesc():splitLines(self.iw - 10, self.font))
+			list.chars[char] = #list
 			i = i + 1
 		end
 	end
 	self.actor_list = list
-	self.max = math.floor((self.ih - 5) / self.font_h) - self.max_h
-end
-
-function _M:drawDialog(s)
-	if self.list[self.sel] then
-		lines = self.desc(self.list == self.store_list and "buy" or "sell", self.list[self.sel].object):splitLines(self.iw - 10, self.font)
-	else
-		lines = {}
-	end
 
-	local sh = self.ih - 4 - self.max_h * self.font:lineSkip()
-	h = sh
-	self:drawWBorder(s, 3, sh, self.iw - 6)
-	for i = 1, #lines do
-		s:drawColorStringBlended(self.font, lines[i], 5, 2 + h)
-		h = h + self.font:lineSkip()
+	if self.c_inven then
+		self.c_inven.list = self.actor_list
+		self.c_store.list = self.store_list
+		self.c_inven:generate()
+		self.c_store:generate()
 	end
-
-	self:drawSelectionList(s, 2, 5, self.font_h, self.store_list, self.list == self.store_list and self.sel or -1, "name", self.scroll, self.max, nil, nil, nil, self.iw / 2 - 5, true)
-	self:drawHBorder(s, self.iw / 2, 2, sh - 4)
-	self:drawSelectionList(s, self.iw / 2 + 5, 5, self.font_h, self.actor_list, self.list == self.actor_list and self.sel or -1, "name", self.scroll, self.max, nil, nil, nil, self.iw / 2 - 5, true)
-	self.changed = false
 end
diff --git a/game/engines/default/engine/dialogs/ShowText.lua b/game/engines/default/engine/dialogs/ShowText.lua
index 5d4465fa44..b4e5056dc8 100644
--- a/game/engines/default/engine/dialogs/ShowText.lua
+++ b/game/engines/default/engine/dialogs/ShowText.lua
@@ -18,35 +18,29 @@
 -- darkgod@te4.org
 
 require "engine.class"
-require "engine.Dialog"
+local Dialog = require "engine.ui.Dialog"
+local Textzone = require "engine.ui.Textzone"
 
-module(..., package.seeall, class.inherit(engine.Dialog))
+module(..., package.seeall, class.inherit(Dialog))
 
 function _M:init(title, file, replace, w, h)
+	local rw, rh = w, h
 	w = math.floor(w or game.w * 0.6)
 	h = math.floor(h or game.h * 0.8)
 
-	self.iw = w - 2 * 5 -- Cheat, this is normaly done by Dialog:init but we need it to generate the list and we needto generate it before init
-	self.font = core.display.newFont("/data/font/Vera.ttf", 12)
+	Dialog.init(self, title or "Text", w, h)
+
 	self:generateList(file, replace)
-	h = math.min(4 + 30 + (#self.list) * self.font:lineSkip(), h)
 
-	engine.Dialog.init(self, title or "Text", w, h, nil, nil, nil, self.font)
+	self.c_desc = Textzone.new{width=math.floor(self.iw - 10), height=self.ih, no_color_bleed=true, auto_height=true, text=self.text}
 
-	self.sel = 1
-	self.max = math.floor((self.ih - 5) / self.font_h) - 1
+	self:loadUI{
+		{left=0, top=0, ui=self.c_desc},
+	}
+	self:setupUI(not rw, not rh)
 
-	self:keyCommands({
-	},{
---		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) self.changed = true end,
---		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) self.changed = true end,
-		ACCEPT = function() game:unregisterDialog(self) end,
-		EXIT = "ACCEPT",
-	})
-	self:mouseZones{
-		{ x=0, y=0, w=game.w, h=game.h, mode={button=true}, norestrict=true, fct=function(button) if button ~= "none" then game:unregisterDialog(self) end end},
-		{ x=0, y=0, w=self.w, h=self.h, fct=function(button, x, y, xrel, yrel, tx, ty)
-		end },
+	self.key:addBinds{
+		EXIT = function() game:unregisterDialog(self) end,
 	}
 end
 
@@ -61,15 +55,6 @@ function _M:generateList(file, replace)
 		return util.getval(replace[what])
 	end)
 
-	self.list = str:splitLines(self.iw - 10, self.font)
+	self.text = str
 	return true
 end
-
-function _M:drawDialog(s)
-	for ii = self.sel, #self.list do
-		local i = ii - self.sel + 1
-		if not self.list[i] or 4 + (i) * self.font:lineSkip() >= self.ih then break end
-		s:drawColorStringBlended(self.font, self.list[i], 5, 4 + (i-1) * self.font:lineSkip())
-	end
-	self.changed = false
-end
diff --git a/game/engines/default/engine/dialogs/SoundMusic.lua b/game/engines/default/engine/dialogs/SoundMusic.lua
index 0839ccb586..d8e46c090c 100644
--- a/game/engines/default/engine/dialogs/SoundMusic.lua
+++ b/game/engines/default/engine/dialogs/SoundMusic.lua
@@ -59,9 +59,7 @@ function _M:init()
 end
 
 function _M:changeVol(v)
-	print("vv", v, self.list[self.sel].act)
 	if self.list[self.sel].act == "music_volume" then
-		print("v<<v", v)
 		game:volumeMusic(game:volumeMusic() + v)
 		self:generateList()
 	elseif self.list[self.sel].act == "sound_volume" then
diff --git a/game/engines/default/engine/dialogs/UseTalents.lua b/game/engines/default/engine/dialogs/UseTalents.lua
index 8f12d72e67..6f9e0182c8 100644
--- a/game/engines/default/engine/dialogs/UseTalents.lua
+++ b/game/engines/default/engine/dialogs/UseTalents.lua
@@ -18,31 +18,49 @@
 -- darkgod@te4.org
 
 require "engine.class"
-require "engine.Dialog"
+local Dialog = require "engine.ui.Dialog"
+local ListColumns = require "engine.ui.ListColumns"
+local Textzone = require "engine.ui.Textzone"
+local Separator = require "engine.ui.Separator"
 
-module(..., package.seeall, class.inherit(engine.Dialog))
+module(..., package.seeall, class.inherit(Dialog))
 
 function _M:init(actor)
 	self.actor = actor
 	actor.hotkey = actor.hotkey or {}
-	engine.Dialog.init(self, "Use Talents: "..actor.name, game.w / 2, game.h / 2)
+	Dialog.init(self, "Use Talents: "..actor.name, game.w * 0.7, game.h * 0.7)
+
+	self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[
+You can bind a talent to a hotkey be pressing the corresponding hotkey while selecting a talent.
+Check out the keybinding screen in the game menu to bind hotkeys to a key (default is 1-0 plus control or shift).
+]]}
+	self.c_desc = Textzone.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, no_color_bleed=true, text=""}
 
 	self:generateList()
 
-	self.scroll = 1
-	self.max = math.floor((self.ih - 5) / self.font_h) - 1
+	self.c_list = ListColumns.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, sortable=true, scrollbar=true, columns={
+		{name="", width=4, display_prop="char", sort="id"},
+		{name="Talent", width=80, display_prop="name", sort="name"},
+		{name="Status", width=16, display_prop="status", sort="status"},
+	}, list=self.list, fct=function(item) self:use(item) end, select=function(item, sel) self:select(item) end}
+
+	self:loadUI{
+		{left=0, top=0, ui=self.c_list},
+		{right=0, top=self.c_tut.h + 20, ui=self.c_desc},
+		{right=0, top=0, ui=self.c_tut},
+		{hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI()
 
-	self:keyCommands({
+	self.key:addCommands{
 		__TEXTINPUT = function(c)
-			if c:find("^[a-zA-Z]$") then
-				local sel = self.keybind[c]
-				if sel then
-					self.sel = sel
-					self:use()
-				end
+			if self.list and self.list.chars[c] then
+				self:use(self.list[self.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,
@@ -79,41 +97,29 @@ function _M:init(actor)
 		HOTKEY_THIRD_10 = function() self:defineHotkey(34) end,
 		HOTKEY_THIRD_11 = function() self:defineHotkey(35) end,
 		HOTKEY_THIRD_12 = function() self:defineHotkey(36) end,
-
-		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, #self.list) self.scroll = util.scroll(self.sel, self.scroll, self.max) self.changed = true end,
-		ACCEPT = function() self:use() end,
 		EXIT = function() game:unregisterDialog(self) end,
-	})
-	self:mouseZones{
-		{ x=0, y=0, w=game.w, h=game.h, mode={button=true}, norestrict=true, fct=function(button) if button == "left" then game:unregisterDialog(self) end end},
-		{ x=2, y=5, w=350, h=self.font_h*self.max, fct=function(button, x, y, xrel, yrel, tx, ty, event)
-			self.changed = true
-			if button ~= "wheelup" and button ~= "wheeldown" then
-				self.sel = util.bound(self.scroll + math.floor(ty / self.font_h), 1, #self.list)
-			end
-			self.changed = true
-
-			if button == "left" and event == "button" then self:use()
-			elseif button == "right" and event == "button" then
-			elseif button == "wheelup" and event == "button" then self.key:triggerVirtual("MOVE_UP")
-			elseif button == "wheeldown" and event == "button" then self.key:triggerVirtual("MOVE_DOWN")
-			end
-		end },
 	}
 end
 
 function _M:defineHotkey(id)
 	if not self.actor.hotkey then return end
+	local item = self.list[self.sel]
+	if not item or not item.talent then return end
 
-	self.actor.hotkey[id] = {"talent", self.list[self.sel].talent}
-	self:simplePopup("Hotkey "..id.." assigned", self.actor:getTalentFromId(self.list[self.sel].talent).name:capitalize().." assigned to hotkey "..id)
+	self.actor.hotkey[id] = {"talent", item.talent}
+	self:simplePopup("Hotkey "..id.." assigned", self.actor:getTalentFromId(item.talent).name:capitalize().." assigned to hotkey "..id)
 	self.actor.changed = true
 end
 
-function _M:use()
+function _M:select(item)
+	if item then
+		self.uis[2].ui = item.zone
+	end
+end
+
+function _M:use(item)
 	game:unregisterDialog(self)
-	self.actor:useTalent(self.list[self.sel].talent)
+	self.actor:useTalent(item.talent)
 end
 
 function _M:makeKey(letter)
@@ -138,8 +144,10 @@ function _M:generateList()
 		for j, t in ipairs(tt.talents) do
 			if self.actor:knowTalent(t.id) and t.mode ~= "passive" then
 				local typename = "talent"
-				if t.type[1]:find("^spell/") then typename = "spell" end
-				list[#list+1] = { name=self:makeKey(letter)..")    "..t.name.." ("..typename..")"..(self.actor:isTalentActive(t.id) and " <sustaining>" or ""), talent=t.id }
+				local zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text=self.actor:getTalentFullDescription(t)}
+				local status = "#LIGHT_GREEN#Active#WHITE#"
+				if t.mode == "sustained" then status = self.actor:isTalentActive(t.id) and "#YELLOW#Sustaining#WHITE#" or "#LIGHT_GREEN#Sustain#WHITE#" end
+				list[#list+1] = { zone=zone, char=self:makeKey(letter), name=t.name.." ("..typename..")", status=status, talent=t.id }
 				keybind[self:makeKey(letter)] = #list + 1
 				if not self.sel then self.sel = #list + 1 end
 				letter = letter + 1
@@ -148,52 +156,11 @@ function _M:generateList()
 		end
 
 		if added then
-			table.insert(list, where+1, { name=cat:capitalize().." / "..tt.name:capitalize(), type=tt.type, color={0x80, 0x80, 0x80} })
+			local zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text=tt.description}
+			table.insert(list, where+1, { zone=zone, char="", name=cat:capitalize().." / "..tt.name:capitalize(), type=tt.type, color={0x80, 0x80, 0x80}, status="" })
 		end
 	end
+	for i = 1, #list do list[i].id = i end
 	self.list = list
 	self.keybind = keybind
 end
-
-function _M:drawDialog(s)
-	-- Description part
-	self:drawHBorder(s, self.iw / 2, 2, self.ih - 4)
-
-	local help
-	if not self.actor.hotkey then
-		help = [[Keyboard: #00FF00#up key/down key#FFFFFF# to select a stat; #00FF00#enter#FFFFFF# to use.
-Mouse: #00FF00#Left click#FFFFFF# to use.
-]]
-	else
-		help = [[Keyboard: #00FF00#up key/down key#FFFFFF# to select a stat; #00FF00#enter#FFFFFF# to use.
-#00FF00#1-0#FFFFFF# to assign a hotkey.
-Mouse: #00FF00#Left click#FFFFFF# to use.
-]]
-	end
-	local talentshelp = help:splitLines(self.iw / 2 - 10, self.font)
-
-	local lines = {}
-	if self.list[self.sel].type then
-		lines = self.actor:getTalentTypeFrom(self.list[self.sel].type).description:splitLines(self.iw / 2 - 10, self.font)
-	else
-		local t = self.actor:getTalentFromId(self.list[self.sel].talent)
-		lines = self.actor:getTalentFullDescription(t):splitLines(self.iw / 2 - 10, self.font)
-	end
-
-	local h = 2
-	for i = 1, #talentshelp do
-		s:drawColorStringBlended(self.font, talentshelp[i], self.iw / 2 + 5, h)
-		h = h + self.font:lineSkip()
-	end
-
-	h = h + self.font:lineSkip()
-	self:drawWBorder(s, self.iw / 2 + self.iw / 6, h - 0.5 * self.font:lineSkip(), self.iw / 6)
-	for i = 1, #lines do
-		s:drawColorStringBlended(self.font, lines[i], self.iw / 2 + 5, 2 + h)
-		h = h + self.font:lineSkip()
-	end
-
-	-- Talents
-	self:drawSelectionList(s, 2, 5, self.font_h, self.list, self.sel, "name", self.scroll, self.max, nil, nil, nil, self.iw / 2 - 5, true)
-	self.changed = false
-end
diff --git a/game/engines/default/engine/ui/Dialog.lua b/game/engines/default/engine/ui/Dialog.lua
index 136e3374e0..254313f08f 100644
--- a/game/engines/default/engine/ui/Dialog.lua
+++ b/game/engines/default/engine/ui/Dialog.lua
@@ -244,7 +244,7 @@ function _M:setupUI(resizex, resizey, on_resize)
 end
 
 function _M:setFocus(id)
-	if self.focus_ui then self.focus_ui.ui:setFocus(false) end
+	if self.focus_ui and self.focus_ui.ui.can_focus then self.focus_ui.ui:setFocus(false) end
 
 	if type(id) == "table" then
 		for i = 1, #self.uis do
@@ -254,6 +254,7 @@ function _M:setFocus(id)
 	end
 
 	local ui = self.uis[id]
+	if not ui.ui.can_focus then return end
 	self.focus_ui = ui
 	self.focus_ui_id = id
 	ui.ui:setFocus(true)
diff --git a/game/engines/default/engine/ui/ListColumns.lua b/game/engines/default/engine/ui/ListColumns.lua
index f4c89199c9..54aa327431 100644
--- a/game/engines/default/engine/ui/ListColumns.lua
+++ b/game/engines/default/engine/ui/ListColumns.lua
@@ -189,7 +189,7 @@ function _M:onSelect()
 	local item = self.list[self.sel]
 	if not item then return end
 
-	if self.select then self.select(item, self.sel) end
+	if rawget(self, "select") then self.select(item, self.sel) end
 end
 
 function _M:onUse()
diff --git a/game/engines/default/engine/ui/Textzone.lua b/game/engines/default/engine/ui/Textzone.lua
index 1f79158278..65dc3f6e76 100644
--- a/game/engines/default/engine/ui/Textzone.lua
+++ b/game/engines/default/engine/ui/Textzone.lua
@@ -30,6 +30,7 @@ function _M:init(t)
 	self.h = assert(t.height, "no list height")
 	self.scrollbar = t.scrollbar
 	self.no_color_bleed = t.no_color_bleed
+	self.auto_height = t.auto_height
 
 	Base.init(self, t)
 end
@@ -41,12 +42,15 @@ function _M:generate()
 	local list = self.text:splitLines(self.w - 10, self.font)
 	self.scroll = 1
 	self.max = #list
-	self.max_display = math.floor(self.h / self.font_h)
-	self.can_focus = self.scrollbar and (self.max_display < self.max)
 
 	local fw, fh = self.w, self.font_h
 	self.fw, self.fh = fw, fh
 
+	if self.auto_height then self.h = self.fh * #list end
+
+	self.max_display = math.floor(self.h / self.fh)
+	self.can_focus = self.scrollbar and (self.max_display < self.max)
+
 	-- Draw the list items
 	self.list = {}
 	local r, g, b = 255, 255, 255
diff --git a/game/engines/default/engine/ui/VariableList.lua b/game/engines/default/engine/ui/VariableList.lua
new file mode 100644
index 0000000000..5d1b6795a9
--- /dev/null
+++ b/game/engines/default/engine/ui/VariableList.lua
@@ -0,0 +1,122 @@
+-- TE4 - T-Engine 4
+-- Copyright (C) 2009, 2010 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"
+
+--- A generic UI list
+module(..., package.seeall, class.inherit(Base, Focusable))
+
+function _M:init(t)
+	self.list = assert(t.list, "no list list")
+	self.w = assert(t.width, "no list width")
+	self.fct = t.fct
+	self.display_prop = t.display_prop or "name"
+
+	Base.init(self, t)
+end
+
+function _M:generate()
+	self.mouse:reset()
+	self.key:reset()
+
+	self.sel = 1
+	self.max = #self.list
+
+	local vs7, vs7_w, vs7_h = self:getImage("ui/varsel-7-sel.png")
+	local vs9, vs9_w, vs9_h = self:getImage("ui/varsel-9-sel.png")
+	local vs3, vs3_w, vs3_h = self:getImage("ui/varsel-3-sel.png")
+	local vs1, vs1_w, vs1_h = self:getImage("ui/varsel-1-sel.png")
+	local vs2, vs2_w, vs2_h = self:getImage("ui/varsel-2-sel.png")
+	local vs8, vs8_w, vs8_h = self:getImage("ui/varsel-8-sel.png")
+	local vs4, vs4_w, vs4_h = self:getImage("ui/varsel-4-sel.png")
+	local vs6, vs6_w, vs6_h = self:getImage("ui/varsel-6-sel.png")
+	local vsc, vsc_w, vsc_h = self:getImage("ui/varsel-repeat-sel.png")
+
+	local fw, fh = self.w, self.font_h
+	self.fw, self.fh = fw, fh
+
+	-- Draw the list items
+	self.h = 0
+	for i, item in ipairs(self.list) do
+		local color = item.color or {255,255,255}
+		local text = item[self.display_prop]:splitLines(fw - vs7_w - vs9_w, self.font)
+		local fh = fh * #text + vs7_h / 3 * 2
+		local ss = core.display.newSurface(fw, fh)
+		local sus = core.display.newSurface(fw, fh)
+		local s = core.display.newSurface(fw, fh)
+
+		for i = 0, fw, vsc_w do for j = 0, fh, vsc_h do ss:merge(vsc, i, j) end end
+		for i = 0, fw, vs8_w do ss:merge(vs8, i, 0) ss:merge(vs2, i, fh - vs2_h) end
+		for j = 0, fh, vs4_h do ss:merge(vs4, 0, j) ss:merge(vs6, fw - vs6_w, j) end
+		ss:merge(vs7, 0, 0)
+		ss:merge(vs9, fw - vs9_w, 0)
+		ss:merge(vs1, 0, fh - vs1_h)
+		ss:merge(vs3, fw - vs3_w, fh - vs3_h)
+
+		s:erase(0, 0, 0)
+
+		local color_r, color_g, color_b = color[1], color[2], color[3]
+		for z = 1, #text do
+			s:drawColorStringBlended(self.font, text[z], vs7_w, vs7_h / 3 + self.font_h * (z-1), color_r, color_g, color_b)
+			color_r, color_g, color_b = ss:drawColorStringBlended(self.font, text[z], vs7_w, vs7_h / 3 + self.font_h * (z-1), color_r, color_g, color_b)
+		end
+
+		item.fh = fh
+		item._tex, item._tex_w, item._tex_h = s:glTexture()
+		item._stex = ss:glTexture()
+		self.h = self.h + fh
+	end
+
+	-- Add UI controls
+	self.mouse:registerZone(0, 0, self.w, self.h, function(button, x, y, xrel, yrel, bx, by, event)
+--		self.sel = util.bound(self.scroll + math.floor(by / self.fh), 1, self.max)
+		if button == "left" and event == "button" then self:onUse() end
+	end)
+	self.key:addBinds{
+		ACCEPT = function() self:onUse() end,
+		MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, self.max) end,
+		MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, self.max) end,
+	}
+end
+
+function _M:onUse()
+	local item = self.list[self.sel]
+	if not item then return end
+	if item.fct then item:fct()
+	else self.fct(item, self.sel) end
+end
+
+function _M:display(x, y)
+	for i = 1, self.max do
+		local item = self.list[i]
+		if not item then break end
+		if self.sel == i then
+			if self.focused then
+				item._stex:toScreenFull(x, y, self.fw, item.fh, item._tex_w, item._tex_h)
+			else
+				item._tex:toScreenFull(x, y, self.fw, item.fh, item._tex_w, item._tex_h)
+			end
+		else
+			item._tex:toScreenFull(x, y, self.fw, item.fh, item._tex_w, item._tex_h)
+		end
+		y = y + item.fh
+	end
+end
diff --git a/game/modules/tome/data/birth/races/hobbit.lua b/game/modules/tome/data/birth/races/hobbit.lua
index 332c7df60a..788c71dd67 100644
--- a/game/modules/tome/data/birth/races/hobbit.lua
+++ b/game/modules/tome/data/birth/races/hobbit.lua
@@ -64,7 +64,7 @@ newBirthDescriptor
 		"Hobbits are agile, lucky, and resilient but lacking in strength.",
 		"They possess the #GOLD#Luck of the Little Folk#WHITE# which allows them to increase their critical strike chance for a few turns.",
 		"#GOLD#Stats modifiers:",
-		"#LIGHT_BLUE# * -3 Strength, +3 Dexterity, +10 Constitution",
+		"#LIGHT_BLUE# * -3 Strength, +3 Dexterity, +1 Constitution",
 		"#LIGHT_BLUE# * +0 Magic, +0 Willpower, +3 Cunning",
 		"#LIGHT_BLUE# * +5 Luck",
 		"#GOLD#Life per levels:#LIGHT_BLUE# 12",
diff --git a/game/modules/tome/data/talents/techniques/bloodthirst.lua b/game/modules/tome/data/talents/techniques/bloodthirst.lua
index 476350ae7f..758b0edc1e 100644
--- a/game/modules/tome/data/talents/techniques/bloodthirst.lua
+++ b/game/modules/tome/data/talents/techniques/bloodthirst.lua
@@ -94,7 +94,7 @@ newTalent{
 	end,
 	info = function(self, t)
 		return ([[You enter a battle frenzy for %d turns. During the time you can not use items, healing has no effect and your health can not drop below 1.
-		At the end of the frenzy you regain %d%% of your health per foes slain during thr frenzy.]]):
+		At the end of the frenzy you regain %d%% of your health per foes slain during the frenzy.]]):
 		format(2 + self:getTalentLevelRaw(t), math.floor(self:getTalentLevel(t) * 3.5))
 	end,
 }
-- 
GitLab