diff --git a/game/data/gfx/cursor.png b/game/data/gfx/cursor.png
new file mode 100644
index 0000000000000000000000000000000000000000..4eb962c0372553077a9be3827c27d1a356743d75
Binary files /dev/null and b/game/data/gfx/cursor.png differ
diff --git a/game/engine/Birther.lua b/game/engine/Birther.lua
index 0bbfe67a9be27d1f41aa371a0a6cbced832aca80..5d40f06b6fb6cbc6ebc5fa2dbfd21286dae479ad 100644
--- a/game/engine/Birther.lua
+++ b/game/engine/Birther.lua
@@ -254,17 +254,17 @@ function _M:drawDialog(s)
 Mouse: #00FF00#Left click#FFFFFF# to accept; #00FF00#right click#FFFFFF# to go back.
 ]]):splitLines(self.iw / 2 - 10, self.font)
 	for i = 1, #birthhelp do
-		s:drawColorString(self.font, birthhelp[i], self.iw / 2 + 5, 2 + (i-1) * self.font:lineSkip())
+		s:drawColorStringBlended(self.font, birthhelp[i], self.iw / 2 + 5, 2 + (i-1) * self.font:lineSkip())
 	end
 
 	local lines = self.list[self.sel].desc:splitLines(self.iw / 2 - 10, self.font)
 	local r, g, b
 	for i = 1, #lines do
