diff --git a/game/engine/Actor.lua b/game/engine/Actor.lua
index e60aaa939764263f90243cdcf73cf2834aedf84b..b042c6c872508eb56ea8651e58e42acc1728285d 100644
--- a/game/engine/Actor.lua
+++ b/game/engine/Actor.lua
@@ -57,7 +57,7 @@ function _M:move(x, y, force)
 	if y >= map.h then y = map.h - 1 end
 	self.x, self.y = x, y
 	map(x, y, Map.ACTOR, self)
-	map:checkAllEntities(x, y, "on_move", self)
+	map:checkAllEntities(x, y, "on_move", self, force)
 	game.level:idleProcessActor(self)
 
 	return true
diff --git a/game/engine/Entity.lua b/game/engine/Entity.lua
index 1cdb0f86670c6df0acbd1591675538fd0ca32f5f..32eb56e533104241311c2d9fd0605912565cf640 100644
--- a/game/engine/Entity.lua
+++ b/game/engine/Entity.lua
@@ -114,6 +114,9 @@ function _M:resolve(t)
 
 	-- Finish resolving stuff
 	if t == self then
+		-- Handle ided if possible
+		if self.resolveIdentify then self:resolveIdentify() end
+
 		if self.resolveLevel then self:resolveLevel() end
 
 		if self.unique and type(self.unique) == "boolean" then
diff --git a/game/engine/Object.lua b/game/engine/Object.lua
index 1e2ae81b584694cb0225f8323b1bbbb9528454f6..7a5f942eb102938b2332d2d142600d03b04c4f8e 100644
--- a/game/engine/Object.lua
+++ b/game/engine/Object.lua
@@ -16,13 +16,6 @@ end
 --- Resolves the object
 -- This will call the entities resolver and then add to the game entities list
 function _M:resolve(t)
-	if not t then
-		-- Handle ided if possible
-		if self.resolveIdentify then
-			self:resolveIdentify()
-		end
-	end
-
 	engine.Entity.resolve(self, t)
 
 	if not t then
diff --git a/game/engine/Savefile.lua b/game/engine/Savefile.lua
index 1e10415592ede6a66da38a5efe198f2cc0341e1a..e2c24fa9cf95930c2d1999d6f3aeca209449e108 100644
--- a/game/engine/Savefile.lua
+++ b/game/engine/Savefile.lua
@@ -78,6 +78,8 @@ end
 
 --- Save the given game
 function _M:saveGame(game)
+	collectgarbage("collect")
+
 	fs.mkdir(self.save_dir)
 
 	local popup = Dialog:simplePopup("Saving game", "Please wait while saving the game...")
@@ -128,7 +130,7 @@ end
 function _M:loadReal(load)
 	if self.loaded[load] then return self.loaded[load] end
 	local f = fs.open(self.load_dir..load, "r")
---	print("loading", load)
+	print("loading", load)
 	local lines = {}
 	while true do
 		local l = f:read()
diff --git a/game/engine/Trap.lua b/game/engine/Trap.lua
index f7a1da48e5ed4aa0a206d1e3d9c386069a533fce..4c4d7764ad5d83c7bdfea3828c32166c04c9b481 100644
--- a/game/engine/Trap.lua
+++ b/game/engine/Trap.lua
@@ -1,5 +1,6 @@
 require "engine.class"
 local Entity = require "engine.Entity"
+local Map = require "engine.Map"
 
 --- Describes a trap
 module(..., package.seeall, class.inherit(Entity))
@@ -15,6 +16,9 @@ function _M:init(t, no_default)
 		self.disarmable = true
 	end
 
+	self.detect_power = self.detect_power or 1
+	self.disarm_power = self.disarm_power or 1
+
 	self.known_by = {}
 	self:loaded()
 end
@@ -24,6 +28,12 @@ function _M:loaded()
 	setmetatable(self.known_by, {__mode="k"})
 end
 
+--- Get trap name
+-- Can be overloaded to do trap identification if needed
+function _M:getName()
+	return self.name
+end
+
 --- Setup the trap
 function _M:setup()
 end
@@ -35,96 +45,41 @@ end
 
 --- Get the known status for the given actor
 function _M:knownBy(actor)
