diff --git a/game/modules/example/class/Actor.lua b/game/modules/example/class/Actor.lua
new file mode 100644
index 0000000000000000000000000000000000000000..c8f26a7aeb6ad15ed8b52e65bd36f284a2715783
--- /dev/null
+++ b/game/modules/example/class/Actor.lua
@@ -0,0 +1,273 @@
+-- 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
+
+require "engine.class"
+require "engine.Actor"
+require "engine.Autolevel"
+require "engine.interface.ActorLife"
+require "engine.interface.ActorProject"
+require "engine.interface.ActorLevel"
+require "engine.interface.ActorStats"
+require "engine.interface.ActorTalents"
+require "engine.interface.ActorResource"
+require "engine.interface.ActorFOV"
+require "mod.class.interface.Combat"
+local Map = require "engine.Map"
+
+module(..., package.seeall, class.inherit(
+	-- a ToME actor is a complex beast it uses may inetrfaces
+	engine.Actor,
+	engine.interface.ActorLife,
+	engine.interface.ActorProject,
+	engine.interface.ActorLevel,
+	engine.interface.ActorStats,
+	engine.interface.ActorTalents,
+	engine.interface.ActorResource,
+	engine.interface.ActorQuest,
+	engine.interface.ActorFOV,
+	mod.class.interface.Combat
+))
+
+function _M:init(t, no_default)
+	-- Define some basic combat stats
+	self.combat_armor = 0
+
+	-- Default regen
+	t.power_regen = t.power_regen or 1
+	t.life_regen = t.life_regen or 0.25 -- Life regen real slow
+
+	-- Default melee barehanded damage
+	self.combat = { dam=1, dammod={str=1} }
+
+	engine.Actor.init(self, t, no_default)
+	engine.interface.ActorLife.init(self, t)
+	engine.interface.ActorProject.init(self, t)
+	engine.interface.ActorTalents.init(self, t)
+	engine.interface.ActorResource.init(self, t)
+	engine.interface.ActorStats.init(self, t)
+	engine.interface.ActorLevel.init(self, t)
+	engine.interface.ActorFOV.init(self, t)
+end
+
+function _M:act()
+	if not engine.Actor.act(self) then return end
+
+	self.changed = true
+
+	-- Cooldown talents
+	self:cooldownTalents()
+	-- Regen resources
+	self:regenLife()
+	self:regenResources()
+
+	-- Still enough energy to act ?
+	if self.energy.value < game.energy_to_act then return false end
+
+	return true
+end
+
+function _M:move(x, y, force)
+	local moved = false
+	if force or self:enoughEnergy() then
+		moved = engine.Actor.move(self, x, y, force)
+		if not force and moved and not self.did_energy then self:useEnergy() end
+	end
+	self.did_energy = nil
+	return moved
+end
+
+function _M:tooltip()
+	return ([[%s
+#00ffff#Level: %d
+#ff0000#HP: %d (%d%%)
+Stats: %d /  %d / %d
+%s]]):format(
+	self.name,
+	self.level,
+	self.life, self.life * 100 / self.max_life,
+	self:getStr(),
+	self:getDex(),
+	self:getCon()
+	)
+end
+
+function _M:die(src)
+	engine.interface.ActorLife.die(self, src)
+
+	-- Gives the killer some exp for the kill
+	if src and src.gainExp then
+		src:gainExp(self:worthExp(src))
+	end
+
+	return true
+end
+
+function _M:levelup()
+	self.max_life = self.max_life + 5
+
+	self:incMaxEnergy(3)
+
+	-- Healp up on new level
+	self.life = self.max_life
+	self.power = self.max_power
+end
+
+--- Notifies a change of stat value
+function _M:onStatChange(stat, v)
+	if stat == self.STAT_CON then
+		self.max_life = self.max_life + 2
+	end
+end
+
+function _M:attack(target)
+	self:bumpInto(target)
+end
+
+
+--- Called before a talent is used
+-- Check the actor can cast it
+-- @param ab the talent (not the id, the table)
+-- @return true to continue, false to stop
+function _M:preUseTalent(ab, silent)
+	if not self:enoughEnergy() then print("fail energy") return false end
+
+	if ab.mode == "sustained" then
+		if ab.sustain_power and self.max_power < ab.sustain_power and not self:isTalentActive(ab.id) then
+			game.logPlayer(self, "You do not have enough power to activate %s.", ab.name)
+			return false
+		end
+	else
+		if ab.power and self:getMana() < ab.power * (100 + self.fatigue) / 100 then
+			game.logPlayer(self, "You do not have enough power to cast %s.", ab.name)
+			return false
+		end
+	end
+
+	if not silent then
+		-- Allow for silent talents
+		if ab.message ~= nil then
+			if ab.message then
+				game.logSeen(self, "%s", self:useTalentMessage(ab))
+			end
+		elseif ab.mode == "sustained" and not self:isTalentActive(ab.id) then
+			game.logSeen(self, "%s activates %s.", self.name:capitalize(), ab.name)
+		elseif ab.mode == "sustained" and self:isTalentActive(ab.id) then
+			game.logSeen(self, "%s deactivates %s.", self.name:capitalize(), ab.name)
+		else
+			game.logSeen(self, "%s uses %s.", self.name:capitalize(), ab.name)
+		end
+	end
+	return true
+end
+
+--- Called before a talent is used
+-- Check if it must use a turn, mana, stamina, ...
+-- @param ab the talent (not the id, the table)
+-- @param ret the return of the talent action
+-- @return true to continue, false to stop
+function _M:postUseTalent(ab, ret)
+	if not ret then return end
+
+	self:useEnergy()
+
+	if ab.mode == "sustained" then
+		if not self:isTalentActive(ab.id) then
+			if ab.sustain_power then
+				self.max_power = self.max_power - ab.sustain_power
+			end
+		else
+			if ab.sustain_power then
+				self.max_power = self.max_power + ab.sustain_power
+			end
+		end
+	else
+		if ab.power then
+			self:incPower(-ab.power)
+		end
+	end
+
+	return true
+end
+
+--- Return the full description of a talent
+-- You may overload it to add more data (like power usage, ...)
+function _M:getTalentFullDescription(t)
+	local d = {}
+
+	if t.mode == "passive" then d[#d+1] = "#6fff83#Use mode: #00FF00#Passive"
+	elseif t.mode == "sustained" then d[#d+1] = "#6fff83#Use mode: #00FF00#Sustained"
+	else d[#d+1] = "#6fff83#Use mode: #00FF00#Activable"
+	end
+
+	if t.power or t.sustain_power then d[#d+1] = "#6fff83#Mana cost: #7fffd4#"..(t.power or t.sustain_power) end
+	if self:getTalentRange(t) > 1 then d[#d+1] = "#6fff83#Range: #FFFFFF#"..self:getTalentRange(t)
+	else d[#d+1] = "#6fff83#Range: #FFFFFF#melee/personal"
+	end
+	if t.cooldown then d[#d+1] = "#6fff83#Cooldown: #FFFFFF#"..t.cooldown end
+
+	return table.concat(d, "\n").."\n#6fff83#Description: #FFFFFF#"..t.info(self, t)
+end
+
+--- How much experience is this actor worth
+-- @param target to whom is the exp rewarded
+-- @return the experience rewarded
+function _M:worthExp(target)
+	if not target.level or self.level < target.level - 3 then return 0 end
+
+	local mult = 2
+	if self.unique then mult = 6
+	elseif self.egoed then mult = 3 end
+	return self.level * mult * self.exp_worth
+end
+
+--- Can the actor see the target actor
+-- This does not check LOS or such, only the actual ability to see it.<br/>
+-- Check for telepathy, invisibility, stealth, ...
+function _M:canSee(actor, def, def_pct)
+	if not actor then return false, 0 end
+
+	-- Check for stealth. Checks against the target cunning and level
+	if actor:attr("stealth") and actor ~= self then
+		local def = self.level / 2 + self:getCun(25)
+		local hit, chance = self:checkHit(def, actor:attr("stealth") + (actor:attr("inc_stealth") or 0), 0, 100)
+		if not hit then
+			return false, chance
+		end
+	end
+
+	if def ~= nil then
+		return def, def_pct
+	else
+		return true, 100
+	end
+end
+
+--- Can the target be applied some effects
+-- @param what a string describing what is being tried
+function _M:canBe(what)
+	if what == "poison" and rng.percent(100 * (self:attr("poison_immune") or 0)) then return false end
+	if what == "cut" and rng.percent(100 * (self:attr("cut_immune") or 0)) then return false end
+	if what == "confusion" and rng.percent(100 * (self:attr("confusion_immune") or 0)) then return false end
+	if what == "blind" and rng.percent(100 * (self:attr("blind_immune") or 0)) then return false end
+	if what == "stun" and rng.percent(100 * (self:attr("stun_immune") or 0)) then return false end
+	if what == "fear" and rng.percent(100 * (self:attr("fear_immune") or 0)) then return false end
+	if what == "knockback" and rng.percent(100 * (self:attr("knockback_immune") or 0)) then return false end
+	if what == "instakill" and rng.percent(100 * (self:attr("instakill_immune") or 0)) then return false end
+	return true
+end
diff --git a/game/modules/example/class/Game.lua b/game/modules/example/class/Game.lua
new file mode 100644
index 0000000000000000000000000000000000000000..16c976e3de96c1af0132dee3b00866102d9672e7
--- /dev/null
+++ b/game/modules/example/class/Game.lua
@@ -0,0 +1,512 @@
+-- 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
+
+require "engine.class"
+require "engine.GameTurnBased"
+require "engine.KeyBind"
+local Savefile = require "engine.Savefile"
+local DamageType = require "engine.DamageType"
+local Zone = require "engine.Zone"
+local Map = require "engine.Map"
+local Target = require "engine.Target"
+local Level = require "engine.Level"
+local Birther = require "engine.Birther"
+
+local Grid = require "mod.class.Grid"
+local Actor = require "mod.class.Actor"
+local Player = require "mod.class.Player"
+local NPC = require "mod.class.NPC"
+
+local LogDisplay = require "engine.LogDisplay"
+local LogFlasher = require "engine.LogFlasher"
+local DebugConsole = require "engine.DebugConsole"
+local FlyingText = require "engine.FlyingText"
+local Tooltip = require "engine.Tooltip"
+
+local QuitDialog = require "mod.dialogs.Quit"
+
+module(..., package.seeall, class.inherit(engine.GameTurnBased, engine.interface.GameMusic, engine.interface.GameSound))
+
+function _M:init()
+	engine.GameTurnBased.init(self, engine.KeyBind.new(), 1000, 100)
+
+	-- Pause at birth
+	self.paused = true
+
+	-- Same init as when loaded from a savefile
+	self:loaded()
+end
+
+function _M:run()
+	self.flash = LogFlasher.new(0, 0, self.w, 20, nil, nil, nil, {255,255,255}, {0,0,0})
+	self.logdisplay = LogDisplay.new(0, self.h * 0.8, self.w * 0.5, self.h * 0.2, nil, nil, nil, {255,255,255}, {30,30,30})
+	self.tooltip = Tooltip.new(nil, nil, {255,255,255}, {30,30,30})
+	self.flyers = FlyingText.new()
+	self:setFlyingText(self.flyers)
+
+	self.log = function(style, ...) if type(style) == "number" then self.logdisplay(...) self.flash(style, ...) else self.logdisplay(style, ...) self.flash(self.flash.NEUTRAL, style, ...) end end
+	self.logSeen = function(e, style, ...) if e and self.level.map.seens(e.x, e.y) then self.log(style, ...) end end
+	self.logPlayer = function(e, style, ...) if e == self.player then self.log(style, ...) end end
+
+	self.log(self.flash.GOOD, "Welcome to #00FF00#the template module!")
+
+	-- Setup inputs
+	self:setupCommands()
+	self:setupMouse()
+
+	-- Starting from here we create a new game
+	if not self.player then self:newGame() end
+
+	-- Setup the targetting system
+	self.target = Target.new(Map, self.player)
+	self.target.target.entity = self.player
+	self.old_tmx, self.old_tmy = 0, 0
+	self.target_style = "lock"
+
+	-- Ok everything is good to go, activate the game in the engine!
+	self:setCurrent()
+
+	if self.level then self:setupDisplayMode() end
+end
+
+function _M:newGame()
+	self.player = Player.new{name=self.player_name, game_ender=true}
+	Map:setViewerActor(self.player)
+	self:setupDisplayMode()
+
+	local birth = Birther.new(self.player, {"base", "role" }, function()
+		self:changeLevel(1, "dungeon")
+		print("[PLAYER BIRTH] resolve...")
+		self.player:resolve()
+		self.player:resolve(nil, true)
+		self.player.energy.value = self.energy_to_act
+		self.paused = true
+		print("[PLAYER BIRTH] resolved!")
+	end)
+	self:registerDialog(birth)
+end
+
+function _M:loaded()
+	engine.GameTurnBased.loaded(self)
+	Zone:setup{npc_class="mod.class.NPC", grid_class="mod.class.Grid", }
+	Map:setViewerActor(self.player)
+	Map:setViewPort(200, 20, self.w - 200, math.floor(self.h * 0.80) - 20, 32, 32, nil, 20, true)
+	self.key = engine.KeyBind.new()
+end
+
+function _M:onResolutionChange()
+	engine.Game.onResolutionChange(self)
+	print("[RESOLUTION] changed to ", self.w, self.h)
+	self:setupDisplayMode()
+	self.flash:resize(0, 0, self.w, 20)
+	self.logdisplay:resize(0, self.h * 0.8, self.w * 0.5, self.h * 0.2)
+end
+
+function _M:setupDisplayMode()
+	print("[DISPLAY MODE] 32x32 ASCII/background")
+	Map:setViewPort(200, 20, self.w - 200, math.floor(self.h * 0.80) - 20, 32, 32, nil, 22, true, true)
+	Map:resetTiles()
+	Map.tiles.use_images = false
+	self:setupMiniMap()
+end
+
+function _M:setupMiniMap()
+	if self.level and self.level.map then self.level.map._map:setupMiniMapGridSize(4) end
+end
+
+function _M:save()
+	return class.save(self, self:defaultSavedFields{}, true)
+end
+
+function _M:getSaveDescription()
+	return {
+		name = self.player.name,
+		description = ([[Exploring level %d of %s.]]):format(self.level.level, self.zone.name),
+	}
+end
+
+function _M:leaveLevel(level, lev, old_lev)
+	if level:hasEntity(self.player) then
+		level.exited = level.exited or {}
+		if lev > old_lev then
+			level.exited.down = {x=self.player.x, y=self.player.y}
+		else
+			level.exited.up = {x=self.player.x, y=self.player.y}
+		end
+		level.last_turn = game.turn
+		level:removeEntity(self.player)
+	end
+end
+
+function _M:changeLevel(lev, zone)
+	local old_lev = (self.level and not zone) and self.level.level or -1000
+	if zone then
+		if self.zone then
+			self.zone:leaveLevel(false, lev, old_lev)
+			self.zone:leave()
+		end
+		if type(zone) == "string" then
+			self.zone = Zone.new(zone)
+		else
+			self.zone = zone
+		end
+	end
+	self.zone:getLevel(self, lev, old_lev)
+
+	self:setupMiniMap()
+end
+
+function _M:getPlayer()
+	return self.player
+end
+
+function _M:tick()
+	if self.level then
+		if self.target.target.entity and not self.level:hasEntity(self.target.target.entity) then self.target.target.entity = false end
+
+		engine.GameTurnBased.tick(self)
+		-- Fun stuff: this can make the game realtime, although callit it in display() will make it work better
+		-- (since display is on a set FPS while tick() ticks as much as possible
+		-- engine.GameEnergyBased.tick(self)
+	end
+	-- When paused (waiting for player input) we return true: this means we wont be called again until an event wakes us
+	if game.paused then return true end
+end
+
+--- Called every game turns
+-- Does nothing, you can override it
+function _M:onTurn()
+	-- The following happens only every 10 game turns (once for every turn of 1 mod speed actors)
+	if self.turn % 10 ~= 0 then return end
+
+	-- Process overlay effects
+	self.level.map:processEffects()
+end
+
+function _M:display()
+	-- We display the player's interface
+	self.flash:display():toScreen(self.flash.display_x, self.flash.display_y)
+	self.logdisplay:display():toScreen(self.logdisplay.display_x, self.logdisplay.display_y)
+	if self.player then self.player.changed = false end
+
+	-- Now the map, if any
+	if self.level and self.level.map and self.level.map.finished then
+		-- Display the map and compute FOV for the player if needed
+		if self.level.map.changed then
+			self.player:playerFOV()
+		end
+
+		self.level.map:display()
+
+		-- Display the targetting system if active
+		self.target:display()
+
+		-- Display a tooltip if available
+		if self.tooltip_x then
+			local mx, my = self.tooltip_x , self.tooltip_y
+			local tmx, tmy = self.level.map:getMouseTile(mx, my)
+			self.tooltip:displayAtMap(tmx, tmy, mx, my)
+		end
+
+		-- Move target around
+		if self.old_tmx ~= tmx or self.old_tmy ~= tmy then
+			self.target.target.x, self.target.target.y = tmx, tmy
+		end
+		self.old_tmx, self.old_tmy = tmx, tmy
+
+		-- And the minimap
+		self.level.map:minimapDisplay(self.w - 200, 20, util.bound(self.player.x - 25, 0, self.level.map.w - 50), util.bound(self.player.y - 25, 0, self.level.map.h - 50), 50, 50, 0.6)
+	end
+
+	engine.GameTurnBased.display(self)
+end
+
+--- Targeting mode
+-- Now before this is an hard piece of code. You probably wont need to change it much.<br/>
+-- This uses a coroutine to allow a talent to request a target without interruption, yet while preserving the realtime-ness of the engine
+function _M:targetMode(v, msg, co, typ)
+	local old = self.target_mode
+	self.target_mode = v
+
+	if not v then
+		Map:setViewerFaction(self.always_target and "players" or nil)
+		if msg then self.log(type(msg) == "string" and msg or "Tactical display disabled. Press shift+'t' or right mouse click to enable.") end
+		self.level.map.changed = true
+		self.target:setActive(false)
+
+		if tostring(old) == "exclusive" then
+			self.key = self.normal_key
+			self.key:setCurrent()
+			if self.target_co then
+				local co = self.target_co
+				self.target_co = nil
+				local ok, err = coroutine.resume(co, self.target.target.x, self.target.target.y, self.target.target.entity)
+				if not ok and err then print(debug.traceback(co)) error(err) end
+			end
+		end
+	else
+		Map:setViewerFaction("players")
+		if msg then self.log(type(msg) == "string" and msg or "Tactical display enabled. Press shift+'t' to disable.") end
+		self.level.map.changed = true
+		self.target:setActive(true, typ)
+		self.target_style = "lock"
+
+		-- Exclusive mode means we disable the current key handler and use a specific one
+		-- that only allows targetting and resumes talent coroutine when done
+		if tostring(v) == "exclusive" then
+			self.target_co = co
+			self.key = self.targetmode_key
+			self.key:setCurrent()
+
+			if self.target.target.entity and self.level.map.seens(self.target.target.entity.x, self.target.target.entity.y) and self.player ~= self.target.target.entity then
+			else
+				self.target:scan(5, nil, self.player.x, self.player.y)
+			end
+		end
+		self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y)
+	end
+end
+
+--- Setup the keybinds
+function _M:setupCommands()
+	-- One key handler for targeting
+	self.targetmode_key = engine.KeyBind.new()
+	self.targetmode_key:addCommands{ _SPACE=function() self:targetMode(false, false) end, }
+	self.targetmode_key:addBinds
+	{
+		TACTICAL_DISPLAY = function() self:targetMode(false, false) end,
+		ACCEPT = function()
+			self:targetMode(false, false)
+			self.tooltip_x, self.tooltip_y = nil, nil
+		end,
+		EXIT = function()
+			self.target.target.entity = nil
+			self.target.target.x = nil
+			self.target.target.y = nil
+			self:targetMode(false, false)
+			self.tooltip_x, self.tooltip_y = nil, nil
+		end,
+		-- Targeting movement
+		RUN_LEFT = function() self.target:freemove(4) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		RUN_RIGHT = function() self.target:freemove(6) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		RUN_UP = function() self.target:freemove(8) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		RUN_DOWN = function() self.target:freemove(2) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		RUN_LEFT_DOWN = function() self.target:freemove(1) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		RUN_RIGHT_DOWN = function() self.target:freemove(3) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		RUN_LEFT_UP = function() self.target:freemove(7) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		RUN_RIGHT_UP = function() self.target:freemove(9) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+
+		MOVE_LEFT = function() if self.target_style == "lock" then self.target:scan(4) else self.target:freemove(4) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		MOVE_RIGHT = function() if self.target_style == "lock" then self.target:scan(6) else self.target:freemove(6) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		MOVE_UP = function() if self.target_style == "lock" then self.target:scan(8) else self.target:freemove(8) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		MOVE_DOWN = function() if self.target_style == "lock" then self.target:scan(2) else self.target:freemove(2) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		MOVE_LEFT_DOWN = function() if self.target_style == "lock" then self.target:scan(1) else self.target:freemove(1) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		MOVE_RIGHT_DOWN = function() if self.target_style == "lock" then self.target:scan(3) else self.target:freemove(3) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		MOVE_LEFT_UP = function() if self.target_style == "lock" then self.target:scan(7) else self.target:freemove(7) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+		MOVE_RIGHT_UP = function() if self.target_style == "lock" then self.target:scan(9) else self.target:freemove(9) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
+	}
+
+	self.normal_key = self.key
+
+	-- One key handled for normal function
+	self.key:addBinds
+	{
+		-- Movements
+		MOVE_LEFT = function() self.player:moveDir(4) end,
+		MOVE_RIGHT = function() self.player:moveDir(6) end,
+		MOVE_UP = function() self.player:moveDir(8) end,
+		MOVE_DOWN = function() self.player:moveDir(2) end,
+		MOVE_LEFT_UP = function() self.player:moveDir(7) end,
+		MOVE_LEFT_DOWN = function() self.player:moveDir(1) end,
+		MOVE_RIGHT_UP = function() self.player:moveDir(9) end,
+		MOVE_RIGHT_DOWN = function() self.player:moveDir(3) end,
+		MOVE_STAY = function() self.player:useEnergy() end,
+
+		RUN_LEFT = function() self.player:runInit(4) end,
+		RUN_RIGHT = function() self.player:runInit(6) end,
+		RUN_UP = function() self.player:runInit(8) end,
+		RUN_DOWN = function() self.player:runInit(2) end,
+		RUN_LEFT_UP = function() self.player:runInit(7) end,
+		RUN_LEFT_DOWN = function() self.player:runInit(1) end,
+		RUN_RIGHT_UP = function() self.player:runInit(9) end,
+		RUN_RIGHT_DOWN = function() self.player:runInit(3) end,
+
+		-- Hotkeys
+		HOTKEY_1 = function() self.player:activateHotkey(1) end,
+		HOTKEY_2 = function() self.player:activateHotkey(2) end,
+		HOTKEY_3 = function() self.player:activateHotkey(3) end,
+		HOTKEY_4 = function() self.player:activateHotkey(4) end,
+		HOTKEY_5 = function() self.player:activateHotkey(5) end,
+		HOTKEY_6 = function() self.player:activateHotkey(6) end,
+		HOTKEY_7 = function() self.player:activateHotkey(7) end,
+		HOTKEY_8 = function() self.player:activateHotkey(8) end,
+		HOTKEY_9 = function() self.player:activateHotkey(9) end,
+		HOTKEY_10 = function() self.player:activateHotkey(10) end,
+		HOTKEY_11 = function() self.player:activateHotkey(11) end,
+		HOTKEY_12 = function() self.player:activateHotkey(12) end,
+		HOTKEY_SECOND_1 = function() self.player:activateHotkey(13) end,
+		HOTKEY_SECOND_2 = function() self.player:activateHotkey(14) end,
+		HOTKEY_SECOND_3 = function() self.player:activateHotkey(15) end,
+		HOTKEY_SECOND_4 = function() self.player:activateHotkey(16) end,
+		HOTKEY_SECOND_5 = function() self.player:activateHotkey(17) end,
+		HOTKEY_SECOND_6 = function() self.player:activateHotkey(18) end,
+		HOTKEY_SECOND_7 = function() self.player:activateHotkey(19) end,
+		HOTKEY_SECOND_8 = function() self.player:activateHotkey(20) end,
+		HOTKEY_SECOND_9 = function() self.player:activateHotkey(21) end,
+		HOTKEY_SECOND_10 = function() self.player:activateHotkey(22) end,
+		HOTKEY_SECOND_11 = function() self.player:activateHotkey(23) end,
+		HOTKEY_SECOND_12 = function() self.player:activateHotkey(24) end,
+		HOTKEY_THIRD_1 = function() self.player:activateHotkey(25) end,
+		HOTKEY_THIRD_2 = function() self.player:activateHotkey(26) end,
+		HOTKEY_THIRD_3 = function() self.player:activateHotkey(27) end,
+		HOTKEY_THIRD_4 = function() self.player:activateHotkey(28) end,
+		HOTKEY_THIRD_5 = function() self.player:activateHotkey(29) end,
+		HOTKEY_THIRD_6 = function() self.player:activateHotkey(30) end,
+		HOTKEY_THIRD_7 = function() self.player:activateHotkey(31) end,
+		HOTKEY_THIRD_8 = function() self.player:activateHotkey(31) end,
+		HOTKEY_THIRD_9 = function() self.player:activateHotkey(33) end,
+		HOTKEY_THIRD_10 = function() self.player:activateHotkey(34) end,
+		HOTKEY_THIRD_11 = function() self.player:activateHotkey(35) end,
+		HOTKEY_THIRD_12 = function() self.player:activateHotkey(36) end,
+		HOTKEY_PREV_PAGE = function() self.player:prevHotkeyPage() end,
+		HOTKEY_NEXT_PAGE = function() self.player:nextHotkeyPage() end,
+
+		-- Actions
+		CHANGE_LEVEL = function()
+			local e = self.level.map(self.player.x, self.player.y, Map.TERRAIN)
+			if self.player:enoughEnergy() and e.change_level then
+				self:changeLevel(e.change_zone and e.change_level or self.level.level + e.change_level, e.change_zone)
+			else
+				self.log("There is no way out of this level here.")
+			end
+		end,
+
+		REST = function()
+			self.player:restInit()
+		end,
+
+		USE_TALENTS = function()
+			self.player:useTalents()
+		end,
+
+		SAVE_GAME = function()
+			self:saveGame()
+		end,
+
+		SHOW_CHARACTER_SHEET = function()
+			self:registerDialog(require("mod.dialogs.CharacterSheet").new(self.player))
+		end,
+
+		-- Exit the game
+		QUIT_GAME = function()
+			self:onQuit()
+		end,
+
+		EXIT = function()
+			local menu menu = require("engine.dialogs.GameMenu").new{
+				"resume",
+				"keybinds",
+				"resolution",
+				"save",
+				"quit"
+			}
+			self:registerDialog(menu)
+		end,
+
+		TACTICAL_DISPLAY = function()
+			if Map.view_faction then
+				self.always_target = nil
+				Map:setViewerFaction(nil)
+			else
+				self.always_target = true
+				Map:setViewerFaction("players")
+			end
+		end,
+
+		LOOK_AROUND = function()
+			self.flash:empty(true)
+			self.flash(self.flash.GOOD, "Looking around... (direction keys to select interresting things, shift+direction keys to move freely)")
+			local co = coroutine.create(function() self.player:getTarget{type="hit", no_restrict=true, range=2000} end)
+			local ok, err = coroutine.resume(co)
+			if not ok and err then print(debug.traceback(co)) error(err) end
+		end,
+	}
+	self.key:setCurrent()
+end
+
+function _M:setupMouse()
+	-- Those 2 locals will be "absorbed" into the mosue event handler function, this is a closure
+	local derivx, derivy = 0, 0
+
+	self.mouse:registerZone(Map.display_x, Map.display_y, Map.viewport.width, Map.viewport.height, function(button, mx, my, xrel, yrel)
+		-- Move tooltip
+		self.tooltip_x, self.tooltip_y = mx, my
+		local tmx, tmy = self.level.map:getMouseTile(mx, my)
+
+		-- Target stuff
+		if button == "right" then
+			self.player:mouseMove(tmx, tmy)
+
+		-- Move map around
+		elseif button == "left" and xrel and yrel then
+			derivx = derivx + xrel
+			derivy = derivy + yrel
+			game.level.map.changed = true
+			if derivx >= game.level.map.tile_w then
+				game.level.map.mx = game.level.map.mx - 1
+				derivx = derivx - game.level.map.tile_w
+			elseif derivx <= -game.level.map.tile_w then
+				game.level.map.mx = game.level.map.mx + 1
+				derivx = derivx + game.level.map.tile_w
+			end
+			if derivy >= game.level.map.tile_h then
+				game.level.map.my = game.level.map.my - 1
+				derivy = derivy - game.level.map.tile_h
+			elseif derivy <= -game.level.map.tile_h then
+				game.level.map.my = game.level.map.my + 1
+				derivy = derivy + game.level.map.tile_h
+			end
+			game.level.map._map:setScroll(game.level.map.mx, game.level.map.my)
+		end
+	end)
+	-- Scroll message log
+	self.mouse:registerZone(self.logdisplay.display_x, self.logdisplay.display_y, self.w, self.h, function(button)
+		if button == "wheelup" then self.logdisplay:scrollUp(1) end
+		if button == "wheeldown" then self.logdisplay:scrollUp(-1) end
+	end, {button=true})
+	self.mouse:setCurrent()
+end
+
+--- Ask if we realy want to close, if so, save the game first
+function _M:onQuit()
+	self.player:restStop()
+
+	if not self.quit_dialog then
+		self.quit_dialog = QuitDialog.new()
+		self:registerDialog(self.quit_dialog)
+	end
+end
+
+--- Requests the game to save
+function _M:saveGame()
+	local save = Savefile.new(self.save_name)
+	save:saveGame(self)
+	save:close()
+	self.log("Saved game.")
+end
diff --git a/game/modules/example/class/Grid.lua b/game/modules/example/class/Grid.lua
new file mode 100644
index 0000000000000000000000000000000000000000..f6df667897e586d0233680e33dcbeee9a9c34af2
--- /dev/null
+++ b/game/modules/example/class/Grid.lua
@@ -0,0 +1,68 @@
+-- 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
+
+require "engine.class"
+require "engine.Grid"
+local DamageType = require "engine.DamageType"
+
+module(..., package.seeall, class.inherit(engine.Grid))
+
+function _M:init(t, no_default)
+	engine.Grid.init(self, t, no_default)
+end
+
+function _M:block_move(x, y, e, act, couldpass)
+	-- Open doors
+	if self.door_opened and act then
+		game.level.map(x, y, engine.Map.TERRAIN, game.zone.grid_list.DOOR_OPEN)
+		return true
+	elseif self.door_opened and not couldpass then
+		return true
+	end
+
+	-- Pass walls
+	if e and self.can_pass and e.can_pass then
+		for what, check in pairs(e.can_pass) do
+			if self.can_pass[what] and self.can_pass[what] <= check then return false end
+		end
+	end
+
+	return self.does_block_move
+end
+
+function _M:on_move(x, y, who, forced)
+	if forced then return end
+	if who.move_project and next(who.move_project) then
+		for typ, dam in pairs(who.move_project) do
+			DamageType:get(typ).projector(who, x, y, typ, dam)
+		end
+	end
+end
+
+function _M:tooltip()
+	if self.show_tooltip then
+		local name = ((self.show_tooltip == true) and self.name or self.show_tooltip)
+		if self.desc then
+			return name.."\n"..self.desc
+		else
+			return name
+		end
+	end
+end
+
diff --git a/game/modules/example/class/NPC.lua b/game/modules/example/class/NPC.lua
new file mode 100644
index 0000000000000000000000000000000000000000..a5b78b82a5c81cf1ebb6add99f8966c30584169c
--- /dev/null
+++ b/game/modules/example/class/NPC.lua
@@ -0,0 +1,70 @@
+-- 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
+
+require "engine.class"
+local ActorAI = require "engine.interface.ActorAI"
+local Faction = require "engine.Faction"
+require "mod.class.Actor"
+
+module(..., package.seeall, class.inherit(mod.class.Actor, engine.interface.ActorAI))
+
+function _M:init(t, no_default)
+	mod.class.Actor.init(self, t, no_default)
+	ActorAI.init(self, t)
+end
+
+function _M:act()
+	-- Do basic actor stuff
+	if not mod.class.Actor.act(self) then return end
+
+	-- Compute FOV, if needed
+	self:computeFOV(self.sight or 20)
+
+	-- Let the AI think .... beware of Shub !
+	-- If AI did nothing, use energy anyway
+	self:doAI()
+	if not self.energy.used then self:useEnergy() end
+end
+
+--- Called by ActorLife interface
+-- We use it to pass aggression values to the AIs
+function _M:onTakeHit(value, src)
+	if not self.ai_target.actor and src.targetable then
+		self.ai_target.actor = src
+	end
+
+	return mod.class.Actor.onTakeHit(self, value, src)
+end
+
+function _M:tooltip()
+	local factcolor, factstate = "#ANTIQUE_WHITE#", "neutral"
+	if self:reactionToward(game.player) < 0 then factcolor, factstate = "#LIGHT_RED#", "hostile"
+	elseif self:reactionToward(game.player) > 0 then factcolor, factstate = "#LIGHT_GREEN#", "friendly"
+	end
+
+	local str = mod.class.Actor.tooltip(self)
+	return str..([[
+
+Faction: %s%s (%s)
+Target: %s
+UID: %d]]):format(
+	factcolor, Faction.factions[self.faction].name, factstate,
+	self.ai_target.actor and self.ai_target.actor.name or "none",
+	self.uid)
+end
diff --git a/game/modules/example/class/Player.lua b/game/modules/example/class/Player.lua
new file mode 100644
index 0000000000000000000000000000000000000000..d19816ab55611e402754504445b70210311fd04c
--- /dev/null
+++ b/game/modules/example/class/Player.lua
@@ -0,0 +1,240 @@
+-- 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
+
+require "engine.class"
+require "mod.class.Actor"
+require "engine.interface.PlayerRest"
+require "engine.interface.PlayerRun"
+require "engine.interface.PlayerHotkeys"
+local Map = require "engine.Map"
+local Dialog = require "engine.Dialog"
+local ActorTalents = require "engine.interface.ActorTalents"
+local DeathDialog = require "mod.dialogs.DeathDialog"
+local Astar = require"engine.Astar"
+local DirectPath = require"engine.DirectPath"
+
+--- Defines the player for ToME
+-- It is a normal actor, with some redefined methods to handle user interaction.<br/>
+-- It is also able to run and rest and use hotkeys
+module(..., package.seeall, class.inherit(
+	mod.class.Actor,
+	engine.interface.PlayerRest,
+	engine.interface.PlayerRun,
+	engine.interface.PlayerHotkeys
+))
+
+function _M:init(t, no_default)
+	t.display=t.display or '@'
+	t.color_r=t.color_r or 230
+	t.color_g=t.color_g or 230
+	t.color_b=t.color_b or 230
+
+	t.player = true
+	t.type = t.type or "humanoid"
+	t.subtype = t.subtype or "player"
+	t.faction = t.faction or "players"
+
+	t.lite = t.lite or 0
+
+	mod.class.Actor.init(self, t, no_default)
+	engine.interface.PlayerHotkeys.init(self, t)
+
+	self.descriptor = {}
+end
+
+function _M:move(x, y, force)
+	local moved = mod.class.Actor.move(self, x, y, force)
+	if moved then
+		game.level.map:moveViewSurround(self.x, self.y, 8, 8)
+	end
+	return moved
+end
+
+function _M:act()
+	if not mod.class.Actor.act(self) then return end
+
+	-- Clean log flasher
+	game.flash:empty()
+
+	-- Resting ? Running ? Otherwise pause
+	if not self:restStep() and not self:runStep() and self.player then
+		game.paused = true
+	end
+end
+
+function _M:playerFOV()
+	-- Clean FOV before computing it
+	game.level.map:cleanFOV()
+	-- Compute both the normal and the lite FOV, using cache
+	self:computeFOV(self.sight or 20, "block_sight", function(x, y, dx, dy, sqdist)
+		game.level.map:apply(x, y, math.max((20 - math.sqrt(sqdist)) / 14, 0.6))
+	end, true, false, true)
+	self:computeFOV(self.lite, "block_sight", function(x, y, dx, dy, sqdist) game.level.map:applyLite(x, y) end, true, true, true)
+end
+
+--- Called before taking a hit, overload mod.class.Actor:onTakeHit() to stop resting and running
+function _M:onTakeHit(value, src)
+	self:runStop("taken damage")
+	self:restStop("taken damage")
+	local ret = mod.class.Actor.onTakeHit(self, value, src)
+	if self.life < self.max_life * 0.3 then
+		local sx, sy = game.level.map:getTileToScreen(self.x, self.y)
+		game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, 2, "LOW HEALTH!", {255,0,0}, true)
+	end
+	return ret
+end
+
+function _M:die(src)
+	if self.game_ender then
+		engine.interface.ActorLife.die(self, src)
+		game.paused = true
+		self.energy.value = game.energy_to_act
+		game:registerDialog(DeathDialog.new(self))
+	else
+		mod.class.Actor.die(self, src)
+	end
+end
+
+function _M:setName(name)
+	self.name = name
+	game.save_name = name
+end
+
+--- Notify the player of available cooldowns
+function _M:onTalentCooledDown(tid)
+	local t = self:getTalentFromId(tid)
+
+	local x, y = game.level.map:getTileToScreen(self.x, self.y)
+	game.flyers:add(x, y, 30, -0.3, -3.5, ("%s available"):format(t.name:capitalize()), {0,255,00})
+	game.log("#00ff00#Talent %s is ready to use.", t.name)
+end
+
+function _M:levelup()
+	mod.class.Actor.levelup(self)
+
+	local x, y = game.level.map:getTileToScreen(self.x, self.y)
+	game.flyers:add(x, y, 80, 0.5, -2, "LEVEL UP!", {0,255,255})
+	game.log("#00ffff#Welcome to level %d.", self.level)
+end
+
+--- Tries to get a target from the user
+-- *WARNING* If used inside a coroutine it will yield and resume it later when a target is found.
+-- This is usualy just what you want so dont think too much about it :)
+function _M:getTarget(typ)
+	if coroutine.running() then
+		local msg
+		if type(typ) == "string" then msg, typ = typ, nil
+		elseif type(typ) == "table" then
+			if typ.default_target then game.target.target.entity = typ.default_target end
+			msg = typ.msg
+		end
+		game:targetMode("exclusive", msg, coroutine.running(), typ)
+		if typ.nolock then
+			game.target_style = "free"
+		end
+		return coroutine.yield()
+	end
+	return game.target.target.x, game.target.target.y, game.target.target.entity
+end
+
+--- Sets the current target
+function _M:setTarget(target)
+	game.target.target.entity = target
+	game.target.target.x = target.x
+	game.target.target.y = target.y
+end
+
+local function spotHostiles(self)
+	local seen = false
+	-- Check for visible monsters, only see LOS actors, so telepathy wont prevent resting
+	core.fov.calc_circle(self.x, self.y, 20, function(_, x, y) return game.level.map:opaque(x, y) end, function(_, x, y)
+		local actor = game.level.map(x, y, game.level.map.ACTOR)
+		if actor and self:reactionToward(actor) < 0 and self:canSee(actor) and game.level.map.seens(x, y) then seen = true end
+	end, nil)
+	return seen
+end
+
+--- Can we continue resting ?
+-- We can rest if no hostiles are in sight, and if we need life/mana/stamina (and their regen rates allows them to fully regen)
+function _M:restCheck()
+	if spotHostiles(self) then return false, "hostile spotted" end
+
+	-- Check ressources, make sure they CAN go up, otherwise we will never stop
+	if self:getPower() < self:getMaxPower() and self.power_regen > 0 then return true end
+	if self.life < self.max_life and self.life_regen> 0 then return true end
+
+	return false, "all resources and life at maximun"
+end
+
+--- Can we continue running?
+-- We can run if no hostiles are in sight, and if we no interresting terrains are next to us
+function _M:runCheck()
+	if spotHostiles(self) then return false, "hostile spotted" end
+
+	-- Notice any noticable terrain
+	local noticed = false
+	self:runScan(function(x, y)
+		-- Only notice interresting terrains
+		local grid = game.level.map(x, y, Map.TERRAIN)
+		if grid and grid.notice then noticed = "interesting terrain" end
+	end)
+	if noticed then return false, noticed end
+
+	self:playerFOV()
+
+	return engine.interface.PlayerRun.runCheck(self)
+end
+
+
+--- Runs to the clicked mouse spot
+-- if no monsters in sight it will try to make an A* path, if it fails it will do a direct path
+-- if there are monsters in sight it will move one stop in the direct path direction
+function _M:mouseMove(tmx, tmy)
+	if config.settings.tome.cheat and game.key.status[game.key._LSHIFT] and game.key.status[game.key._LCTRL] then
+		game.log("[CHEAT] teleport to %dx%d", tmx, tmy)
+		self:move(tmx, tmy, true)
+	else
+		-- If hostiles, attack!
+		if spotHostiles(self) or math.floor(core.fov.distance(self.x, self.y, tmx, tmy)) == 1 then
+			local l = line.new(self.x, self.y, tmx, tmy)
+			local nx, ny = l()
+			self:move(nx or self.x, ny or self.y)
+			return
+		end
+
+		local a = Astar.new(game.level.map, self)
+		local path = a:calc(self.x, self.y, tmx, tmy, true)
+		-- No Astar path ? jsut be dumb and try direct line
+		if not path then
+			local d = DirectPath.new(game.level.map, self)
+			path = d:calc(self.x, self.y, tmx, tmy, true)
+		end
+
+		if path then
+			-- Should we just try to move in the direction, aka: attack!
+			if path[1] and game.level.map:checkAllEntities(path[1].x, path[1].y, "block_move", self) then self:move(path[1].x, path[1].y) return end
+
+			 -- Insert the player coords, running needs to find the player
+			table.insert(path, 1, {x=self.x, y=self.y})
+
+			-- Move along the projected A* path
+			self:runFollow(path)
+		end
+	end
+end
diff --git a/game/modules/example/class/interface/Combat.lua b/game/modules/example/class/interface/Combat.lua
new file mode 100644
index 0000000000000000000000000000000000000000..947bb6444e18a282d2ae100d417008d5a1b77525
--- /dev/null
+++ b/game/modules/example/class/interface/Combat.lua
@@ -0,0 +1,65 @@
+-- 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
+
+require "engine.class"
+local DamageType = require "engine.DamageType"
+local Map = require "engine.Map"
+local Chat = require "engine.Chat"
+local Target = require "engine.Target"
+local Talents = require "engine.interface.ActorTalents"
+
+--- Interface to add ToME combat system
+module(..., package.seeall, class.make)
+
+--- Checks what to do with the target
+-- Talk ? attack ? displace ?
+function _M:bumpInto(target)
+	local reaction = self:reactionToward(target)
+	if reaction < 0 then
+		return self:attackTarget(target)
+	elseif reaction >= 0 then
+		if self.move_others then
+			-- Displace
+			game.level.map:remove(self.x, self.y, Map.ACTOR)
+			game.level.map:remove(target.x, target.y, Map.ACTOR)
+			game.level.map(self.x, self.y, Map.ACTOR, target)
+			game.level.map(target.x, target.y, Map.ACTOR, self)
+			self.x, self.y, target.x, target.y = target.x, target.y, self.x, self.y
+		end
+	end
+end
+
+--- Makes the death happen!
+function _M:attackTarget(target, damtype, mult, noenergy)
+	local speed, hit = nil, false
+
+	if self.combat then
+		local s, h = self:attackTargetWith(target, self.combat, damtype, mult)
+		speed = math.max(speed or 0, s)
+		hit = hit or h
+	end
+
+	-- We use up our own energy
+	if speed and not noenergy then
+		self:useEnergy(game.energy_to_act * speed)
+		self.did_energy = true
+	end
+
+	return hit
+end
diff --git a/game/modules/example/data/birth/descriptors.lua b/game/modules/example/data/birth/descriptors.lua
new file mode 100644
index 0000000000000000000000000000000000000000..8191fb93369a208dec4ee732b4b6c50d458a8d2a
--- /dev/null
+++ b/game/modules/example/data/birth/descriptors.lua
@@ -0,0 +1,48 @@
+-- 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
+
+newBirthDescriptor{
+	type = "base",
+	name = "base",
+	desc = {
+	},
+	experience = 1.0,
+
+	copy = {
+		max_level = 10,
+	},
+}
+
+newBirthDescriptor{
+	type = "role",
+	name = "Destroyer",
+	desc =
+	{
+		"Boom!",
+	},
+}
+
+newBirthDescriptor{
+	type = "role",
+	name = "Acid-maniac",
+	desc =
+	{
+		"Zshhhhhhhh!",
+	},
+}
diff --git a/game/modules/example/data/damage_types.lua b/game/modules/example/data/damage_types.lua
new file mode 100644
index 0000000000000000000000000000000000000000..9854ec9176e4c4e76d1e462edb6655b9911f3574
--- /dev/null
+++ b/game/modules/example/data/damage_types.lua
@@ -0,0 +1,53 @@
+-- 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
+
+-- The basic stuff used to damage a grid
+setDefaultProjector(function(src, x, y, type, dam)
+	local target = game.level.map(x, y, Map.ACTOR)
+	if target then
+		local flash = game.flash.NEUTRAL
+		if target == game.player then flash = game.flash.BAD end
+		if src == game.player then flash = game.flash.GOOD end
+
+		game.logSeen(target, flash, "%s hits %s for %s%0.2f %s damage#LAST#.", src.name:capitalize(), target.name, DamageType:get(type).text_color or "#aaaaaa#", dam, DamageType:get(type).name)
+		local sx, sy = game.level.map:getTileToScreen(x, y)
+		if target:takeHit(dam, src) then
+			if src == game.player or target == game.player then
+				game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, -3, "Kill!", {255,0,255})
+			end
+		else
+			if src == game.player then
+				game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, -3, tostring(-math.ceil(dam)), {0,255,0})
+			elseif target == game.player then
+				game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, -3, tostring(-math.ceil(dam)), {255,0,0})
+			end
+		end
+		return dam
+	end
+	return 0
+end)
+
+newDamageType{
+	name = "physical", type = "PHYSICAL",
+}
+
+-- Acid detroys potions
+newDamageType{
+	name = "acid", type = "ACID", text_color = "#GREEN#",
+}
diff --git a/game/modules/example/data/general/grids/basic.lua b/game/modules/example/data/general/grids/basic.lua
new file mode 100644
index 0000000000000000000000000000000000000000..6d14ec4987bcf837299356f63b8c6a22595e75eb
--- /dev/null
+++ b/game/modules/example/data/general/grids/basic.lua
@@ -0,0 +1,99 @@
+-- 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
+
+newEntity{
+	define_as = "UP_WILDERNESS",
+	name = "exit to the wilds",
+	display = '<', color_r=255, color_g=0, color_b=255,
+	always_remember = true,
+	notice = true,
+	change_level = 1,
+	change_zone = "wilderness",
+}
+
+newEntity{
+	define_as = "UP",
+	name = "previous level",
+	display = '<', color_r=255, color_g=255, color_b=0,
+	notice = true,
+	always_remember = true,
+	change_level = -1,
+}
+
+newEntity{
+	define_as = "DOWN",
+	name = "next level",
+	display = '>', color_r=255, color_g=255, color_b=0,
+	notice = true,
+	always_remember = true,
+	change_level = 1,
+}
+
+newEntity{
+	define_as = "FLOOR",
+	name = "floor", image = "terrain/marble_floor.png",
+	display = '.', color_r=255, color_g=255, color_b=255, back_color=colors.DARK_GREY,
+}
+
+newEntity{
+	define_as = "WALL",
+	name = "wall", image = "terrain/granite_wall1.png",
+	display = '#', color_r=255, color_g=255, color_b=255, back_color=colors.GREY,
+	always_remember = true,
+	does_block_move = true,
+	can_pass = {pass_wall=1},
+	block_sight = true,
+	air_level = -20,
+	dig = "FLOOR",
+}
+
+newEntity{
+	define_as = "DOOR",
+	name = "door", image = "terrain/granite_door1.png",
+	display = '+', color_r=238, color_g=154, color_b=77, back_color=colors.DARK_UMBER,
+	notice = true,
+	always_remember = true,
+	block_sight = true,
+	door_opened = "DOOR_OPEN",
+	dig = "DOOR_OPEN",
+}
+
+newEntity{
+	define_as = "DOOR_OPEN",
+	name = "open door", image = "terrain/granite_door1_open.png",
+	display = "'", color_r=238, color_g=154, color_b=77, back_color=colors.DARK_GREY,
+	always_remember = true,
+	door_closed = "DOOR",
+}
+
+newEntity{
+	define_as = "OLD_FLOOR",
+	name = "floor", image = "terrain/maze_floor.png",
+	display = '.', color_r=255, color_g=255, color_b=255, back_color=colors.DARK_GREY,
+}
+
+newEntity{
+	define_as = "OLD_WALL",
+	name = "wall", image = "terrain/granite_wall_lichen.png", back_color=colors.GREY,
+	display = '#', color_r=255, color_g=255, color_b=255,
+	always_remember = true,
+	does_block_move = true,
+	block_sight = true,
+	air_level = -20,
+}
diff --git a/game/modules/example/data/general/npcs/kobold.lua b/game/modules/example/data/general/npcs/kobold.lua
new file mode 100644
index 0000000000000000000000000000000000000000..e890bb8fdd6a5dda82192fd77ad36c3b0f946bd4
--- /dev/null
+++ b/game/modules/example/data/general/npcs/kobold.lua
@@ -0,0 +1,115 @@
+-- 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
+
+local Talents = require("engine.interface.ActorTalents")
+
+newEntity{ --rodent base
+	define_as = "BASE_NPC_RODENT",
+	type = "vermin", subtype = "rodent",
+	display = "r", color=colors.WHITE,
+	can_multiply = 2,
+	body = { INVEN = 10 },
+
+	autolevel = "warrior",
+	ai = "dumb_talented_simple", ai_state = { talent_in=3, },
+	energy = { mod=1 },
+	stats = { str=8, dex=15, mag=3, con=5 },
+	combat_armor = 1, combat_def = 1,
+	rank = 1,
+	size_category = 1,
+}
+
+newEntity{ base = "BASE_NPC_RODENT",
+	name = "giant white mouse", color=colors.WHITE,
+	level_range = {1, 3}, exp_worth = 1,
+	rarity = 4,
+	max_life = resolvers.rngavg(5,9),
+	combat = { dam=5, atk=15, apr=10 },
+}
+
+newEntity{ base = "BASE_NPC_RODENT",
+	name = "giant brown mouse", color=colors.UMBER,
+	level_range = {1, 3}, exp_worth = 1,
+	rarity = 4,
+	max_life = resolvers.rngavg(5,9),
+	combat = { dam=5, atk=15, apr=10 },
+}
+
+newEntity{ base = "BASE_NPC_RODENT",
+	name = "giant white rat", color=colors.WHITE,
+	level_range = {1, 4}, exp_worth = 1,
+	rarity = 5,
+	max_life = resolvers.rngavg(15,20),
+	combat = { dam=7, atk=15, apr=10 },
+}
+
+newEntity{ base = "BASE_NPC_RODENT",
+	name = "giant brown rat", color=colors.UMBER,
+	level_range = {1, 4}, exp_worth = 1,
+	rarity = 5,
+	max_life = resolvers.rngavg(15,20),
+	combat = { dam=7, atk=15, apr=10 },
+}
+
+newEntity{ base = "BASE_NPC_RODENT",
+	name = "giant rabbit", color=colors.UMBER,
+	desc = [[Kill the wabbit, kill the wabbit, kill the wabbbbbiiiiiit.]],
+	level_range = {1, 4}, exp_worth = 1,
+	rarity = 6,
+	max_life = resolvers.rngavg(20,30),
+	combat = { dam=8, atk=16, apr=10 },
+}
+
+newEntity{ base = "BASE_NPC_RODENT",
+	name = "giant crystal rat", color=colors.PINK,
+	desc = [[Instead of fur this rat has crystals growing on its back which provide extra protection.]],
+	level_range = {1, 5}, exp_worth = 1,
+	rarity = 6,
+	max_life = resolvers.rngavg(35,50),
+	combat = { dam=7, atk=15, apr=10 },
+	combat_armor = 4, combat_def = 2,
+}
+
+newEntity{ base = "BASE_NPC_RODENT",
+	name = "cute little bunny", color=colors.SALMON,
+	desc = [[It looks at you with cute little eyes before jumping at you with razor sharp teeth.]],
+	level_range = {1, 15}, exp_worth = 3,
+	rarity = 200,
+	max_life = resolvers.rngavg(15,20),
+	combat = { dam=50, atk=15, apr=10 },
+	combat_armor = 1, combat_def = 20,
+}
+
+newEntity{ base = "BASE_NPC_RODENT",
+	name = "giant grey mouse", color=colors.SLATE,
+	level_range = {1, 3}, exp_worth = 1,
+	rarity = 6,
+	max_life = resolvers.rngavg(5,9),
+	combat = { dam=5, atk=15, apr=10 },
+	resolvers.talents{ [Talents.T_CRAWL_POISON]=1 },
+}
+
+newEntity{ base = "BASE_NPC_RODENT",
+	name = "giant grey rat", color=colors.SLATE,
+	level_range = {1, 4}, exp_worth = 1,
+	rarity = 7,
+	max_life = resolvers.rngavg(15,20),
+	combat = { dam=7, atk=15, apr=10 },
+	resolvers.talents{ [Talents.T_CRAWL_POISON]=1 },
+}
diff --git a/game/modules/example/data/gfx/particles/acid.lua b/game/modules/example/data/gfx/particles/acid.lua
new file mode 100644
index 0000000000000000000000000000000000000000..6a5cfb89e9ed7fbe6b4974fb8b46c09d6b96d090
--- /dev/null
+++ b/game/modules/example/data/gfx/particles/acid.lua
@@ -0,0 +1,38 @@
+-- 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
+
+return {
+	base = 1000,
+
+	angle = { 0, 360 }, anglev = { 2000, 4000 }, anglea = { 200, 600 },
+
+	life = { 5, 10 },
+	size = { 3, 6 }, sizev = {0, 0}, sizea = {0, 0},
+
+	r = {0, 0}, rv = {0, 0}, ra = {0, 0},
+	g = {80, 200}, gv = {0, 10}, ga = {0, 0},
+	b = {0, 0}, bv = {0, 0}, ba = {0, 0},
+	a = {255, 255}, av = {0, 0}, aa = {0, 0},
+
+}, function(self)
+	self.nb = (self.nb or 0) + 1
+	if self.nb < 4 then
+		self.ps:emit(100)
+	end
+end
diff --git a/game/modules/example/data/rooms/pilar.lua b/game/modules/example/data/rooms/pilar.lua
new file mode 100644
index 0000000000000000000000000000000000000000..337f27805e2df9d456c181de2f8a668360a92ea4
--- /dev/null
+++ b/game/modules/example/data/rooms/pilar.lua
@@ -0,0 +1,26 @@
+-- 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
+
+return {
+[[#!!!!!!!!#]],
+[[!........!]],
+[[!...##...!]],
+[[!........!]],
+[[#!!!!!!!!#]],
+}
diff --git a/game/modules/example/data/rooms/simple.lua b/game/modules/example/data/rooms/simple.lua
new file mode 100644
index 0000000000000000000000000000000000000000..bcaf29ac196c8593c9c500c8c0f851024679549b
--- /dev/null
+++ b/game/modules/example/data/rooms/simple.lua
@@ -0,0 +1,37 @@
+-- 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
+
+return function(gen, id)
+	local w = rng.range(5, 12)
+	local h = rng.range(5, 12)
+	return { name="simple"..w.."x"..h, w=w, h=h, generator = function(self, x, y, is_lit)
+		for i = 1, self.w do
+			for j = 1, self.h do
+				if i == 1 or i == self.w or j == 1 or j == self.h then
+					gen.map.room_map[i-1+x][j-1+y].can_open = true
+					gen.map(i-1+x, j-1+y, Map.TERRAIN, gen.grid_list[gen:resolve('#')])
+				else
+					gen.map.room_map[i-1+x][j-1+y].room = id
+					gen.map(i-1+x, j-1+y, Map.TERRAIN, gen.grid_list[gen:resolve('.')])
+				end
+				if is_lit then gen.map.lites(i-1+x, j-1+y, true) end
+			end
+		end
+	end}
+end
diff --git a/game/modules/example/data/talents.lua b/game/modules/example/data/talents.lua
new file mode 100644
index 0000000000000000000000000000000000000000..da57bd68b205d77cc81be4d184b9a2d487f148f8
--- /dev/null
+++ b/game/modules/example/data/talents.lua
@@ -0,0 +1,34 @@
+-- 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
+
+newTalentType{ type="role/combat", name = "combat", description = "Combat techniques" }
+
+newTalent{
+	name = "Kick",
+	type = {"role/combat", 1},
+	points = 1,
+	cooldown = 6,
+	power = 2,
+	action = function(self, t)
+		return true
+	end,
+	info = function(self, t)
+		return "Kick!"
+	end,
+}
diff --git a/game/modules/example/data/zones/dungeon/grids.lua b/game/modules/example/data/zones/dungeon/grids.lua
new file mode 100644
index 0000000000000000000000000000000000000000..14dc047df0c83b59498fb0f62c673df2fbc8ca70
--- /dev/null
+++ b/game/modules/example/data/zones/dungeon/grids.lua
@@ -0,0 +1,20 @@
+-- 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
+
+load("/data/general/grids/basic.lua")
diff --git a/game/modules/example/data/zones/dungeon/npcs.lua b/game/modules/example/data/zones/dungeon/npcs.lua
new file mode 100644
index 0000000000000000000000000000000000000000..5e16d33fe1228c493e5dced7fa841113439e3137
--- /dev/null
+++ b/game/modules/example/data/zones/dungeon/npcs.lua
@@ -0,0 +1,20 @@
+-- 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
+
+--load("/data/general/npcs/rodent.lua")
diff --git a/game/modules/example/data/zones/dungeon/objects.lua b/game/modules/example/data/zones/dungeon/objects.lua
new file mode 100644
index 0000000000000000000000000000000000000000..cad1c5dde4ec112098297ecc1d9d7be26841d4f6
--- /dev/null
+++ b/game/modules/example/data/zones/dungeon/objects.lua
@@ -0,0 +1,18 @@
+-- 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
diff --git a/game/modules/example/data/zones/dungeon/traps.lua b/game/modules/example/data/zones/dungeon/traps.lua
new file mode 100644
index 0000000000000000000000000000000000000000..cad1c5dde4ec112098297ecc1d9d7be26841d4f6
--- /dev/null
+++ b/game/modules/example/data/zones/dungeon/traps.lua
@@ -0,0 +1,18 @@
+-- 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
diff --git a/game/modules/example/data/zones/dungeon/zone.lua b/game/modules/example/data/zones/dungeon/zone.lua
new file mode 100644
index 0000000000000000000000000000000000000000..d3b7a486ac662b17b140f6eba8b1422db88bcc90
--- /dev/null
+++ b/game/modules/example/data/zones/dungeon/zone.lua
@@ -0,0 +1,48 @@
+-- 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
+
+return {
+	name = "Old ruins",
+	level_range = {1, 1},
+	max_level = 10,
+	decay = {300, 800},
+	width = 50, height = 50,
+	persistant = "zone",
+	generator =  {
+		map = {
+			class = "engine.generator.map.Roomer",
+			nb_rooms = 10,
+			rooms = {"simple", "pilar"},
+			lite_room_chance = 100,
+			['.'] = "FLOOR",
+			['#'] = "WALL",
+			up = "UP",
+			down = "DOWN",
+			door = "DOOR",
+		},
+		actor = {
+			class = "engine.generator.actor.Random",
+			nb_npc = {20, 30},
+--			guardian = "SHADE_OF_ANGMAR", -- The gardian is set in the static map
+		},
+	},
+	levels =
+	{
+	},
+}
diff --git a/game/modules/example/dialogs/CharacterSheet.lua b/game/modules/example/dialogs/CharacterSheet.lua
new file mode 100644
index 0000000000000000000000000000000000000000..7ac799ccb31ec4d92f2a8194040c7182641f84b7
--- /dev/null
+++ b/game/modules/example/dialogs/CharacterSheet.lua
@@ -0,0 +1,279 @@
+-- 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
+
+require "engine.class"
+local Dialog = require "engine.Dialog"
+local DamageType = require "engine.DamageType"
+local Talents = require "engine.interface.ActorTalents"
+
+module(..., package.seeall, class.inherit(engine.Dialog))
+
+function _M:init(actor)
+	self.actor = actor
+	engine.Dialog.init(self, "Character Sheet: "..self.actor.name.." (Press 'd' to save)", 800, 400, nil, nil, nil, core.display.newFont("/data/font/VeraMono.ttf", 12))
+
+	self:keyCommands({
+		__TEXTINPUT = function(c)
+			if c == 'd' or c == 'D' then
+				self:dump()
+			end
+		end,
+	}, {
+		ACCEPT = "EXIT",
+		EXIT = function()
+			game:unregisterDialog(self)
+		end,
+	})
+end
+
+function _M:drawDialog(s)
+	local cur_exp, max_exp = game.player.exp, game.player:getExpChart(game.player.level+1)
+
+	local h = 0
+	local w = 0
+	s:drawString(self.font, "Sex:   "..game.player.descriptor.sex, w, h, 0, 200, 255) h = h + self.font_h
+	s:drawString(self.font, "Race:  "..game.player.descriptor.subrace, w, h, 0, 200, 255) h = h + self.font_h
+	s:drawString(self.font, "Class: "..game.player.descriptor.subclass, w, h, 0, 200, 255) h = h + self.font_h
+	h = h + self.font_h
+	s:drawColorString(self.font, "Level: #00ff00#"..game.player.level, w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("Exp:  #00ff00#%2d%%"):format(100 * cur_exp / max_exp), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("Gold: #00ff00#%0.2f"):format(game.player.money), w, h, 255, 255, 255) h = h + self.font_h
+
+	h = h + self.font_h
+
+	s:drawColorString(self.font, ("#c00000#Life:    #00ff00#%d/%d"):format(game.player.life, game.player.max_life), w, h, 255, 255, 255) h = h + self.font_h
+	if game.player:knowTalent(game.player.T_STAMINA_POOL) then
+		s:drawColorString(self.font, ("#ffcc80#Stamina: #00ff00#%d/%d"):format(game.player:getStamina(), game.player.max_stamina), w, h, 255, 255, 255) h = h + self.font_h
+	end
+	if game.player:knowTalent(game.player.T_MANA_POOL) then
+		s:drawColorString(self.font, ("#7fffd4#Mana:    #00ff00#%d/%d"):format(game.player:getMana(), game.player.max_mana), w, h, 255, 255, 255) h = h + self.font_h
+	end
+	if game.player:knowTalent(game.player.T_SOUL_POOL) then
+		s:drawColorString(self.font, ("#777777#Soul:    #00ff00#%d/%d"):format(game.player:getSoul(), game.player.max_soul), w, h, 255, 255, 255) h = h + self.font_h
+	end
+	if game.player:knowTalent(game.player.T_EQUILIBRIUM_POOL) then
+		s:drawColorString(self.font, ("#00ff74#Equi:    #00ff00#%d"):format(game.player:getEquilibrium()), w, h, 255, 255, 255) h = h + self.font_h
+	end
+
+	h = h + self.font_h
+	s:drawColorString(self.font, ("STR: #00ff00#%3d"):format(game.player:getStr()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("DEX: #00ff00#%3d"):format(game.player:getDex()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("MAG: #00ff00#%3d"):format(game.player:getMag()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("WIL: #00ff00#%3d"):format(game.player:getWil()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("CUN: #00ff00#%3d"):format(game.player:getCun()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("CON: #00ff00#%3d"):format(game.player:getCon()), w, h, 255, 255, 255) h = h + self.font_h
+
+	h = 0
+	w = 200
+	-- All weapons in main hands
+	if self.actor:getInven(self.actor.INVEN_MAINHAND) then
+		for i, o in ipairs(self.actor:getInven(self.actor.INVEN_MAINHAND)) do
+			if o.combat then
+				s:drawColorString(self.font, ("Attack(Main Hand): #00ff00#%3d"):format(game.player:combatAttack(o.combat)), w, h, 255, 255, 255) h = h + self.font_h
+				s:drawColorString(self.font, ("Damage(Main Hand): #00ff00#%3d"):format(game.player:combatDamage(o.combat)), w, h, 255, 255, 255) h = h + self.font_h
+				s:drawColorString(self.font, ("APR   (Main Hand): #00ff00#%3d"):format(game.player:combatAPR(o.combat)), w, h, 255, 255, 255) h = h + self.font_h
+				s:drawColorString(self.font, ("Crit  (Main Hand): #00ff00#%3d%%"):format(game.player:combatCrit(o.combat)), w, h, 255, 255, 255) h = h + self.font_h
+				s:drawColorString(self.font, ("Speed (Main Hand): #00ff00#%0.2f"):format(game.player:combatSpeed(o.combat)), w, h, 255, 255, 255) h = h + self.font_h
+			end
+		end
+	end
+	h = h + self.font_h
+	-- All wpeaons in off hands
+	-- Offhand atatcks are with a damage penality, taht can be reduced by talents
+	if self.actor:getInven(self.actor.INVEN_OFFHAND) then
+		local offmult = (mult or 1) / 2
+		if self.actor:knowTalent(Talents.T_DUAL_WEAPON_TRAINING) then
+			offmult = (mult or 1) / (2 - (self.actor:getTalentLevel(Talents.T_DUAL_WEAPON_TRAINING) / 6))
+		end
+		for i, o in ipairs(self.actor:getInven(self.actor.INVEN_OFFHAND)) do
+			if o.combat then
+				s:drawColorString(self.font, ("Attack (Off Hand): #00ff00#%3d"):format(game.player:combatAttack(o.combat)), w, h, 255, 255, 255) h = h + self.font_h
+				s:drawColorString(self.font, ("Damage (Off Hand): #00ff00#%3d"):format(game.player:combatDamage(o.combat) * offmult), w, h, 255, 255, 255) h = h + self.font_h
+				s:drawColorString(self.font, ("APR    (Off Hand): #00ff00#%3d"):format(game.player:combatAPR(o.combat)), w, h, 255, 255, 255) h = h + self.font_h
+				s:drawColorString(self.font, ("Crit   (Off Hand): #00ff00#%3d%%"):format(game.player:combatCrit(o.combat)), w, h, 255, 255, 255) h = h + self.font_h
+				s:drawColorString(self.font, ("Speed  (Off Hand): #00ff00#%0.2f"):format(game.player:combatSpeed(o.combat)), w, h, 255, 255, 255) h = h + self.font_h
+			end
+		end
+	end
+	h = h + self.font_h
+	s:drawColorString(self.font, ("Spellpower:  #00ff00#%3d"):format(game.player:combatSpellpower()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("Spell Crit:  #00ff00#%3d%%"):format(game.player:combatSpellCrit()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("Spell Speed: #00ff00#%3d"):format(game.player:combatSpellSpeed()), w, h, 255, 255, 255) h = h + self.font_h
+
+	h = h + self.font_h
+	for i, t in ipairs(DamageType.dam_def) do
+		if self.actor.inc_damage[DamageType[t.type]] and self.actor.inc_damage[DamageType[t.type]] ~= 0 then
+			s:drawColorString(self.font, ("%s damage: #00ff00#%3d%%"):format(t.name:capitalize(), self.actor.inc_damage[DamageType[t.type]]), w, h, 255, 255, 255) h = h + self.font_h
+		end
+	end
+
+	h = 0
+	w = 400
+	s:drawColorString(self.font, ("Fatigue:        #00ff00#%3d%%"):format(game.player.fatigue), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("Armor:          #00ff00#%3d"):format(game.player:combatArmor()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("Defence:        #00ff00#%3d"):format(game.player:combatDefense()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("Ranged Defence: #00ff00#%3d"):format(game.player:combatDefenseRanged()), w, h, 255, 255, 255) h = h + self.font_h
+
+	h = h + self.font_h
+	s:drawColorString(self.font, ("Physical Resist: #00ff00#%3d"):format(game.player:combatPhysicalResist()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("Spell Resist:    #00ff00#%3d"):format(game.player:combatSpellResist()), w, h, 255, 255, 255) h = h + self.font_h
+	s:drawColorString(self.font, ("Mental Resist:   #00ff00#%3d"):format(game.player:combatMentalResist()), w, h, 255, 255, 255) h = h + self.font_h
+
+	h = h + self.font_h
+	for i, t in ipairs(DamageType.dam_def) do
+		if self.actor.resists[DamageType[t.type]] and self.actor.resists[DamageType[t.type]] ~= 0 then
+			s:drawColorString(self.font, ("%s Resist: #00ff00#%3d%%"):format(t.name:capitalize(), self.actor.resists[DamageType[t.type]]), w, h, 255, 255, 255) h = h + self.font_h
+		end
+	end
+
+	h = 0
+	w = 600
+	s:drawColorString(self.font, "#LIGHT_BLUE#Current effects:", w, h, 255, 255, 255) h = h + self.font_h
+	for tid, act in pairs(game.player.sustain_talents) do
+		if act then s:drawColorString(self.font, ("#LIGHT_GREEN#%s"):format(game.player:getTalentFromId(tid).name), w, h, 255, 255, 255) h = h + self.font_h end
+	end
+	for eff_id, p in pairs(game.player.tmp) do
+		local e = game.player.tempeffect_def[eff_id]
+		if e.status == "detrimental" then
+			s:drawColorString(self.font, ("#LIGHT_RED#%s"):format(e.desc), w, h, 255, 255, 255) h = h + self.font_h
+		else
+			s:drawColorString(self.font, ("#LIGHT_GREEN#%s"):format(e.desc), w, h, 255, 255, 255) h = h + self.font_h
+		end
+	end
+
+	self.changed = false
+end
+
+function _M:dump()
+	fs.mkdir("/character-dumps")
+	local file = "/character-dumps/"..(game.player.name:gsub("[^a-zA-Z0-9_-.]", "_")).."-"..os.date("%Y%m%d-%H%M%S")..".txt"
+	local fff = fs.open(file, "w")
+	local nl = function(s) fff:write(s or "") fff:write("\n") end
+	local nnl = function(s) fff:write(s or "") end
+
+	nl("Sex:   "..game.player.descriptor.sex)
+	nl("Race:  "..game.player.descriptor.subrace)
+	nl("Class: "..game.player.descriptor.subclass)
+	nl("Level: "..game.player.level)
+
+	nl()
+	local cur_exp, max_exp = game.player.exp, game.player:getExpChart(game.player.level+1)
+	nl(("Exp:  %2d%%"):format(100 * cur_exp / max_exp))
+	nl(("Gold: %0.2f"):format(game.player.money))
+
+	nl()
+	nl(("Life:    %d/%d"):format(game.player.life, game.player.max_life))
+	if game.player:knowTalent(game.player.T_STAMINA_POOL) then
+		nl(("Stamina: %d/%d"):format(game.player:getStamina(), game.player.max_stamina))
+	end
+	if game.player:knowTalent(game.player.T_MANA_POOL) then
+		nl(("Mana:    %d/%d"):format(game.player:getMana(), game.player.max_mana))
+	end
+	if game.player:knowTalent(game.player.T_SOUL_POOL) then
+		nl(("Soul:    %d/%d"):format(game.player:getSoul(), game.player.max_soul))
+	end
+	if game.player:knowTalent(game.player.T_EQUILIBRIUM_POOL) then
+		nl(("Equi:    %d"):format(game.player:getEquilibrium()))
+	end
+
+	nl()
+	nl(("STR: %3d"):format(game.player:getStr()))
+	nl(("DEX: %3d"):format(game.player:getDex()))
+	nl(("MAG: %3d"):format(game.player:getMag()))
+	nl(("WIL: %3d"):format(game.player:getWil()))
+	nl(("CUN: %3d"):format(game.player:getCun()))
+	nl(("CON: %3d"):format(game.player:getCon()))
+
+	-- All weapons in main hands
+	if self.actor:getInven(self.actor.INVEN_MAINHAND) then
+		for i, o in ipairs(self.actor:getInven(self.actor.INVEN_MAINHAND)) do
+			if o.combat then
+				nl()
+				nl(("Attack(Main Hand): %3d"):format(game.player:combatAttack(o.combat)))
+				nl(("Damage(Main Hand): %3d"):format(game.player:combatDamage(o.combat)))
+				nl(("APR   (Main Hand): %3d"):format(game.player:combatAPR(o.combat)))
+				nl(("Crit  (Main Hand): %3d%%"):format(game.player:combatCrit(o.combat)))
+				nl(("Speed (Main Hand): %0.2f"):format(game.player:combatSpeed(o.combat)))
+			end
+		end
+	end
+
+	-- All wpeaons in off hands
+	-- Offhand atatcks are with a damage penality, taht can be reduced by talents
+	if self.actor:getInven(self.actor.INVEN_OFFHAND) then
+		local offmult = (mult or 1) / 2
+		if self.actor:knowTalent(Talents.T_DUAL_WEAPON_TRAINING) then
+			offmult = (mult or 1) / (2 - (self.actor:getTalentLevel(Talents.T_DUAL_WEAPON_TRAINING) / 6))
+		end
+		for i, o in ipairs(self.actor:getInven(self.actor.INVEN_OFFHAND)) do
+			if o.combat then
+				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(("Speed  (Off Hand): %0.2f"):format(game.player:combatSpeed(o.combat)))
+			end
+		end
+	end
+
+	nl()
+	nl(("Spellpower:  %3d"):format(game.player:combatSpellpower()))
+	nl(("Spell Crit:  %3d%%"):format(game.player:combatSpellCrit()))
+	nl(("Spell Speed: %3d"):format(game.player:combatSpellSpeed()))
+
+	nl()
+	for i, t in ipairs(DamageType.dam_def) do
+		if self.actor.inc_damage[DamageType[t.type]] and self.actor.inc_damage[DamageType[t.type]] ~= 0 then
+			nl(("%s damage: %3d%%"):format(t.name:capitalize(), self.actor.inc_damage[DamageType[t.type]]))
+		end
+	end
+
+	nl()
+	nl(("Fatigue:        %3d%%"):format(game.player.fatigue))
+	nl(("Armor:          %3d"):format(game.player:combatArmor()))
+	nl(("Defence:        %3d"):format(game.player:combatDefense()))
+	nl(("Ranged Defence: %3d"):format(game.player:combatDefenseRanged()))
+
+	nl()
+	nl(("Physical Resist: %3d"):format(game.player:combatPhysicalResist()))
+	nl(("Spell Resist:    %3d"):format(game.player:combatSpellResist()))
+	nl(("Mental Resist:   %3d"):format(game.player:combatMentalResist()))
+
+	nl()
+	for i, t in ipairs(DamageType.dam_def) do
+		if self.actor.resists[DamageType[t.type]] and self.actor.resists[DamageType[t.type]] ~= 0 then
+			nl(("%s Resist: %3d%%"):format(t.name:capitalize(), self.actor.resists[DamageType[t.type]]))
+		end
+	end
+
+	nl()
+	local most_kill, most_kill_max = "none", 0
+	local total_kill = 0
+	for name, nb in pairs(game.player.all_kills or {}) do
+		if nb > most_kill_max then most_kill_max = nb most_kill = name end
+		total_kill = total_kill + nb
+	end
+	nl(("Number of NPC killed: %s"):format(total_kill))
+	nl(("Most killed NPC: %s (%d)"):format(most_kill, most_kill_max))
+
+	fff:close()
+
+	Dialog:simplePopup("Character dump complete", "File: "..fs.getRealPath(file))
+end
diff --git a/game/modules/example/dialogs/DeathDialog.lua b/game/modules/example/dialogs/DeathDialog.lua
new file mode 100644
index 0000000000000000000000000000000000000000..6d1a7030b734e4701307ba7df4126737e892ed7f
--- /dev/null
+++ b/game/modules/example/dialogs/DeathDialog.lua
@@ -0,0 +1,178 @@
+-- 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
+
+require "engine.class"
+require "engine.Dialog"
+local Savefile = require "engine.Savefile"
+local Map = require "engine.Map"
+
+module(..., package.seeall, class.inherit(engine.Dialog))
+
+function _M:init(actor)
+	self.actor = actor
+	engine.Dialog.init(self, "Death!", 500, 300)
+
+	self:generateList()
+
+	self.sel = 1
+	self.scroll = 1
+	self.max = math.floor((self.ih - 45) / self.font_h) - 1
+
+	self:keyCommands(nil, {
+		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=2, y=10 + self.font:lineSkip()*6, 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 == "left" then self:use()
+			end
+			self.changed = true
+		end },
+	}
+end
+
+--- Clean the actor from debuffs/buffs
+function _M:cleanActor()
+	local effs = {}
+
+	-- Go through all spell effects
+	for eff_id, p in pairs(self.actor.tmp) do
+
+		local e = self.actor.tempeffect_def[eff_id]
+		effs[#effs+1] = {"effect", eff_id}
+	end
+
+	-- Go through all sustained spells
+	for tid, act in pairs(self.actor.sustain_talents) do
+		if act then
+			effs[#effs+1] = {"talent", tid}
+		end
+	end
+
+	while #effs > 0 do
+		local eff = rng.tableRemove(effs)
+
+		if eff[1] == "effect" then
+			self.actor:removeEffect(eff[2])
+		else
+			local old = self.actor.energy.value
+			self.actor:useTalent(eff[2])
+			-- Prevent using energy
+			self.actor.energy.value = old
+		end
+	end
+end
+
+--- Restore ressources
+function _M:restoreRessources()
+	self.actor.life = self.actor.max_life
+	self.actor.mana = self.actor.max_mana
+	self.actor.stamina = self.actor.max_stamina
+	self.actor.equilibrium = 0
+	self.actor.air = self.actor.max_air
+
+	self.actor.energy.value = game.energy_to_act
+end
+
+--- Basic resurection
+function _M:resurrectBasic()
+	self.actor.dead = false
+	self.actor.died = (self.actor.died or 0) + 1
+
+	local x, y = util.findFreeGrid(self.actor.x, self.actor.y, 20, true, {[Map.ACTOR]=true})
+	if not x then x, y = self.actor.x, self.actor.y end
+	self.actor.x, self.actor.y = nil, nil
+
+	self.actor:move(x, y, true)
+	game.level:addEntity(self.actor)
+	game:unregisterDialog(self)
+	game.level.map:redisplay()
+
+	world:gainAchievement("UNSTOPPABLE", self.actor)
+end
+
+function _M:use()
+	if not self.list[self.sel] then return end
+	local act = self.list[self.sel].action
+
+	if act == "exit" then
+		local save = Savefile.new(game.save_name)
+		save:delete()
+		save:close()
+		util.showMainMenu()
+	elseif act == "dump" then
+		game:registerDialog(require("mod.dialogs.CharacterSheet").new(self.actor))
+	elseif act == "cheat" then
+		game.logPlayer(self.actor, "#LIGHT_BLUE#You resurrect! CHEATER !")
+
+		self:cleanActor()
+		self:restoreRessources()
+		self:resurrectBasic()
+	elseif act == "blood_life" then
+		self.actor.blood_life = false
+		game.logPlayer(self.actor, "#LIGHT_RED#The Blood of Life rushes through your dead body. You come back to life!")
+
+		self:cleanActor()
+		self:restoreRessources()
+		self:resurrectBasic()
+	elseif act == "skeleton" then
+		self.actor:attr("re-assembled", 1)
+		game.logPlayer(self.actor, "#YELLOW#Your bones magically come back together. You are once more able to dish pain to your foes!")
+
+		self:cleanActor()
+		self:restoreRessources()
+		self:resurrectBasic()
+	end
+end
+
+function _M:generateList()
+	local list = {}
+
+	if config.settings.tome.cheat then list[#list+1] = {name="Resurrect by cheating", action="cheat"} end
+	if self.actor:attr("blood_life") and not self.actor:attr("undead") then list[#list+1] = {name="Resurrect with the Blood of Life", action="blood_life"} end
+	if self.actor:getTalentLevelRaw(self.actor.T_SKELETON_REASSEMBLE) >= 5 and not self.actor:attr("re-assembled") then list[#list+1] = {name="Re-assemble your bones ad resurrect (Skeleton ability)", action="skeleton"} end
+
+	list[#list+1] = {name="Character dump", action="dump"}
+	list[#list+1] = {name="Exit to main menu", action="exit"}
+
+	self.list = list
+end
+
+function _M:drawDialog(s)
+	local help = ([[You have #LIGHT_RED#died#LAST#!
+Death in T.o.M.E. is usually permanent, but if you have a means of resurrection it will be proposed in the menu below.
+You can dump your character data to a file to remember her/him forever, or you can exit and try again to survive in the wilds!
+]]):splitLines(self.iw - 10, self.font)
+
+	local h = 2
+	local r, g, b
+	for i = 1, #help do
+		r, g, b = s:drawColorString(self.font, help[i], 5, h, r, g, b)
+		h = h + self.font:lineSkip()
+	end
+	h = h + self.font:lineSkip()
+
+	self:drawWBorder(s, 2, h - 0.5 * self.font:lineSkip(), self.iw - 4)
+
+	self:drawSelectionList(s, 2, h, self.font_h, self.list, self.sel, "name")
+	self.changed = false
+end
diff --git a/game/modules/example/dialogs/Quit.lua b/game/modules/example/dialogs/Quit.lua
new file mode 100644
index 0000000000000000000000000000000000000000..84b0303f1cf3c372deeec5058afa944a4f98943f
--- /dev/null
+++ b/game/modules/example/dialogs/Quit.lua
@@ -0,0 +1,45 @@
+-- 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
+
+require "engine.class"
+require "engine.Dialog"
+local Savefile = require "engine.Savefile"
+
+module(..., package.seeall, class.inherit(engine.Dialog))
+
+function _M:init()
+	engine.Dialog.init(self, "Realy exit ToME?", 300, 100)
+	self:keyCommands({
+		__DEFAULT = function()
+			game:unregisterDialog(self)
+			game.quit_dialog = false
+		end,
+	}, {
+		ACCEPT = function()
+			local save = Savefile.new(game.save_name)
+			save:saveGame(game)
+			save:close()
+			util.showMainMenu()
+		end,
+	})
+end
+
+function _M:drawDialog(s, w, h)
+	s:drawColorStringCentered(self.font, "Press enter to quit, any other keys to stay", 2, 2, self.iw - 2, self.ih - 2)
+end
diff --git a/game/modules/example/init.lua b/game/modules/example/init.lua
new file mode 100644
index 0000000000000000000000000000000000000000..a2c541f3a311fc936d5e81479efc1c766d009583
--- /dev/null
+++ b/game/modules/example/init.lua
@@ -0,0 +1,30 @@
+-- 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
+
+name = "Example Module"
+long_name = "Example Module for T-Engine4"
+short_name = "example"
+author = { "DarkGod", "darkgod@te4.org" }
+homepage = "http://te4.org/modules:example"
+version = {1,0,0}
+engine = {1,0,0}
+description = [[
+This is *NOT* a game, just an example/template to make your own using the T-Engine4.
+]]
+starter = "mod.load"
diff --git a/game/modules/example/load.lua b/game/modules/example/load.lua
new file mode 100644
index 0000000000000000000000000000000000000000..8d85cb226c71438c3dca6962d912887991d04575
--- /dev/null
+++ b/game/modules/example/load.lua
@@ -0,0 +1,53 @@
+-- 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
+
+-- This file loads the game module, and loads data
+local KeyBind = require "engine.KeyBind"
+local DamageType = require "engine.DamageType"
+local ActorStats = require "engine.interface.ActorStats"
+local ActorResource = require "engine.interface.ActorResource"
+local ActorTalents = require "engine.interface.ActorTalents"
+local ActorAI = require "engine.interface.ActorAI"
+local ActorLevel = require "engine.interface.ActorLevel"
+local Birther = require "engine.Birther"
+
+-- Usefull keybinds
+KeyBind:load("move,hotkeys,inventory,actions,debug")
+
+-- Damage types
+DamageType:loadDefinition("/data/damage_types.lua")
+
+-- Talents
+ActorTalents:loadDefinition("/data/talents.lua")
+
+-- Actor resources
+ActorResource:defineResource("Power", "power", nil, "power_regen", "Power represent your ability to use special talents.")
+
+-- Actor stats
+ActorStats:defineStat("Strength",	"str", 10, 1, 100, "Strength defines your character's ability to apply physical force. It increases your melee damage, damage with heavy weapons, your chance to resist physical effects, and carrying capacity.")
+ActorStats:defineStat("Dexterity",	"dex", 10, 1, 100, "Dexterity defines your character's ability to be agile and alert. It increases your chance to hit, your ability to avoid attacks and your damage with light weapons.")
+ActorStats:defineStat("Constitution",	"con", 10, 1, 100, "Constitution defines your character's ability to withstand and resist damage. It increases your maximun life and physical resistance.")
+
+-- Actor AIs
+ActorAI:loadDefinition("/engine/ai/")
+
+-- Birther descriptor
+Birther:loadDefinition("/data/birth/descriptors.lua")
+
+return {require "mod.class.Game" }