-		r, g, b = s:drawColorString(self.font, lines[i], self.iw / 2 + 5, 2 + (i + #birthhelp + 1) * self.font:lineSkip(), r, g, b)
+		r, g, b = s:drawColorStringBlended(self.font, lines[i], self.iw / 2 + 5, 2 + (i + #birthhelp + 1) * self.font:lineSkip(), r, g, b)
 	end
 
 	-- Stats
-	s:drawColorString(self.font, "Selecting: "..self.current_type:capitalize(), 2, 2)
+	s:drawColorStringBlended(self.font, "Selecting: "..self.current_type:capitalize(), 2, 2)
 	self:drawWBorder(s, 2, 20, 200)
 
 	self:drawSelectionList(s, 2, 25, self.font_h, self.list, self.sel, "name")
diff --git a/game/engine/Button.lua b/game/engine/Button.lua
new file mode 100644
index 0000000000000000000000000000000000000000..09c034c639c74787477204ef3750191653dc88b8
--- /dev/null
+++ b/game/engine/Button.lua
@@ -0,0 +1,57 @@
+-- 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"
+require "engine.Tiles"
+require "engine.Mouse"
+require "engine.KeyBind"
+local ButtonList = require "engine.ButtonList"
+
+--- Handles dialog windows
+module(..., package.seeall, class.make)
+
+function _M:init(name, text, x, y, w, height, owner, font, fct)	
+	self.type = "Button"
+	self.owner = owner
+	self.name = name	
+	self.fct = fct
+	self.font = font	
+	self.x = x
+	self.y = y
+	self.btn = 	{ 
+		susel = ButtonList:makeButton(text, self.font, w, height, false),
+		sel = ButtonList:makeButton(text, self.font, 50, height, true),
+		h = height,
+		mouse_over= function(button)							
+						if self.owner.state ~= self.name then self.owner:focusControl(self.name) end						
+						if button == "left" then
+							self:fct()
+						end
+					end
+					}
+	self.owner.mouse:registerZone(owner.display_x + self.x, owner.display_y + self.y + height, w, height, self.btn.mouse_over)	
+end
+
+function _M:drawControl(s)	
+	if (self.owner.state == self.name) then
+		s:merge(self.btn.sel, self.x, self.y)		
+	else
+		s:merge(self.btn.susel, self.x, self.y)				
+	end	
+end
\ No newline at end of file
diff --git a/game/engine/ButtonList.lua b/game/engine/ButtonList.lua
index 0ceeb9d7d152bce2db43ae8225653f65e849a074..dc59b722d21da8f8df63eb1bac222d5d11241211 100644
--- a/game/engine/ButtonList.lua
+++ b/game/engine/ButtonList.lua
@@ -156,7 +156,7 @@ function _M:makeButton(name, font, w, h, sel)
 	end
 
 	local sw, sh = font:size(name)
-	s:drawColorString(font, name, (w - sw) / 2, (h - sh) / 2)
+	s:drawColorStringBlended(font, name, (w - sw) / 2, (h - sh) / 2)
 
 	return s
 end
diff --git a/game/engine/DebugConsole.lua b/game/engine/DebugConsole.lua
index 8c092de9b0d12e2ba5c1f15761f9ad70032c4037..7ae661a70f0a2d4063f709285912c07379d4b490 100644
--- a/game/engine/DebugConsole.lua
+++ b/game/engine/DebugConsole.lua
@@ -79,10 +79,10 @@ function _M:drawDialog(s, w, h)
 	local i, dh = 1, 0
 	while dh < self.h do
 		if not self.history[self.scroll + i] then break end
-		s:drawString(self.font, self.history[self.scroll + i], 0, self.ih - (i + 1) * self.font:lineSkip(), 255, 255, 255)
+		s:drawStringBlended(self.font, self.history[self.scroll + i], 0, self.ih - (i + 1) * self.font:lineSkip(), 255, 255, 255)
 		i = i + 1
 		dh = dh + self.font:lineSkip()
 	end
 
-	s:drawString(self.font, self.line, 0, self.ih - self.font:lineSkip(), 255, 255, 255)
+	s:drawStringBlended(self.font, self.line, 0, self.ih - self.font:lineSkip(), 255, 255, 255)
 end
diff --git a/game/engine/Dialog.lua b/game/engine/Dialog.lua
index f421082c375aa5e77c8c10656e85c3b8628600a6..9d11b14ec7db54cbbc005e1ab068bf691ad1cbe4 100644
--- a/game/engine/Dialog.lua
+++ b/game/engine/Dialog.lua
@@ -82,6 +82,10 @@ end
 --- Create a Dialog
 function _M:init(title, w, h, x, y, alpha, font)
 	self.title = title
+	self.controls = { }
+	self.tabindex = 0
+	self.state = ""
+	self.currenttabindex = 0
 	self.w, self.h = math.floor(w), math.floor(h)
 	self.display_x = math.floor(x or (game.w - self.w) / 2)
 	self.display_y = math.floor(y or (game.h - self.h) / 2)
@@ -100,7 +104,8 @@ function _M:display()
 	if not self.changed then return self.surface end
 
 	local s = self.surface
-	s:erase(0,0,0,200)
+	self.alpha = self.alpha or 200
+	s:erase(0,0,0,self.alpha)
 
 	s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_7.png"), 0, 0)
 	s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_9.png"), self.w - 8, 0)
@@ -118,7 +123,7 @@ function _M:display()
 
 	local tw, th = self.font:size(self.title)
 	s:drawColorStringBlended(self.font, self.title, (self.w - tw) / 2, 4, 255,255,255)
-
+	
 	self.internal_surface:erase()
 	self:drawDialog(self.internal_surface)
 	s:merge(self.internal_surface, 5, 20 + 3)
@@ -126,6 +131,57 @@ function _M:display()
 	return self.surface
 end
 
+function _M:addControl(control)			
+	control.tabindex = self.tabindex
+	self.tabindex = self.tabindex + 1	
+	self.controls[control.name] = control	
+	table.sort(self.controls, function(a,b) return a.tabindex<b.tabindex end)
+end
+
+function _M:changeFocus(up)
+    local add = 1
+	if not up then add = -1 end
+	self.currenttabindex = self.currenttabindex + add
+	if (self.currenttabindex==self.tabindex) then self.currenttabindex = 0 end	
+	if self.currenttabindex==-1 then self.currenttabindex=self.tabindex-1 end
+	local name = ""
+	for i, cntrl in pairs(self.controls) do
+		if cntrl.tabindex==self.currenttabindex then 
+			if self.controls[self.state] and self.controls[self.state].unFocus then self.controls[self.state]:unFocus() end
+			cntrl.focused=true 
+			name=i 
+		end
+	end	
+	return name
+end
+
+function _M:focusControl(focusOn)
+	if focusOn==self.state then return end
+	local oldstate = self.state
+	for i, cntrl in pairs(self.controls) do
+		if i==focusOn then cntrl.focused=true self.state=i self.currenttabindex=cntrl.tabindex end
+		if i==oldstate and cntrl.unFocus then cntrl:unFocus() end
+	end		
+end
+
+
+function _M:databind()
+	local result = { } 
+	for i, cntrl in pairs(self.controls or { }) do		
+	    if cntrl.type and cntrl.type=="TextBox" then
+			result[cntrl.name] = cntrl.text
+		end
+	end		
+	return result
+end
+
+
+function _M:drawControls(s)	
+	for i, cntrl in pairs(self.controls or { }) do		
+	    cntrl:drawControl(s)
+	end	
+end
+
 function _M:toScreen(x,y)
 	self.surface:toScreenWithTexture(self.texture,x,y)
 end
diff --git a/game/engine/PlayerProfile.lua b/game/engine/PlayerProfile.lua
index 910343b8242407d41e9221c80f8e2174a9a0d7dc..35091d88f740a4062560591753052faef459deb6 100644
--- a/game/engine/PlayerProfile.lua
+++ b/game/engine/PlayerProfile.lua
@@ -66,7 +66,7 @@ end
 ------------------------------------------------------------
 
 
---- Handles the player profile, possible online
+--- Handles the player profile, possibly online
 module(..., package.seeall, class.make)
 
 function _M:init(name)
@@ -96,8 +96,8 @@ end
 function _M:loadGenericProfile()
 	local d = "/profiles/"..self.name.."/generic/"
 	fs.mount(engine.homepath, "/")
-
 	for i, file in ipairs(fs.list(d)) do
+
 		if file:find(".profile$") then
 			local f, err = loadfile(d..file)
 			if not f and err then error(err) end
@@ -115,9 +115,8 @@ end
 function _M:loadModuleProfile(short_name)
 	local d = "/profiles/"..self.name.."/modules/"..short_name.."/"
 	fs.mount(engine.homepath, "/")
-
+	print("[Module Profile] ", engine.homepath.."/"..d)
 	self.modules[short_name] = self.modules[short_name] or {}
-
 	for i, file in ipairs(fs.list(d)) do
 		if file:find(".profile$") then
 			local f, err = loadfile(d..file)
@@ -195,6 +194,7 @@ function _M:rpc(data)
 end
 
 function _M:tryAuth()
+	print("[ONLINE PROFILE] auth")
 	local data = self:rpc{action="TryAuth", login=self.login, pass=self.pass}
 	if not data then print("[ONLINE PROFILE] not auth") return end
 	print("[ONLINE PROFILE] auth as ", data.name, data.hash)
@@ -232,7 +232,6 @@ function _M:setConfigs(module, name, val)
 	local data = self:rpc{action="SetConfigs", login=self.login, hash=self.auth.hash, module=module, data={[name] = val}}
 	if not data then return end
 	print("[ONLINE PROFILE] saved ", module, name, val)
-	util.show_backtrace()
 end
 
 function _M:syncOnline(module)
@@ -249,9 +248,40 @@ function _M:syncOnline(module)
 	print("[ONLINE PROFILE] saved ", module)
 end
 
-function _M:newProfile()
-	local data = self:rpc{action="NewProfile", login=tostring(os.time()), email=os.time().."te4@mailcatch.com", name=os.time().."member", pass="test"}
+function _M:newProfile(Login, Name, Password, Email)
+	print("[ONLINE PROFILE] profile options ", Login, Email, Name, Password)
+	local data = self:rpc{action="NewProfile", login=Login, email=Email, name=Name, pass=Password}
 	if not data then print("[ONLINE PROFILE] could not create") return end
 	print("[ONLINE PROFILE] profile id ", data.id)
+	self:performlogin(Login, Password, Name, Email)
+end
+
+function _M:performlogin(login, pass, name, email)
+	self.login=login
+	self.pass=pass
+	print("[ONLINE PROFILE] attempting log in ", self.login, self.pass)
+	self:tryAuth()
+	if (profile.auth) then
+		self.generic.online = { login=login, pass=pass,name=name or "", email=email or "" }
+		self:saveGenericProfile("online", self.generic.online)
+		self:getConfigs("generic")
+		self:syncOnline("generic")
+	end
 end
 
+function _M:logOut()
+	profile.generic.online = nil
+	profile.auth = nil
+	local d = "/profiles/"..self.name.."/generic/"
+	fs.mount(engine.homepath, "/")
+	fs.delete(d.."online.profile")
+end
+
+function _M:checkFirstRun()
+	local result = self.generic.firstrun
+	if not result then
+		firstrun = { When=os.time() }
+		self:saveGenericProfile("firstrun", firstrun, false)
+	end
+	return result
+end
\ No newline at end of file
diff --git a/game/engine/TextBox.lua b/game/engine/TextBox.lua
new file mode 100644
index 0000000000000000000000000000000000000000..a4551e8bdcc741848a73e01a66ba948074468b0e
--- /dev/null
+++ b/game/engine/TextBox.lua
@@ -0,0 +1,128 @@
+-- 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"
+require "engine.Tiles"
+require "engine.Mouse"
+require "engine.KeyBind"
+require "engine.interface.ControlCursorSupport"
+--- Handles textbox input control
+module(..., package.seeall, class.inherit(
+		engine.interface.ControlCursorSupport))
+
+tiles = engine.Tiles.new(16, 16)
+
+function _M:init(dialogdef, owner, font, mask, fct)	        
+	--name, title, min, max, x, y, w, height	
+	self.type = "TextBox"
+	self.name = dialogdef.name
+	self.title = dialogdef.title
+	self.min = dialogdef.min or 2
+	self.max = dialogdef.max or 25
+	self.h = dialogdef.h or 30
+	self.font = font
+	self.w = dialogdef.w or 200	
+	self.x = dialogdef.x
+	self.y = dialogdef.y
+	self.text = ""
+	self.owner = owner	
+	self.btn = 	{ 		
+		h = dialogdef.h,
+		mouse_over= function(button)							
+						if self.owner.state ~= self.name then self.focused=true self.owner:focusControl(self.name) end						
+						if button == "right" then
+							self.text=""							
+							self.ownwer.changed=true
+						end
+					end
+				}
+	self.owner.mouse:registerZone(self.owner.display_x + self.x, self.owner.display_y + self.y + self.h, self.w, self.h, self.btn.mouse_over)		
+	self:startCursor()	
+end
+
+function _M:delete()
+	if (self.cursorPosition>=self.maximumCurosrPosition) then return end
+	local temptext = self.text:sub(1, self.cursorPosition)
+	if self.cursorPosition < self.maximumCurosrPosition - 1 then temptext = temptext..self.text:sub(self.cursorPosition + 2, self.text:len()) end
+	self.text =  temptext
+	self.maximumCurosrPosition = self.maximumCurosrPosition - 1
+end	
+
+function _M:backSpace()
+	if (self.cursorPosition==0) then return end
+	local temptext = self.text:sub(1, self.cursorPosition - 1)	
+	if self.cursorPosition < self.maximumCurosrPosition then temptext = temptext..self.text:sub(self.cursorPosition + 1, self.text:len()) end
+	self.text =  temptext
+	self.maximumCurosrPosition = self.maximumCurosrPosition - 1
+	self.cursorPosition = self.cursorPosition - 1
+end
+
+function _M:textInput(c)
+	if self.text:len() < self.max then
+		local temp=nil
+		if self.cursorPosition < self.maximumCurosrPosition then temp=self.text:sub(self.cursorPosition + 1, self.text:len()) end
+		self.text = self.text:sub(1,self.cursorPosition) .. c
+		if temp then self.text=self.text..temp end
+		self.owner.changed = true
+		print("[Cursor] before ", c, self.cursorPosition, self.maximumCurosrPosition)
+		self:moveRight(1, true)		
+		print("[Cursor] after ", c, self.cursorPosition, self.maximumCurosrPosition)
+	end
+end
+
+function _M:unFocus()
+	self.focused = false
+end
+
+
+function _M:drawControl(s)    
+	
+	local r, g, b
+	local w = self.w
+	local h = self.h
+	local tw, th = self.font:size(self.title)
+	tw = tw + 10
+	local title=self.title
+	if self.owner.state==self.name then
+		title = title.."*"
+	end
+	r, g, b = s:drawColorStringBlended(self.font, title, self.x, self.y + ((h - th) / 2), r, g, b)	
+		
+	s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_7"..(sel and "_sel" or "")..".png"), self.x + tw, self.y)
+	s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_9"..(sel and "_sel" or "")..".png"), w + self.x - 8, self.y)
+	s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_1"..(sel and "_sel" or "")..".png"), self.x + tw, self.y + h - 8)
+	s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_3"..(sel and "_sel" or "")..".png"), self.x + w - 8, self.y + h - 8)
+	for i = 8, w - tw - 9 do
+		s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_8.png"), self.x + tw + i, self.y)
+		s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_8.png"), self.x + tw + i, self.y + h - 3)
+	end
+	for i = 8, h - 9 do
+		s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_4.png"), self.x + tw, self.y + i)
+		s:merge(tiles:get(nil, 0,0,0, 0,0,0, "border_4.png"), self.x + w - 3, self.y + i)
+	end
+
+	local text = self.text
+	if text=="" then text=self.mask or "" end
+	local sw, sh = self.font:size(text)
+	
+	local baseX = self.x + tw + 10
+	
+	s:drawColorStringBlended(self.font, text, baseX, self.y + h - sh - 8)
+	self:drawCursor(s, baseX, text)
+end
diff --git a/game/engine/dialogs/DownloadDialog.lua b/game/engine/dialogs/DownloadDialog.lua
index f4eb9b7ec2304ac97500d6f82f0085e3fe49f34c..1dd07951ee4ef2571161c8e346b59d4b21652d90 100644
--- a/game/engine/dialogs/DownloadDialog.lua
+++ b/game/engine/dialogs/DownloadDialog.lua
@@ -56,8 +56,8 @@ function _M:drawDialog(s)
 		end
 	end
 
-	s:drawString(self.font, "From: "..self.url, 2, 2, 255, 255, 255)
-	s:drawString(self.font, "Received: "..self.received, 2, 25, 255, 255, 255)
+	s:drawStringBlended(self.font, "From: "..self.url, 2, 2, 255, 255, 255)
+	s:drawStringBlended(self.font, "Received: "..self.received, 2, 25, 255, 255, 255)
 end
 
 function _M:startDownload()
diff --git a/game/engine/interface/ControlCursorSupport.lua b/game/engine/interface/ControlCursorSupport.lua
new file mode 100644
index 0000000000000000000000000000000000000000..e913e8f38aa5395478d5e6eb6cc3c7c6a21295ee
--- /dev/null
+++ b/game/engine/interface/ControlCursorSupport.lua
@@ -0,0 +1,58 @@
+-- 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"
+require "engine.Tiles"
+
+local Dialog = require "engine.Dialog"
+
+tiles = engine.Tiles.new(16, 16)
+
+--- Handles player running
+-- This should work for running inside tunnel, alongside walls, in open spaces.<br/>
+module(..., package.seeall, class.make)
+
+function _M:startCursor()	
+	self.cursorPosition = 0
+	self.maximumCurosrPosition = 0
+	self.focused = false
+end
+
+function _M:moveRight(x, add)	
+	if add and self.cursorPosition + x > self.maximumCurosrPosition then self.maximumCurosrPosition = self.cursorPosition + x end
+	if self.cursorPosition + x <= self.maximumCurosrPosition then
+		self.cursorPosition = self.cursorPosition + x
+	end
+end
+
+function _M:moveLeft(x)
+	if self.cursorPosition - x >= 0 then
+		self.cursorPosition = self.cursorPosition - x
+	end
+end
+
+-- @param s surface to draw on
+function _M:drawCursor(s, baseX, text)
+	local sw, sh = self.font:size(text:sub(1, self.cursorPosition))	
+	local t = os.time() % 2	
+	if t < 1 and self.focused then
+		s:merge(tiles:get(nil, 0,0,0, 0,0,0, "cursor.png"), sw + baseX, self.y + self.h - sh - 2)
+		s:merge(tiles:get(nil, 0,0,0, 0,0,0, "cursor.png"), sw + baseX, self.y + self.h - sh - 10)
+	end
+end
\ No newline at end of file
diff --git a/game/engine/interface/WorldAchievements.lua b/game/engine/interface/WorldAchievements.lua
index c489d85b40c85c682f38b6c14411d9dd9648d458..4e8933528f6e196bb0058f7dd0ecc76eb83b9fcf 100644
--- a/game/engine/interface/WorldAchievements.lua
+++ b/game/engine/interface/WorldAchievements.lua
@@ -74,7 +74,7 @@ end
 
 
 --- Gain Personal achievement for player only
--- @
+-- @param silent suppress the message to the player
 -- @param id the achivement to gain
 -- @param src who did it
 function _M:gainPersonalAchievement(silent, id, src, ...)
@@ -98,7 +98,7 @@ function _M:gainAchievement(id, src, ...)
 	local a = self.achiev_defs[id]
 	if not a then error("Unknown achievement "..id) return end
 
-	if self.achieved[id] and self.playerachieved[id] then return end
+	if self.achieved[id] and src.achievements and src.achievements[id] then return end
 
 	if a.can_gain then
 		local data = nil
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 40daac61afb94b6782021919847b2a2c9ef0bd06..d36058be7ff56ef0e8781aa5e549bf90ba6fa79d 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -572,6 +572,7 @@ function _M:die(src)
 			for inven_id, inven in pairs(self.inven) do
 				for i, o in ipairs(inven) do
 					if not o.no_drop then
+						o.droppedBy = self.name
 						game.level.map:addObject(self.x, self.y, o)
 					end
 				end
@@ -597,6 +598,10 @@ function _M:die(src)
 		world:gainAchievement("EXTERMINATOR", p, self)
 		world:gainAchievement("PEST_CONTROL", p, self)
 
+		if self.unique then
+			p:registerUniqueKilled(self)
+		end
+
 		-- Record kills
 		p.all_kills = p.all_kills or {}
 		p.all_kills[self.name] = p.all_kills[self.name] or 0
@@ -756,6 +761,9 @@ function _M:onAddObject(o)
 
 	-- Achievement checks
 	if self.player then
+		if o.unique then
+			self:resolveSource():registerArtifactsPicked(o)
+		end
 		world:gainAchievement("DEUS_EX_MACHINA", self, o)
 	end
 end
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index fdec9a77eb77b0470c9fb6d6a960294ddd24a5ac..d337f057a698eeb46a7db9c739bebbd5ba7a9200 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -72,7 +72,6 @@ function _M:init()
 	engine.interface.GameSound.init(self)
 
 	self.persistant_actors = {}
-
 	-- Pause at birth
 	self.paused = true
 
@@ -143,12 +142,14 @@ function _M:newGame()
 		self.player.energy.value = self.energy_to_act
 		self.always_target = true
 		Map:setViewerFaction(self.player.faction)
+
 		self.paused = true
 		print("[PLAYER BIRTH] resolved!")
 		self.player:playerLevelup(function()
 			self.player:grantQuest(self.player.starting_quest)
 			self:registerDialog(require("mod.dialogs.IntroDialog").new(self.player))
 			self.player:resetToFull()
+			self.player:registerCharacterPlayed()
 		end)
 	end, quickbirth)
 	self:registerDialog(birth)
diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua
index b294963470b7b05df3bf3889b9f8296a2a0b570b..77a08cadb3728d913496d4b0fd377a5ba44cf5d6 100644
--- a/game/modules/tome/class/Player.lua
+++ b/game/modules/tome/class/Player.lua
@@ -24,6 +24,7 @@ require "engine.interface.PlayerRun"
 require "engine.interface.PlayerHotkeys"
 require "engine.interface.PlayerSlide"
 require "engine.interface.PlayerMouse"
+require "mod.class.interface.PlayerStats"
 local Map = require "engine.Map"
 local Dialog = require "engine.Dialog"
 local ActorTalents = require "engine.interface.ActorTalents"
@@ -40,7 +41,8 @@ module(..., package.seeall, class.inherit(
 	engine.interface.PlayerRun,
 	engine.interface.PlayerHotkeys,
 	engine.interface.PlayerMouse,
-	engine.interface.PlayerSlide
+	engine.interface.PlayerSlide,
+	mod.class.interface.PlayerStats
 ))
 
 function _M:init(t, no_default)
@@ -239,6 +241,8 @@ function _M:die(src)
 		engine.interface.ActorLife.die(self, src)
 		game.paused = true
 		self.energy.value = game.energy_to_act
+		self.killedBy = src
+		self:registerDeath(self.killedBy)
 		game:registerDialog(DeathDialog.new(self))
 	else
 		mod.class.Actor.die(self, src)
diff --git a/game/modules/tome/class/interface/PlayerStats.lua b/game/modules/tome/class/interface/PlayerStats.lua
new file mode 100644
index 0000000000000000000000000000000000000000..0ca9dca32bc3406cc397d135711cb1dc59eb572d
--- /dev/null
+++ b/game/modules/tome/class/interface/PlayerStats.lua
@@ -0,0 +1,68 @@
+-- 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"
+
+module(..., package.seeall, class.make)
+
+function _M:playerStatGetCharacterIdentifier()
+	return self.descriptor.world..","..self.descriptor.subrace..","..self.descriptor.subclass..","..self.descriptor.difficulty
+end
+
+function _M:registerDeath(src)
+	local pid = self:playerStatGetCharacterIdentifier()
+	local name = src.name
+
+	profile.mod.deaths = profile.mod.deaths or {}
+	profile.mod.deaths.count = (profile.mod.deaths.count or 0) + 1
+
+	profile.mod.deaths.sources = profile.mod.deaths.sources or {}
+	profile.mod.deaths.sources[pid] = profile.mod.deaths.sources[pid] or {}
+	profile.mod.deaths.sources[pid][name] = (profile.mod.deaths.sources[pid][name] or 0) + 1
+	profile:saveModuleProfile("deaths", profile.mod.deaths)
+end
+
+function _M:registerUniqueKilled(who)
+	local pid = self:playerStatGetCharacterIdentifier()
+
+	profile.mod.uniques = profile.mod.uniques or { uniques={} }
+	profile.mod.uniques.uniques[who.name] = profile.mod.uniques.uniques[who.name] or {}
+	profile.mod.uniques.uniques[who.name][pid] = (profile.mod.uniques.uniques[who.name][pid] or 0) + 1
+	profile:saveModuleProfile("uniques", profile.mod.uniques)
+end
+
+function _M:registerArtifactsPicked(what)
+	if what.stat_picked_up then return end
+	what.stat_picked_up = true
+	local pid = self:playerStatGetCharacterIdentifier()
+	local name = what:getName{do_color=false, do_count=false, force_id=true}
+
+	profile.mod.artifacts = profile.mod.artifacts or { artifacts={} }
+	profile.mod.artifacts.artifacts[name] = profile.mod.artifacts.artifacts[name] or {}
+	profile.mod.artifacts.artifacts[name][pid] = (profile.mod.artifacts.artifacts[name][pid] or 0) + 1
+	profile:saveModuleProfile("artifacts", profile.mod.artifacts)
+end
+
+function _M:registerCharacterPlayed()
+	local pid = self:playerStatGetCharacterIdentifier()
+
+	profile.mod.characters = profile.mod.characters or { characters={} }
+	profile.mod.characters.characters[pid] = (profile.mod.characters.characters[pid] or 0) + 1
+	profile:saveModuleProfile("characters", profile.mod.characters)
+end
diff --git a/game/modules/tome/data/talents/techniques/superiority.lua b/game/modules/tome/data/talents/techniques/superiority.lua
index ad5534b3de09c010ced6db5bcfe7de84b1ff6e6d..d1e371e41f2473aa7540e8793dc0d4241b2dbe7b 100644
--- a/game/modules/tome/data/talents/techniques/superiority.lua
+++ b/game/modules/tome/data/talents/techniques/superiority.lua
@@ -1,116 +1,116 @@
--- ToME - Tales of Middle-Earth
--- 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
-
-
-newTalent{
-	name = "Juggernaut",
-	type = {"technique/superiority", 1},
-	require = techs_req_high1,
-	points = 5,
+-- ToME - Tales of Middle-Earth
+-- 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
+
+
+newTalent{
+	name = "Juggernaut",
+	type = {"technique/superiority", 1},
+	require = techs_req_high1,
+	points = 5,
 	random_ego = "attack",
-	cooldown = 40,
-	stamina = 60,
-	action = function(self, t)
-		self:setEffect(self.EFF_EARTHEN_BARRIER, 20, {power=self:getTalentLevelRaw(t) * 5})
-		return true
-	end,
-	info = function(self, t)
-		return ([[Concentrate on the battle, ignoring some of the damage you take.
-		Improves physical damage reduction by %d%%.]]):format(self:getTalentLevelRaw(t) * 5)
-	end,
-}
-
-newTalent{
-	name = "Onslaught",
-	type = {"technique/superiority", 2},
-	require = techs_req_high2,
-	points = 5,
-	mode = "sustained",
-	cooldown = 60,
-	sustain_stamina = 80,
-	activate = function(self, t)
-		return {
-			onslaught = self:addTemporaryValue("onslaught", math.floor(self:getTalentLevel(t))),
-			stamina = self:addTemporaryValue("stamina_regen", -15),
-		}
-	end,
-
-	deactivate = function(self, t, p)
-		self:removeTemporaryValue("onslaught", p.onslaught)
-		self:removeTemporaryValue("stamina_regen", p.stamina)
-		return true
-	end,
-	info = function(self, t)
-		return ([[Take an offensive stance, as you walk through your foes you knock them all back in an frontal arc (up to %d grids).
-		This consumes stamina rapidly.]]):
-		format(self:getTalentLevel(t))
-	end,
-}
-
-newTalent{
-	name = "Battle Call",
-	type = {"technique/superiority", 3},
-	require = techs_req_high3,
-	points = 5,
+	cooldown = 40,
+	stamina = 60,
+	action = function(self, t)
+		self:setEffect(self.EFF_EARTHEN_BARRIER, 20, {power=self:getTalentLevelRaw(t) * 5})
+		return true
+	end,
+	info = function(self, t)
+		return ([[Concentrate on the battle, ignoring some of the damage you take.
+		Improves physical damage reduction by %d%%.]]):format(self:getTalentLevelRaw(t) * 5)
+	end,
+}
+
+newTalent{
+	name = "Onslaught",
+	type = {"technique/superiority", 2},
+	require = techs_req_high2,
+	points = 5,
+	mode = "sustained",
+	cooldown = 60,
+	sustain_stamina = 80,
+	activate = function(self, t)
+		return {
+			onslaught = self:addTemporaryValue("onslaught", math.floor(self:getTalentLevel(t))),
+			stamina = self:addTemporaryValue("stamina_regen", -15),
+		}
+	end,
+
+	deactivate = function(self, t, p)
+		self:removeTemporaryValue("onslaught", p.onslaught)
+		self:removeTemporaryValue("stamina_regen", p.stamina)
+		return true
+	end,
+	info = function(self, t)
+		return ([[Take an offensive stance, as you walk through your foes you knock them all back in an frontal arc (up to %d grids).
+		This consumes stamina rapidly.]]):
+		format(self:getTalentLevel(t))
+	end,
+}
+
+newTalent{
+	name = "Battle Call",
+	type = {"technique/superiority", 3},
+	require = techs_req_high3,
+	points = 5,
 	random_ego = "attack",
-	cooldown = 10,
-	stamina = 30,
-	action = function(self, t)
-		local tg = {type="ball", range=0, friendlyfire=true, radius=2 + self:getTalentLevel(t), talent=t}
-		self:project(tg, self.x, self.y, function(px, py)
-			local target = game.level.map(px, py, Map.ACTOR)
-			if not target then return end
-			local tx, ty = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true})
-			if tx and ty and target:canBe("teleport") then
-				target:move(tx, ty, true)
-				game.logSeen(target, "%s is called to battle!", target.name:capitalize())
-			end
-		end)
-		return true
-	end,
-	info = function(self, t)	
-		return ([[Call all foes in a radius of %d around you into battle, getting them into melee range in an instant.]]):format(2+self:getTalentLevel(t))
-	end,
-}
-
-newTalent{
-	name = "Shattering Impact",
-	type = {"technique/superiority", 4},
-	require = techs_req_high4,
-	points = 5,
-	mode = "sustained",
-	cooldown = 30,
-	sustain_stamina = 40,
-	activate = function(self, t)
-		return {
-			dam = self:addTemporaryValue("shattering_impact", self:combatTalentWeaponDamage(t, 0.2, 0.6)),
-		}
-	end,
-
-	deactivate = function(self, t, p)
-		self:removeTemporaryValue("shattering_impact", p.dam)
-		return true
-	end,
-	info = function(self, t)
-		return ([[Put all your strength into your weapon blows, creating shattering impact that deal %d%% weapon damage to all nearby foes.
-		The impact damage will also affect you.
-		Each blow will drain 15 stamina.]]):
-		format(100 * self:combatTalentWeaponDamage(t, 0.1, 0.45))
-	end,
-}
+	cooldown = 10,
+	stamina = 30,
+	action = function(self, t)
+		local tg = {type="ball", range=0, friendlyfire=true, radius=2 + self:getTalentLevel(t), talent=t}
+		self:project(tg, self.x, self.y, function(px, py)
+			local target = game.level.map(px, py, Map.ACTOR)
+			if not target then return end
+			local tx, ty = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true})
+			if tx and ty and target:canBe("teleport") then
+				target:move(tx, ty, true)
+				game.logSeen(target, "%s is called to battle!", target.name:capitalize())
+			end
+		end)
+		return true
+	end,
+	info = function(self, t)	
+		return ([[Call all foes in a radius of %d around you into battle, getting them into melee range in an instant.]]):format(2+self:getTalentLevel(t))
+	end,
+}
+
+newTalent{
+	name = "Shattering Impact",
+	type = {"technique/superiority", 4},
+	require = techs_req_high4,
+	points = 5,
+	mode = "sustained",
+	cooldown = 30,
+	sustain_stamina = 40,
+	activate = function(self, t)
+		return {
+			dam = self:addTemporaryValue("shattering_impact", self:combatTalentWeaponDamage(t, 0.2, 0.6)),
+		}
+	end,
+
+	deactivate = function(self, t, p)
+		self:removeTemporaryValue("shattering_impact", p.dam)
+		return true
+	end,
+	info = function(self, t)
+		return ([[Put all your strength into your weapon blows, creating shattering impact that deal %d%% weapon damage to all nearby foes.
+		The impact damage will also affect you.
+		Each blow will drain 15 stamina.]]):
+		format(100 * self:combatTalentWeaponDamage(t, 0.1, 0.45))
+	end,
+}
diff --git a/game/modules/tome/data/talents/techniques/warcries.lua b/game/modules/tome/data/talents/techniques/warcries.lua
index 703d90811746666e4b35517868c01f220231faa7..d461b0f127ec9925e4491e4c99318c1e881c1a40 100644
--- a/game/modules/tome/data/talents/techniques/warcries.lua
+++ b/game/modules/tome/data/talents/techniques/warcries.lua
@@ -1,101 +1,101 @@
--- ToME - Tales of Middle-Earth
--- 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
-
-
-newTalent{
-	name = "Shattering Shout",
-	type = {"technique/warcries", 1},
-	require = techs_req_high1,
-	points = 5,
+-- ToME - Tales of Middle-Earth
+-- 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
+
+
+newTalent{
+	name = "Shattering Shout",
+	type = {"technique/warcries", 1},
+	require = techs_req_high1,
+	points = 5,
 	random_ego = "attack",
-	cooldown = 7,
-	stamina = 20,
-	range = 4,
-	action = function(self, t)
-		local tg = {type="cone", range=0, radius=3 + self:getTalentLevelRaw(t), friendlyfire=false}
-		local x, y = self:getTarget(tg)
-		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.PHYSICAL, (50 + self:getTalentLevel(t) * self:getStr()) / 2.3, {type="flame"})
-		return true
-	end,
-	info = function(self, t)
-		return ([[A powerful shout, doing %0.2f physical damage in a radius %d cone in front of you.
-		The damage increases with Strength.]]):format((50 + self:getTalentLevel(t) * self:getStr()) / 2.3, 3 + self:getTalentLevelRaw(t))
-	end,
-}
-
-newTalent{
-	name = "Second Wind",
-	type = {"technique/warcries", 2},
-	require = techs_req_high2,
-	points = 5,
+	cooldown = 7,
+	stamina = 20,
+	range = 4,
+	action = function(self, t)
+		local tg = {type="cone", range=0, radius=3 + self:getTalentLevelRaw(t), friendlyfire=false}
+		local x, y = self:getTarget(tg)
+		if not x or not y then return nil end
+		self:project(tg, x, y, DamageType.PHYSICAL, (50 + self:getTalentLevel(t) * self:getStr()) / 2.3, {type="flame"})
+		return true
+	end,
+	info = function(self, t)
+		return ([[A powerful shout, doing %0.2f physical damage in a radius %d cone in front of you.
+		The damage increases with Strength.]]):format((50 + self:getTalentLevel(t) * self:getStr()) / 2.3, 3 + self:getTalentLevelRaw(t))
+	end,
+}
+
+newTalent{
+	name = "Second Wind",
+	type = {"technique/warcries", 2},
+	require = techs_req_high2,
+	points = 5,
 	random_ego = "utility",
-	cooldown = 150,
-	action = function(self, t)
-		self:incStamina(20 + self:getTalentLevel(t) * 12)
-		return true
-	end,
-	info = function(self, t)
-		return ([[Take a deep breath to recover %d stamina.]]):
-		format(20 + self:getTalentLevel(t) * 12)
-	end,
-}
-
-newTalent{
-	name = "Battle Shout",
-	type = {"technique/warcries", 3},
-	require = techs_req_high3,
-	points = 5,
+	cooldown = 150,
+	action = function(self, t)
+		self:incStamina(20 + self:getTalentLevel(t) * 12)
+		return true
+	end,
+	info = function(self, t)
+		return ([[Take a deep breath to recover %d stamina.]]):
+		format(20 + self:getTalentLevel(t) * 12)
+	end,
+}
+
+newTalent{
+	name = "Battle Shout",
+	type = {"technique/warcries", 3},
+	require = techs_req_high3,
+	points = 5,
 	random_ego = "defensive",
-	cooldown = 30,
-	stamina = 40,
-	action = function(self, t)
-		self:setEffect(self.EFF_BATTLE_SHOUT, 5 + self:getTalentLevelRaw(t) * 2, {power=10+self:getTalentLevelRaw(t)})
-		return true
-	end,
-	info = function(self, t)	
-		return ([[Boost your life and stamina by %d%% for %d turns by uttering your battle shout.]]):format(5 + self:getTalentLevelRaw(t) * 2, 10 + self:getTalentLevelRaw(t))
-	end,
-}
-
-newTalent{
-	name = "Battle Cry",
-	type = {"technique/warcries", 4},
-	require = techs_req_high4,
-	points = 5,
+	cooldown = 30,
+	stamina = 40,
+	action = function(self, t)
+		self:setEffect(self.EFF_BATTLE_SHOUT, 5 + self:getTalentLevelRaw(t) * 2, {power=10+self:getTalentLevelRaw(t)})
+		return true
+	end,
+	info = function(self, t)	
+		return ([[Boost your life and stamina by %d%% for %d turns by uttering your battle shout.]]):format(5 + self:getTalentLevelRaw(t) * 2, 10 + self:getTalentLevelRaw(t))
+	end,
+}
+
+newTalent{
+	name = "Battle Cry",
+	type = {"technique/warcries", 4},
+	require = techs_req_high4,
+	points = 5,
 	random_ego = "attack",
-	cooldown = 30,
-	stamina = 40,
-	range = 4,
-	action = function(self, t)
-		local tg = {type="cone", range=0, radius=3 + self:getTalentLevelRaw(t), friendlyfire=false}
-		local x, y = self:getTarget(tg)
-		if not x or not y then return nil end
-		self:project(tg, x, y, function(px, py)
-			local target = game.level.map(px, py, Map.ACTOR)
-			if not target or not target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 10) then return end
-			target:setEffect(target.EFF_BATTLE_CRY, 7, {power=7 * self:getTalentLevel(t)})
-		end, {type="flame"})
-		return true
-	end,
-	info = function(self, t)
-		return ([[Your battle cry shatters the will of your foes, lowering their defense by %d for 7 turns, making them easier to hit.]]):
-		format(7 * self:getTalentLevel(t))
-	end,
-}
+	cooldown = 30,
+	stamina = 40,
+	range = 4,
+	action = function(self, t)
+		local tg = {type="cone", range=0, radius=3 + self:getTalentLevelRaw(t), friendlyfire=false}
+		local x, y = self:getTarget(tg)
+		if not x or not y then return nil end
+		self:project(tg, x, y, function(px, py)
+			local target = game.level.map(px, py, Map.ACTOR)
+			if not target or not target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 10) then return end
+			target:setEffect(target.EFF_BATTLE_CRY, 7, {power=7 * self:getTalentLevel(t)})
+		end, {type="flame"})
+		return true
+	end,
+	info = function(self, t)
+		return ([[Your battle cry shatters the will of your foes, lowering their defense by %d for 7 turns, making them easier to hit.]]):
+		format(7 * self:getTalentLevel(t))
+	end,
+}
diff --git a/game/modules/tome/dialogs/CharacterSheet.lua b/game/modules/tome/dialogs/CharacterSheet.lua
index 3ee95ec605b3a7c044986d8d7e0b2ecd2b7f9de5..638111e999bec086c1378c4d5f0e3401b65642b8 100644
--- a/game/modules/tome/dialogs/CharacterSheet.lua
+++ b/game/modules/tome/dialogs/CharacterSheet.lua
@@ -203,7 +203,7 @@ function _M:dump()
 			if o.combat then
 				strings[1] = ("Attack(Main Hand): %3d"):format(game.player:combatAttack(o.combat))
 				strings[2] = ("Damage(Main Hand): %3d"):format(game.player:combatDamage(o.combat))