-	print("actor", actor.name, "knows", self.name)
 	return self.known_by[actor]
 end
 
---- Try to disarm the trap
-function _M:disarm(x, y, who)
+--- Can we disarm this trap?
+function _M:canDisarm(x, y, who)
 	if not self.disarmable then return false end
-end
-
---- Trigger the trap
-function _M:trigger(x, y, who)
-	if not self.message then
-		game.logSeen(who, "%s triggers a trap (%s)!", who.name:capitalize(), self.name)
-	else
-		local tname = who.name
-		local str =self.message
-		str = str:gsub("@target@", tname)
-		str = str:gsub("@Target@", tname:capitalize())
-		game.logSeen(who, "%s", str)
-	end
-	if self:triggered(x, y, who) then
-		self:knownBy(who, true)
-		game.level.map:updateMap(x, y)
-	end
-end
-
---- When moving on a trap, trigger it
-function _M:on_move(x, y, who)
-	self:trigger(x, y, who)
-end
-
---[=[
-require "engine.class"
-local Entity = require "engine.Entity"
-
---- Describes a trap
-module(..., package.seeall, class.inherit(Entity))
-
-function _M:init(t, no_default)
-	t = t or {}
-
-	assert(t.triggered, "no trap triggered action")
-
-	Entity.init(self, t, no_default)
-
-	if self.disarmable == nil then
-		self.disarmable = true
-	end
-
-	self.known_by_list = {}
-	self:loaded()
-end
-
-function _M:loaded()
-	-- known_by table is a weak table on keys, so taht it does not prevent garbage collection of actors
-	self.known_by = {}
-	setmetatable(self.known_by, {__mode="k"})
-	setmetatable(self.known_by_list, {__mode="v"})
-
-	-- Restore
-	for i, a in ipairs(self.known_by_list) self.known_by[a] = true end
-end
-
---- Setup the trap
-function _M:setup()
-end
-
---- Set the known status for the given actor
-function _M:setKnown(actor)
-	print("actor", actor.name, "knows", self.name)
-	if not self.known_by[actor] then
-		self.known_by[actor] = true
-		table.insert(self.known_by_list, actor)
-	end
-end
-
---- Get the known status for the given actor
-function _M:knownBy(actor)
-	return self.known_by[actor]
+	return true
 end
 
 --- Try to disarm the trap
 function _M:disarm(x, y, who)
-	if not self.disarmable then return false end
+	if not self:canDisarm(x, y, who) then
+		game.logSeen(who, "%s fails to disarm a trap (%s).", who.name:capitalize(), self:getName())
+		return false
+	end
+	game.level.map:remove(x, y, Map.TRAP)
+	if self.removed then
+		self:removed(x, y, who)
+	end
+	game.logSeen(who, "%s disarms a trap (%s).", who.name:capitalize(), self:getName())
+	return true
 end
 
 --- Trigger the trap
 function _M:trigger(x, y, who)
+	-- Try to disarm
+	if self:knownBy(who) then
+		-- Try to disarm
+		if self:disarm(x, y, who) then
+			return
+		end
+	end
+
 	if not self.message then
-		game.logSeen(who, "%s triggers a trap (%s)!", who.name:capitalize(), self.name)
+		game.logSeen(who, "%s triggers a trap (%s)!", who.name:capitalize(), self:getName())
 	else
 		local tname = who.name
 		local str =self.message
@@ -133,14 +88,12 @@ function _M:trigger(x, y, who)
 		game.logSeen(who, "%s", str)
 	end
 	if self:triggered(x, y, who) then
-		self.known_by[who.uid] = true
+		self:setKnown(who, true)
 		game.level.map:updateMap(x, y)
 	end
 end
 
 --- When moving on a trap, trigger it
-function _M:on_move(x, y, who)
-	self:trigger(x, y, who)
+function _M:on_move(x, y, who, forced)
+	if not forced then self:trigger(x, y, who) end
 end
-
---]=]
\ No newline at end of file
diff --git a/game/engine/Zone.lua b/game/engine/Zone.lua
index 4fc534748200b03f90dd24cbddeb500b77681886..785c5a4df4a73f83a0c8670a63c64aea0debd0f8 100644
--- a/game/engine/Zone.lua
+++ b/game/engine/Zone.lua
@@ -191,7 +191,7 @@ function _M:finishEntity(level, type, e, ego_chance)
 			end
 			print("applying ego", ego.name, "to ", e.name, "::", newname)
 			ego.unided_name = nil
-			table.merge(e, ego, true)
+			table.mergeAdd(e, ego, true)
 			e.name = newname
 			e.egoed = true
 		end
diff --git a/game/engine/class.lua b/game/engine/class.lua
index b83f822eea6afd5edc1323c8b1cf3c45c44ba724..32fd2b2cd6a0e8582d3b6ce16a633da9de176fa3 100644
--- a/game/engine/class.lua
+++ b/game/engine/class.lua
@@ -124,6 +124,7 @@ local function serialize_data(outf, name, value, saved, filter, allow, savefile,
 					local fieldname
 					-- Special case to handle index by objects
 					if type(k) == "table" and k.__CLASSNAME then
+						savefile:addToProcess(k)
 						fieldname = string.format("%s[loadObject('%s')]", name, savefile:getFileName(k))
 					else
 						fieldname = string.format("%s[%s]", name, basicSerialize(k))
diff --git a/game/engine/interface/ObjectIdentify.lua b/game/engine/interface/ObjectIdentify.lua
index 173905ba6e719455c9db0ec942d91234076c3f55..a7e7e31a0cbe2346c59f15a2d793391c7606c48b 100644
--- a/game/engine/interface/ObjectIdentify.lua
+++ b/game/engine/interface/ObjectIdentify.lua
@@ -27,8 +27,8 @@ end
 --- Is the object identified ?
 function _M:isIdentified()
 	-- Auto id by type ?
-	if game.object_known_types and game.object_known_types[self.type] and game.object_known_types[self.type][self.subtype] then
-		self.identified = game.object_known_types[self.type][self.subtype]
+	if game.object_known_types and game.object_known_types[self.type] and game.object_known_types[self.type][self.subtype] and game.object_known_types[self.type][self.subtype][self.name] then
+		self.identified = game.object_known_types[self.type][self.subtype][self.name]
 	end
 
 	return self.identified
@@ -40,7 +40,8 @@ function _M:identify(id)
 	if self.id_by_type then
 		game.object_known_types = game.object_known_types or {}
 		game.object_known_types[self.type] = game.object_known_types[self.type] or {}
-		game.object_known_types[self.type][self.subtype] = id
+		game.object_known_types[self.type][self.subtype] = game.object_known_types[self.type][self.subtype] or {}
+		game.object_known_types[self.type][self.subtype][self.name] = id
 	end
 end
 
diff --git a/game/engine/utils.lua b/game/engine/utils.lua
index 8f652a4f1b675b8b0f66736c0a11636331507c13..b417963a780d384abea7504e107a6be69f22b61b 100644
--- a/game/engine/utils.lua
+++ b/game/engine/utils.lua
@@ -23,6 +23,20 @@ function table.merge(dst, src, deep)
 	end
 end
 
+function table.mergeAdd(dst, src, deep)
+	for k, e in pairs(src) do
+		if deep and dst[k] and type(e) == "table" and type(dst[k]) == "table" and not e.__CLASSNAME then
+			table.merge(dst[k], e, true)
+		elseif deep and not dst[k] and type(e) == "table" and not e.__CLASSNAME then
+			dst[k] = table.clone(e, true)
+		elseif dst[k] and type(e) == "number" then
+			dst[k] = dst[k] + e
+		else
+			dst[k] = e
+		end
+	end
+end
+
 function string.ordinal(number)
 	local suffix = "th"
 	number = tonumber(number)
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 9b638718f8855f60fae4ec02fb9ce964c8443f2a..df6ee46ecc4608d890f033dfa1bb76716659d4ed 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -126,6 +126,21 @@ function _M:move(x, y, force)
 		if not force and moved and not self.did_energy then self:useEnergy() end
 	end
 	self.did_energy = nil
+
+	-- Try to detect traps
+	if self:knowTalent(self.T_TRAP_DETECTION) then
+		local power = self:getTalentLevel(self.T_TRAP_DETECTION) * self:getCun(25)
+		local grids = core.fov.circle_grids(self.x, self.y, 1, true)
+		for x, yy in pairs(grids) do for y, _ in pairs(yy) do
+			local trap = game.level.map(x, y, Map.TRAP)
+			if trap and not trap:knownBy(self) and self:checkHit(power, trap.detect_power) then
+				trap:setKnown(self, true)
+				game.level.map:updateMap(x, y)
+				game.logPlayer(self, "You have found a trap (%s)!", trap:getName())
+			end
+		end end
+	end
+
 	return moved
 end
 
diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua
index d3922ab3961ca150a5dfe66262ec91f344eca220..2c8a7486a48d5d2ea107508021c67fc894260c66 100644
--- a/game/modules/tome/class/Player.lua
+++ b/game/modules/tome/class/Player.lua
@@ -189,6 +189,10 @@ function _M:runCheck()
 		-- Objects are always interresting
 		local obj = game.level.map:getObject(x, y, 1)
 		if obj then noticed = "object seen" end
+
+		-- Traps are always interresting if known
+		local trap = game.level.map(x, y, Map.TRAP)
+		if trap and trap:knownBy(self) then noticed = "trap spotted" end
 	end)
 	if noticed then return false, noticed end
 