-				strings[3] = ("APR	(Main Hand): %3d"):format(game.player:combatAPR(o.combat))
+				strings[3] = ("APR   (Main Hand): %3d"):format(game.player:combatAPR(o.combat))
 				strings[4] = ("Crit  (Main Hand): %3d%%"):format(game.player:combatCrit(o.combat))
 				strings[5] = ("Speed (Main Hand): %0.2f"):format(game.player:combatSpeed(o.combat))
 			end
@@ -256,8 +256,8 @@ function _M:dump()
 				nl()
 				nl(("Attack (Off Hand): %3d"):format(game.player:combatAttack(o.combat)))
 				nl(("Damage (Off Hand): %3d"):format(game.player:combatDamage(o.combat) * offmult))
-				nl(("APR	 (Off Hand): %3d"):format(game.player:combatAPR(o.combat)))
-				nl(("Crit	(Off Hand): %3d%%"):format(game.player:combatCrit(o.combat)))
+				nl(("APR    (Off Hand): %3d"):format(game.player:combatAPR(o.combat)))
+				nl(("Crit   (Off Hand): %3d%%"):format(game.player:combatCrit(o.combat)))
 				nl(("Speed  (Off Hand): %0.2f"):format(game.player:combatSpeed(o.combat)))
 			end
 		end
@@ -393,6 +393,9 @@ function _M:dump()
 					local char = string.char(string.byte('a') + index)
 					nl(("%s) %s"):format(char, o:getName{force_id=true}))
 					nl(("   %s"):format(table.concat(o:getTextualDesc(), "\n    ")))
+					if o.droppedBy then 
+						nl(("   Dropped by %s"):format(o.droppedBy))
+					end
 					index = index + 1
 				end
 			end
@@ -402,7 +405,7 @@ function _M:dump()
 	nl()
 	nl("  [Player Achievments]")
 	nl()
-	for id, data in pairs(self.actor.achievements) do
+	for id, data in pairs(self.actor.achievements or {}) do
 		local a = world:getAchievementFromId(id)
 		nl(("%s Was Achieved for %s At %s"):format(a.name,a.desc,data.when))
 	end
@@ -415,6 +418,9 @@ function _M:dump()
 			local char = string.char(string.byte('a') + item - 1)
 			nl(("%s) %s"):format(char, o:getName{force_id=true}))
 			nl(("   %s"):format(table.concat(o:getTextualDesc(), "\n    ")))
+			if o.droppedBy then 
+				nl(("   Dropped by %s"):format(o.droppedBy))
+			end
 		end
 	end
 
diff --git a/game/special/mainmenu/class/Game.lua b/game/special/mainmenu/class/Game.lua
index 9219d94c89e2d03ef18c2641f2990da22fccfeff..261c88bce136a722009e7ccf16de3f8a623da4d8 100644
--- a/game/special/mainmenu/class/Game.lua
+++ b/game/special/mainmenu/class/Game.lua
@@ -105,6 +105,10 @@ function _M:commandLineArgs(args)
 	if req_mod then
 		local mod = self.mod_list[req_mod]
 		if mod then