diff --git a/game/modules/tome/class/Trap.lua b/game/modules/tome/class/Trap.lua
index 3aa8e02c772f000f740fb39e314a7f5193ccfe17..70ac47dda42b5e5d2cb9c8a9cb48e796ed54ae2b 100644
--- a/game/modules/tome/class/Trap.lua
+++ b/game/modules/tome/class/Trap.lua
@@ -1,15 +1,43 @@
 require "engine.class"
 require "engine.Trap"
+require "engine.interface.ObjectIdentify"
 
 module(..., package.seeall, class.inherit(
-	engine.Trap
+	engine.Trap,
+	engine.interface.ObjectIdentify
 ))
 
 function _M:init(t, no_default)
 	engine.Trap.init(self, t, no_default)
+	engine.interface.ObjectIdentify.init(self, t)
+end
+
+--- Gets the full name of the object
+function _M:getName()
+	local name = self.name
+	if not self:isIdentified() and self:getUnidentifiedName() then name = self:getUnidentifiedName() end
+	return name
 end
 
 --- Returns a tooltip for the trap
 function _M:tooltip()
-	return self.name
+	if self:knownBy(game.player) then
+		return self:getName()
+	end
+end
+
+--- Can we disarm this trap?
+function _M:canDisarm(x, y, who)
+	if not engine.Trap.canDisarm(self, x, y, who) then return false end
+
+	-- do we know how to disarm?
+	if who:knowTalent(who.T_TRAP_DISARM) then
+		local power = who:getTalentLevel(who.T_TRAP_DISARM) * who:getCun(25)
+		if who:checkHit(power, self.disarm_power) then
+			return true
+		end
+	end
+
+	-- False by default
+	return false
 end
diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua
index bd692b2868d1f3095465f2b645f1df928af6ba2c..536f3551fe664b19f4a880daf4a05c017b0832a8 100644
--- a/game/modules/tome/class/interface/Combat.lua
+++ b/game/modules/tome/class/interface/Combat.lua
@@ -353,7 +353,7 @@ function _M:hasHeavyArmor()
 	if not armor or armor.subtype ~= "heavy" then
 		return nil
 	end
-	return shield
+	return armor
 end
 
 --- Check if the actor has a massive armor
@@ -363,5 +363,5 @@ function _M:hasMassiveArmor()
 	if not armor or armor.subtype ~= "massive" then
 		return nil
 	end
-	return shield
+	return armor
 end
diff --git a/game/modules/tome/data/birth/mage.lua b/game/modules/tome/data/birth/mage.lua
index fe4fab3e0f68f1a22219e6cd9147276c60894922..4912e99e3d086a309510d19fb5ae2f57fd89ae6b 100644
--- a/game/modules/tome/data/birth/mage.lua
+++ b/game/modules/tome/data/birth/mage.lua
@@ -70,7 +70,6 @@ newBirthDescriptor{
 		[ActorTalents.T_MANATHRUST] = 1,
 		[ActorTalents.T_FLAME] = 1,
 		[ActorTalents.T_FREEZE] = 1,
-		[ActorTalents.T_SENSE] = 5,
 	},
 }
 