+			profile.generic.modules_loaded = profile.generic.modules_loaded or {}
+			profile.generic.modules_loaded[req_mod] = (profile.generic.modules_loaded[req_mod] or 0) + 1
+			profile:saveGenericProfile("modules_loaded", profile.generic.modules_loaded)
+
 			local M, W = mod.load()
 			_G.game = M.new()
 
@@ -140,6 +144,21 @@ function _M:onQuit()
 	os.exit()
 end
 
+function _M:checkFirstTime()
+	if not profile.generic.firstrun then
+		profile:checkFirstRun()
+		Dialog:yesnoPopup("First run profile notification", "Do you want to create or login to an Online profile?", function(ret)
+			if ret then
+				self:selectStepOnlineProfile()
+			else
+				self:selectStepMain()
+			end
+		end)
+	else
+		self:selectStepMain()
+	end
+end
+
 function _M:selectStepMain()
 	if self.step and self.step.close then self.step:close() end
 
@@ -177,6 +196,14 @@ function _M:selectStepMain()
 			end,
 		},
 	}, self.w * 0.3, self.h * 0.2, self.w * 0.4, self.h * 0.3)
+
+	if not self.firstrunchecked then
+		-- Check first time run for online profile
+		self.firstrunchecked = true
+		self:checkFirstTime()
+		return
+	end
+
 	self.step:setKeyHandling()
 	self.step:setMouseHandling()
 end