diff --git a/game/modules/tome/data/birth/rogue.lua b/game/modules/tome/data/birth/rogue.lua
index ec0ad3fa3df9479f46c874dc0237b3649b6fe0ee..8665ae64453bc4a41b98120f2e02a93e4a20bdeb 100644
--- a/game/modules/tome/data/birth/rogue.lua
+++ b/game/modules/tome/data/birth/rogue.lua
@@ -41,10 +41,12 @@ newBirthDescriptor{
 		["cunning/stealth"]={true, 0.3},
 		["cunning/traps"]={true, 0.3},
 		["cunning/dirty"]={true, 0.3},
+		["cunning/survival"]={true, 0.3},
 	},
 	talents = {
 		[ActorTalents.T_STEALTH] = 1,
 		[ActorTalents.T_WEAPON_COMBAT] = 1,
 		[ActorTalents.T_KNIFE_MASTERY] = 1,
+		[ActorTalents.T_TRAP_DETECTION] = 1,
 	},
 }
diff --git a/game/modules/tome/data/general/traps/elemental.lua b/game/modules/tome/data/general/traps/elemental.lua
new file mode 100644
index 0000000000000000000000000000000000000000..68c243a5bde9a68e6e2cf34857f15da25f09fce8
--- /dev/null
+++ b/game/modules/tome/data/general/traps/elemental.lua
@@ -0,0 +1,114 @@
+newEntity{ define_as = "TRAP_ELEMENTAL",
+	type = "elemental", id_by_type=true, unided_name = "trap",
+	display = '^',
+	triggered = function(self, x, y, who)
+		(self.src or who):project({type="hit"}, x, y, self.damtype, self.dam, self.particles and {type=self.particles})
+		return true
+	end,
+}
+newEntity{ define_as = "TRAP_ELEMENTAL_BLAST",
+	type = "elemental", id_by_type=true, unided_name = "trap",
+	display = '^',
+	triggered = function(self, x, y, who)
+		(self.src or who):project({type="ball", radius=self.radius or 2}, x, y, self.damtype, self.dam, self.particles and {type=self.particles})
+		return true
+	end,
+}
+
+-------------------------------------------------------
+-- Bolts
+-------------------------------------------------------
+newEntity{ base = "TRAP_ELEMENTAL",
+	subtype = "acid",
+	name = "acid trap",
+	detect_power = 6, disarm_power = 6,
+	rarity = 3, level_range = {1, 30},
+	color_r=40, color_g=220, color_b=0,
+	message = "A stream of acid gushes onto @target@!",
+	dam = resolvers.mbonus(150, 5), damtype = DamageType.ACID,
+}
+newEntity{ base = "TRAP_ELEMENTAL",
+	subtype = "fire",
+	name = "fire trap",
+	detect_power = 6, disarm_power = 6,
+	rarity = 3, level_range = {1, 30},
+	color_r=220, color_g=0, color_b=0,
+	message = "A bolt of fire fires onto @target@!",
+	dam = resolvers.mbonus(180, 10), damtype = DamageType.FIREBURN,
+}
+newEntity{ base = "TRAP_ELEMENTAL",
+	subtype = "cold",
+	name = "ice trap",
+	detect_power = 6, disarm_power = 6,
+	rarity = 3, level_range = {1, 30},
+	color_r=150, color_g=150, color_b=220,
+	message = "A bolt of ice gushes onto @target@!",
+	dam = resolvers.mbonus(150, 5), damtype = DamageType.ICE,
+}
+newEntity{ base = "TRAP_ELEMENTAL",
+	subtype = "lightning",
+	name = "lightning trap",
+	detect_power = 6, disarm_power = 6,
+	rarity = 3, level_range = {1, 30},
+	color_r=0, color_g=0, color_b=220,
+	message = "A bolt of lightning fires onto @target@!",
+	dam = resolvers.mbonus(150, 5), damtype = DamageType.LIGHTNING,
+}
+newEntity{ base = "TRAP_ELEMENTAL",
+	subtype = "poison",
+	name = "poison trap",
+	detect_power = 6, disarm_power = 6,
+	rarity = 3, level_range = {1, 30},
+	color_r=0, color_g=220, color_b=0,
+	message = "A stream of poison gushes onto @target@!",
+	dam = resolvers.mbonus(150, 5), damtype = DamageType.POISON,
+}
+
+-------------------------------------------------------
+-- Blasts
+-------------------------------------------------------
+newEntity{ base = "TRAP_ELEMENTAL_BLAST",
+	subtype = "acid",
+	name = "acid blast trap",
+	detect_power = 50, disarm_power = 50,
+	rarity = 3, level_range = {20, 50},
+	color_r=40, color_g=220, color_b=0,
+	message = "A stream of acid gushes onto @target@!",
+	dam = resolvers.mbonus(250, 5), damtype = DamageType.ACID, radius = 2,
+}
+newEntity{ base = "TRAP_ELEMENTAL_BLAST",
+	subtype = "fire",
+	name = "fire blast trap",
+	detect_power = 50, disarm_power = 50,
+	rarity = 3, level_range = {20, 50},
+	color_r=220, color_g=0, color_b=0,
+	message = "A bolt of fire fires onto @target@!",
+	dam = resolvers.mbonus(300, 10), damtype = DamageType.FIREBURN, radius = 2,
+}
+newEntity{ base = "TRAP_ELEMENTAL_BLAST",
+	subtype = "cold",
+	name = "ice blast trap",
+	detect_power = 50, disarm_power = 50,
+	rarity = 3, level_range = {20, 50},
+	color_r=150, color_g=150, color_b=220,
+	message = "A bolt of ice gushes onto @target@!",
+	dam = resolvers.mbonus(250, 5), damtype = DamageType.ICE, radius = 2,
+}
+newEntity{ base = "TRAP_ELEMENTAL_BLAST",
+	subtype = "lightning",
+	name = "lightning blast trap",
+	detect_power = 50, disarm_power = 50,
+	rarity = 3, level_range = {20, 50},
+	color_r=0, color_g=0, color_b=220,
+	message = "A bolt of lightning fires onto @target@!",
+	dam = resolvers.mbonus(250, 5), damtype = DamageType.LIGHTNING, radius = 2,
+}
+newEntity{ base = "TRAP_ELEMENTAL_BLAST",
+	subtype = "poison",
+	name = "poison blast trap",
+	detect_power = 50, disarm_power = 50,
+	rarity = 3, level_range = {20, 50},
+	color_r=0, color_g=220, color_b=0,
+	message = "A stream of poison gushes onto @target@!",
+	dam = resolvers.mbonus(250, 5), damtype = DamageType.POISON, radius = 2,
+}
diff --git a/game/modules/tome/data/gfx/particles/acid.lua b/game/modules/tome/data/gfx/particles/acid.lua
new file mode 100644
index 0000000000000000000000000000000000000000..43667cbf13e02cf40866c2a8be4270959c4b73bd
--- /dev/null
+++ b/game/modules/tome/data/gfx/particles/acid.lua
@@ -0,0 +1,19 @@
+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/tome/data/talents/cunning/cunning.lua b/game/modules/tome/data/talents/cunning/cunning.lua
index 6e30c90b635df56eaee9494b5d354a10a494a40e..a92d480f3b2be126a773f4549223726f7614fec3 100644
--- a/game/modules/tome/data/talents/cunning/cunning.lua
+++ b/game/modules/tome/data/talents/cunning/cunning.lua
@@ -1,8 +1,32 @@
 -- Cunning talents
 newTalentType{ type="cunning/stealth", name = "stealth", description = "Allows the user to enter stealth." }
-newTalentType{ type="cunning/traps", name = "traps", description = "The knowledge of traps." }
+newTalentType{ type="cunning/trapping", name = "trapping", description = "The knowledge of trap laying." }
 newTalentType{ type="cunning/dirty", name = "dirty fighting", description = "Teaches various talents to criple your foes." }
+newTalentType{ type="cunning/survival", name = "survival", description = "The knowledge of the dangers of the world, and how to best avoid them." }
+
+-- Generic requires for cunning based on talent level
+cuns_req1 = {
+	stat = { cun=function(level) return 12 + (level-1) * 2 end },
+	level = function(level) return 0 + (level-1)  end,
+}
+cuns_req2 = {
+	stat = { cun=function(level) return 20 + (level-1) * 2 end },
+	level = function(level) return 4 + (level-1)  end,
+}
+cuns_req3 = {
+	stat = { cun=function(level) return 28 + (level-1) * 2 end },
+	level = function(level) return 8 + (level-1)  end,
+}
+cuns_req4 = {
+	stat = { cun=function(level) return 36 + (level-1) * 2 end },
+	level = function(level) return 12 + (level-1)  end,
+}
+cuns_req5 = {
+	stat = { cun=function(level) return 44 + (level-1) * 2 end },
+	level = function(level) return 16 + (level-1)  end,
+}
 
 load("/data/talents/cunning/stealth.lua")
 load("/data/talents/cunning/traps.lua")