@@ -201,7 +228,7 @@ function _M:selectStepNew()
 		local lines = game.mod_list[game.step.selected].description:splitLines(self.w - 8, self.font)
 		local r, g, b
 		for i = 1, #lines do
-			r, g, b = s:drawColorString(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b)
+			r, g, b = s:drawColorStringBlended(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b)
 		end
 	end
 	self:registerDialog(display_module)
@@ -264,7 +291,7 @@ function _M:selectStepLoad()
 		local lines = list[game.step.selected].description:splitLines(self.w - 8, self.font)
 		local r, g, b
 		for i = 1, #lines do
-			r, g, b = s:drawColorString(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b)
+			r, g, b = s:drawColorStringBlended(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b)
 		end
 	end
 	self:registerDialog(display_module)
@@ -331,7 +358,7 @@ function _M:selectStepInstall()
 		local lines = dllist[game.step.selected].description:splitLines(self.w - 8, self.font)
 		local r, g, b
 		for i = 1, #lines do
-			r, g, b = s:drawColorString(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b)
+			r, g, b = s:drawColorStringBlended(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b)
 		end
 	end
 	self:registerDialog(display_module)
@@ -342,68 +369,75 @@ function _M:selectStepInstall()
 	self.step.key:addBind("EXIT", function() self:unregisterDialog(display_module) self:selectStepMain() end)
 end
 