+load("/data/talents/cunning/survival.lua")
 load("/data/talents/cunning/dirty.lua")
diff --git a/game/modules/tome/data/talents/cunning/survival.lua b/game/modules/tome/data/talents/cunning/survival.lua
new file mode 100644
index 0000000000000000000000000000000000000000..a4c0e63764680c684d9947b6864332071888bc11
--- /dev/null
+++ b/game/modules/tome/data/talents/cunning/survival.lua
@@ -0,0 +1,23 @@
+newTalent{
+	name = "Trap Detection",
+	type = {"cunning/survival", 1},
+	require = cuns_req1,
+	mode = "passive",
+	points = 5,
+	info = function(self, t)
+		return ([[Your attention to details allows you to detect traps around you (%d detection 'power').]]):
+		format(self:getTalentLevel(t) * self:getCun(25))
+	end,
+}
+
+newTalent{
+	name = "Trap Disarm",
+	type = {"cunning/survival", 1},
+	require = cuns_req1,
+	mode = "passive",
+	points = 5,
+	info = function(self, t)
+		return ([[Yuo have learned to disarm traps. (%d disarm power).]]):
+		format(self:getTalentLevel(t) * self:getCun(25))
+	end,
+}
diff --git a/game/modules/tome/data/zones/tower-amon-sul/traps.lua b/game/modules/tome/data/zones/tower-amon-sul/traps.lua
index f281ce45e8b2ece102c8ffc92cbed81c1ca68c64..61e5f0daf639c9f18947c9f2431c5ccdc17720fc 100644
--- a/game/modules/tome/data/zones/tower-amon-sul/traps.lua
+++ b/game/modules/tome/data/zones/tower-amon-sul/traps.lua
@@ -1 +1 @@
-load("/data/general/traps/basic.lua")
+load("/data/general/traps/elemental.lua")
diff --git a/game/modules/tome/load.lua b/game/modules/tome/load.lua
index 8bf39dd000f18b5ef142c55d85b87b18d3a989ae..5ce3804e077f043131f5e0c31079cb212070905a 100644
--- a/game/modules/tome/load.lua
+++ b/game/modules/tome/load.lua
@@ -59,6 +59,9 @@ ActorAI:loadDefinition("/mod/ai/")
 -- Birther descriptor
 Birther:loadDefinition("/data/birth/descriptors.lua")
 
+------------------------------------------------------------------------
+-- Count the number of talents per types
+------------------------------------------------------------------------
 local type_tot = {}
 for i, t in ipairs(ActorTalents.talents_def) do
 	type_tot[t.type[1]] = (type_tot[t.type[1]] or 0) + t.points
@@ -73,5 +76,6 @@ table.sort(stype_tot, function(a, b) return a[1] < b[1] end)
 for i, t in ipairs(stype_tot) do
 	print("[SCHOOL TOTAL]", t[2], t[1])
 end
+------------------------------------------------------------------------
 
 return require "mod.class.Game"
diff --git a/ideas/cunning.ods b/ideas/cunning.ods
index 0411623c72475e5a4ed26d111dfcf24da91629ff..6fb519e88ff36ee53d91110af4bfd58085c5ce69 100644
Binary files a/ideas/cunning.ods and b/ideas/cunning.ods differ