-function _M:selectStepProfile()
-	local linda, th = Module:loadRemoteList()
-	local rawdllist = linda:receive("moduleslist")
-	th:join()
-
-	local dllist = {}
-	for i, mod in ipairs(rawdllist) do
-		if not self.mod_list[mod.short_name] then
-			dllist[#dllist+1] = mod
+function _M:createProfile(loginItem)
+	if self.justlogin then
+		profile:performlogin(loginItem.login, loginItem.pass)
+		if profile.auth then
+			Dialog:simplePopup("Profile logged in!", "Your online profile is active now...", function() self:checkLogged() self:selectStepProfile() end )
 		else
-			local lmod = self.mod_list[mod.short_name]
-			if mod.version[1] * 1000000 + mod.version[2] * 1000 + mod.version[3] > lmod.version[1] * 1000000 + lmod.version[2] * 1000 + lmod.version[3] then
-				dllist[#dllist+1] = mod
-			end
+			Dialog:simplePopup("Log in rejected", "Couldn't log you...", function() self:selectStepProfile() end )
 		end
-	end
-
-	if #dllist == 0 then
-		Dialog:simplePopup("No modules available", "There are no modules to install or upgrade.")
 		return
 	end
-
-	local display_module = Dialog.new("", self.w * 0.73, self.h, self.w * 0.26, 0, 255)
-
-	for i, mod in ipairs(dllist) do
-		mod.fct = function()
-			local d = DownloadDialog.new("Downloading: "..mod.long_name, mod.download, function(di, data)
-				fs.mkdir("/modules")
-				local f = fs.open("/modules/"..mod.short_name..".team", "w")
-				for i, v in ipairs(data) do f:write(v) end
-				f:close()
-
-				-- Relist modules and savefiles
-				self.mod_list = Module:listModules()
-				self.save_list = Module:listSavefiles()
-
-				if self.mod_list[mod.short_name] then
-					Dialog:simplePopup("Success!", "Your new game is now installed, you can play!", function() self:unregisterDialog(display_module) self:selectStepMain() end)
-				else
-					Dialog:simplePopup("Error!", "The downloaded game does not seem to respond to the test. Please contact contact@te4.org")
-				end
-			end)
-			self:registerDialog(d)
-			d:startDownload()
-		end
-		mod.onSelect = function()
-			display_module.title = mod.long_name
-			display_module.changed = true
-		end
+	profile:newProfile(loginItem.login, loginItem.name, loginItem.pass, loginItem.email)
+	if (profile.auth) then
+		Dialog:simplePopup("Profile created!", "Your online profile is active now...", function() self:checkLogged() self:selectStepProfile() end )
+	else
+		Dialog:simplePopup("Profile Failed to authenticate!", "Try logging in in a few moments", function() self:selectStepProfile() end )
 	end
 
-	display_module.drawDialog = function(self, s)
-		local lines = dllist[game.step.selected].description:splitLines(self.w - 8, self.font)
-		local r, g, b
-		for i = 1, #lines do
-			r, g, b = s:drawColorString(self.font, lines[i], 0, i * self.font:lineSkip(), r, g, b)
-		end
-	end
-	self:registerDialog(display_module)
+end
 
-	self.step = ButtonList.new(dllist, 10, 10, self.w * 0.24, (5 + 35) * #dllist, nil, 5)
+function _M:selectStepProfile()
+	self.step = ButtonList.new({
+		{
+			name = "Online Profile",
+			fct = function()
+				self:selectStepOnlineProfile()
+			end,
+		},
+		{
+			name = "Browse Generic Profile",
+			fct = function()
+				self:selectGenericProfile()
+			end,
+		},
+		{
+			name = "Browse Module Profiles",
+			fct = function()
+				self:selectModuleProfile()
+			end,
+		},
+		{
+			name = "Exit",
+			fct = function()
+				game:unregisterDialog(self)
+				self:selectStepMain()
+			end,
+		},
+	}, self.w * 0.3, self.h * 0.2, self.w * 0.4, self.h * 0.3)
 	self.step:setKeyHandling()
 	self.step:setMouseHandling()
 	self.step.key:addBind("EXIT", function() self:unregisterDialog(display_module) self:selectStepMain() end)
 end
+
+function _M:selectStepOnlineProfile()
+	if (profile.auth) then
+		Dialog:yesnoPopup("You are logged in", "Do you want to log out?", function(ret)
+			if ret then
+				profile:logOut()
+				self:checkLogged()
+			end
+		end)
+	else
+	local dialogdef = { }
+	dialogdef.name = "Profile login";
+	dialogdef.short = "Login";
+	dialogdef.fct = function(login) self:setPlayerLogin(login) end
+	Dialog:yesnoPopup("You are not registered", "Do you want to login to an existing profile?", function(ret)
+			self.justlogin = ret
+			dialogdef.justlogin = ret
+			self:registerDialog(require('special.mainmenu.dialogs.ProfileLogin').new(dialogdef))
+		end)
+	end
+end
diff --git a/game/special/mainmenu/dialogs/ProfileLogin.lua b/game/special/mainmenu/dialogs/ProfileLogin.lua
new file mode 100644
index 0000000000000000000000000000000000000000..95c1f5ec344c840e25f1e0508e5db9c22fae0f1a
--- /dev/null
+++ b/game/special/mainmenu/dialogs/ProfileLogin.lua
@@ -0,0 +1,130 @@
+-- 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"
+require "engine.Dialog"
+local ButtonList = require "engine.ButtonList"
+local Button = require "engine.Button"
+local TextBox = require "engine.TextBox"
+
+module(..., package.seeall, class.inherit(engine.Dialog))
+
+function _M:init(dialogdef)
+	engine.Dialog.init(self, "Profile Login "..dialogdef.name, 500, 400)	
+	self.dialogdef = dialogdef
+	self.alpha = 230
+	self.justlogin = dialogdef.justlogin
+	
+	self:keyCommands({        
+		_DELETE = function()
+			if self.controls[self.state] and self.controls[self.state].delete then
+				self.controls[self.state]:delete()
+			end
+		end,
+		_TAB = function()
+			self.state = self:changeFocus(true)
+		end,
+		_DOWN = function()
+			self.state = self:changeFocus(true)
+		end,
+		_UP = function()
+			self.state = self:changeFocus(false)
+		end,
+		_RIGHT = function()		
+			if self.state ~= "" and self.controls[self.state] and self.controls[self.state].moveRight then				
+				self.controls[self.state]:moveRight(1)
+			else
+				self.state = self:changeFocus(true)
+			end
+		end,
+		_LEFT = function()
+			if self.state ~= "" and self.controls[self.state] and self.controls[self.state].moveLeft then				
+				self.controls[self.state]:moveLeft(1)
+			else
+				self.state = self:changeFocus(false)
+			end
+		end,
+		_BACKSPACE = function()
+			if self.state ~= "" and self.controls[self.state] and self.controls[self.state].type=="TextBox" then
+				self.controls[self.state]:backSpace()
+			end
+		end,
+		__TEXTINPUT = function(c)			
+			if self.state ~= "" and self.controls[self.state] and self.controls[self.state].type=="TextBox" then
+				self.controls[self.state]:textInput(c)
+			end			
+		end,
+		_RETURN = function()
+			if self.state ~= "" and self.controls[self.state] and self.controls[self.state].type=="Button" then
+				self.controls[self.state]:fct()
+			end
+		end,
+					}, {
+		EXIT = function()
+			game:unregisterDialog(self)
+			game:selectStepProfile()
+		end
+	})
+	self:setMouseHandling()
+	self:addControl(Button.new("ok", "Ok", 50, 330, 50, 30, self, self.font, function() self:okclick() end))
+	self:addControl(Button.new("cancel", "Canel", 400, 330, 50, 30, self, self.font, function() self:cancelclick() end))
+	self:addControl(TextBox.new({name="login",title="You Login:",min=2, max=25, x=30, y=30, w=350, h=30}, self, self.font, "login name"))
+	self:addControl(TextBox.new({name="pass",title="Password:",min=2, max=25, x=30, y=70, w=350, h=30}, self, self.font, "password"))
+	if not self.justlogin then
+		self:addControl(TextBox.new({name="email",title="Email Address:",min=2, max=25, x=30, y=110, w=350, h=30}, self, self.font, "email address"))
+		self:addControl(TextBox.new({name="name",title="Name:",min=2, max=25, x=30, y=150, w=350, h=30}, self, self.font, "name"))
+	end
+	self:focusControl("login")	
+end
+
+
+function _M:okclick()
+	game:unregisterDialog(self) 
+	results = self:databind()	
+	game:selectStepProfile()
+	game:createProfile(results)
+end
+
+function _M:cancelclick()
+	game:unregisterDialog(self) 
+	game:selectStepProfile()
+end
+
+function _M:setMouseHandling()
+	self.old_mouse = engine.Mouse.current
+	self.mouse = engine.Mouse.new()
+	self.mouse:setCurrent()	
+	game.mouse = self.mouse
+end
+
+
+function _M:drawDialog(s, w, h)    	
+	local y = 200
+	local x = 30
+	lines = { "What's the online profile for?", "* Playing from several computers without copying and worries.", "* Keep track of your modules progression.", "* For Example Kill Count, Unlockables unlocked and achievments in TOME", "* Unlimited possibilites for migrating your module information.", "* Cool statistics for each module to help sharpen your gameplay style", "* Who doesn't like statistics?"}
+	for i = 1, #lines do
+		r, g, b = s:drawColorStringBlended(self.font, lines[i], x, y + i * self.font:lineSkip(), r, g, b)		
+	end	
+	self:drawControls(s)	
+end
+
+function _M:close()
+	if self.old_key then self.old_key:setCurrent() end
+	if self.old_mouse then self.old_mouse:setCurrent() end
+end