diff --git a/game/engines/default/engine/Savefile.lua b/game/engines/default/engine/Savefile.lua
index a3a5bfc207888596ff68ebb30e15999388c3528a..8a4207f93f01361bb4f091f538366181052a66a6 100644
--- a/game/engines/default/engine/Savefile.lua
+++ b/game/engines/default/engine/Savefile.lua
@@ -101,14 +101,6 @@ function _M:saveObject(obj, zip)
 		local tbl = table.remove(self.process)
 		self.tables[tbl] = self:getFileName(tbl)
 		zip:add(self:getFileName(tbl), tbl:save())
-		-- If run from a coroutine, we pause every object
-		if self.coroutine then
-			local coret = coroutine.yield()
-			if coret and type(coret) == "string" and coret == "cancel" then
-				print("[SAVE] abording")
-				break
-			end
-		end
 	end
 	return self.tables[obj]
 end
diff --git a/game/engines/default/engine/class.lua b/game/engines/default/engine/class.lua
index ba02bfc0026be1a469e99abebc1d01b32d5e7d48..adb6ba7a415c44619d76a9887d41fd174798a61c 100644
--- a/game/engines/default/engine/class.lua
+++ b/game/engines/default/engine/class.lua
@@ -168,6 +168,9 @@ local function basicSerialize(o, t)
 end
 
 local function serialize_data(outf, name, value, saved, filter, allow, savefile, force)
+	-- If run from a coroutine, we pause every table
+	if savefile.coroutine then coroutine.yield() end
+
 	saved = saved or {}       -- initial value
 	outf(name, " = ")
 	local tvalue = type(value)
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 27721f79fd1a424f68fbba93f7a484ce49e44709..1f6301e3deab9637be95ec3c9f44e580ae2870df 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -1416,7 +1416,7 @@ function _M:getTalentFullDescription(t, addlevel)
 	if self:getTalentRange(t) > 1 then d:add({"color",0x6f,0xff,0x83}, "Range: ", {"color",0xFF,0xFF,0xFF}, ""..self:getTalentRange(t), true)
 	else d:add({"color",0x6f,0xff,0x83}, "Range: ", {"color",0xFF,0xFF,0xFF}, "melee/personal", true)
 	end
-	if t.cooldown then d:add({"color",0x6f,0xff,0x83}, "Cooldown: ", {"color",0xFF,0xFF,0xFF}, ""..util.getval(t.cooldown, self, t), true) end
+	if self:getTalentCooldown(t) then d:add({"color",0x6f,0xff,0x83}, "Cooldown: ", {"color",0xFF,0xFF,0xFF}, ""..self:getTalentCooldown(t), true) end
 	local speed = self:getTalentProjectileSpeed(t)
 	if speed then d:add({"color",0x6f,0xff,0x83}, "Travel Speed: ", {"color",0xFF,0xFF,0xFF}, ""..(speed * 100).."% of base", true)
 	else d:add({"color",0x6f,0xff,0x83}, "Travel Speed: ", {"color",0xFF,0xFF,0xFF}, "instantaneous", true)
@@ -1432,18 +1432,23 @@ function _M:getTalentFullDescription(t, addlevel)
 	return d
 end
 
---- Starts a talent cooldown; overloaded from the default to handle talent cooldown reduction
--- @param t the talent to cooldown
-function _M:startTalentCooldown(t)
+function _M:getTalentCooldown(t)
 	if not t.cooldown then return end
 	local cd = t.cooldown
 	if type(cd) == "function" then cd = cd(self, t) end
 	if self.talent_cd_reduction[t.id] then cd = cd - self.talent_cd_reduction[t.id] end
 	if t.is_spell then
-		self.talents_cd[t.id] = math.ceil(cd * (1 - self.spell_cooldown_reduction or 0))
+		return math.ceil(cd * (1 - self.spell_cooldown_reduction or 0))
 	else
-		self.talents_cd[t.id] = cd
+		return cd
 	end
+end
+
+--- Starts a talent cooldown; overloaded from the default to handle talent cooldown reduction
+-- @param t the talent to cooldown
+function _M:startTalentCooldown(t)
+	if not t.cooldown then return end
+	self.talents_cd[t.id] = self:getTalentCooldown(t)
 	self.changed = true
 end
 
diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua
index 97067bee6d2264ed4b81154bb2dabbaf1e574d92..581d716bf6e490919aea43d65e2fb3dfe65f1be9 100644
--- a/game/modules/tome/class/Player.lua
+++ b/game/modules/tome/class/Player.lua
@@ -108,6 +108,13 @@ function _M:onEnterLevel(zone, level)
 	if self.random_escort_levels and self.random_escort_levels[zone.short_name] and self.random_escort_levels[zone.short_name][level.level] then
 		self:grantQuest("escort-duty")
 	end
+
+	-- Cancel effects
+	local effs = {}
+	for eff_id, p in pairs(self.tmp) do
+		if self.tempeffect_def[eff_id].cancel_on_level_change then effs[#effs+1] = eff_id end
+	end
+	for i, eff_id in ipairs(effs) do self:removeEffect(eff_id) end
 end
 
 function _M:onLeaveLevel(zone, level)
@@ -139,6 +146,7 @@ function _M:move(x, y, force)
 			if obj.auto_pickup then
 				self:pickupFloor(i, true)
 			else
+				if self:attr("auto_id_mundane") and obj:getPowerRank() <= 1 then obj:identify(true) end
 				nb = nb + 1
 				i = i + 1
 			end
@@ -249,6 +257,7 @@ local wild_fovdist = {}
 for i = 0, 10 * 10 do
 	wild_fovdist[i] = math.max((5 - math.sqrt(i)) / 1.4, 0.6)
 end
+local arcane_eye_true_seeing = function() return true, 100 end
 
 function _M:playerFOV()
 	-- Clean FOV before computing it
@@ -293,6 +302,20 @@ function _M:playerFOV()
 		end, true, true, true)
 	end
 
+	-- Handle arcane eye
+	if self:hasEffect(self.EFF_ARCANE_EYE) then
+		local eff = self:hasEffect(self.EFF_ARCANE_EYE)
+		local map = game.level.map
+
+		core.fov.calc_circle(
+			eff.x, eff.y, eff.radius, function(_, x, y) if map:checkAllEntities(x, y, "block_sight", self) then return true end end,
+			function(_, x, y)
+				local t = map(x, y, map.ACTOR)
+				if t and (eff.true_seeing or self:canSee(t)) then map.seens(x, y, 1) end
+			end,
+			cache and map._fovcache["block_sight"]
+		)
+	end
 
 	-- Handle Preternatural Senses talent, a simple FOV, using cache.
 	if self:knowTalent(self.T_PRETERNATURAL_SENSES) then
diff --git a/game/modules/tome/data/talents/spells/divination.lua b/game/modules/tome/data/talents/spells/divination.lua
index 6b4bb4cdf2135f209f2b844d8cbcd29ee85f3b29..d5d217cee89a7e886396d313e9a8281dea1ff422 100644
--- a/game/modules/tome/data/talents/spells/divination.lua
+++ b/game/modules/tome/data/talents/spells/divination.lua
@@ -25,9 +25,6 @@ newTalent{
 	random_ego = "utility",
 	mana = 10,
 	cooldown = 10,
-	tactical = {
-		ATTACK = 10,
-	},
 	action = function(self, t)
 		local rad = 10 + self:combatSpellpower(0.1) * self:getTalentLevel(t)
 		self:setEffect(self.EFF_SENSE, 2, {
@@ -48,58 +45,36 @@ newTalent{
 }
 
 newTalent{
-	name = "Identify",
+	name = "Arcane Eye",
 	type = {"spell/divination", 2},
 	require = spells_req2,
 	points = 5,
-	random_ego = "utility",
-	mana = 20,
+	mana = 15,
+	cooldown = 10,
+	no_energy = true,
 	action = function(self, t)
-		local rad = math.floor(0 + (self:getTalentLevel(t) - 4))
-
-		if self:getTalentLevel(t) < 3 then
-			self:showEquipInven("Identify object", function(o) return not o:isIdentified() end, function(o)
-				o:identify(true)
-				game.logPlayer(self, "You identify: %s", o:getName{do_color=true})
-				return true
-			end)
-			return true
-		end
-
-		if self:getTalentLevel(t) >= 3 then
-			for inven_id, inven in pairs(self.inven) do
-				for i, o in ipairs(inven) do
-					o:identify(true)
-				end
-			end
-			game.logPlayer(self, "You identify all your inventory.")
-		end
-
-		if self:getTalentLevel(t) >= 4 then
-			local idfloor = function(x, y)
-				local idx = 1
-				while true do
-					local o = game.level.map:getObject(x, y, idx)
-					if not o then break end
-					o:identify(true)
-					idx = idx + 1
-				end
-			end
-			local rad = math.floor(0 + (self:getTalentLevel(t) - 4))
-			if rad == 0 then idfloor(self.x, self.y)
-			else self:project({type="ball", radius=rad}, self.x, self.y, idfloor)
-			end
-
-			game.logPlayer(who, "You identify everything around you.")
-		end
+		local tg = {type="hit", nolock=true, pass_terrain=true, nowarning=true, range=100, requires_knowledge=false}
+		x, y = self:getTarget(tg)
+		if not x then return nil end
+		-- Target code doesnot restrict the target coordinates to the range, it lets the poject function do it
+		-- but we cant ...
+		local _ _, x, y = self:canProject(tg, x, y)
 
+		local dur = math.floor(10 + self:getTalentLevel(t) * 3)
+		local radius = math.floor(4 + self:getTalentLevel(t) * 3)
+		self:setEffect(self.EFF_ARCANE_EYE, dur, {x=x, y=y, radius=radius, true_seeing=self:getTalentLevel(t) >= 5})
 		game:playSoundNear(self, "talents/spell_generic")
 		return true
 	end,
 	info = function(self, t)
-		return ([[Identify the powers and nature of an object.
-		At level 3 it identifies all the objects in your possession.
-		At level 4 it identifies all the objects on the floor in a radius of %d.]]):format(math.floor(0 + (self:getTalentLevel(t) - 4)))
+		return ([[Summons an etheral magical eye at the designated location that lasts for %d turns.
+		The eye can not be seen or attacked by other creatures and posses magical vision that allows it to see any creature in a %d range around it.
+		It does not require light to do so but it can not see through walls.
+		Casting the eye does not take a turn.
+		Only one arcane eye can exist at any given time.
+		At level 5 its vision can see through invisibility, stealth and all other sight affecting effects.
+		]]):
+		format(math.floor(10 + self:getTalentLevel(t) * 3), math.floor(4 + self:getTalentLevel(t) * 3))
 	end,
 }
 
@@ -112,12 +87,12 @@ newTalent{
 	mana = 20,
 	cooldown = 20,
 	action = function(self, t)
-		self:magicMap(10 + self:combatSpellpower(0.1) * self:getTalentLevel(t))
+		self:magicMap(5 + self:combatTalentSpellDamage(t, 2, 12))
 		game:playSoundNear(self, "talents/spell_generic")
 		return true
 	end,
 	info = function(self, t)
-		return ([[Form a map of your surroundings in your mind in a radius of %d.]]):format(10 + self:combatSpellpower(0.1) * self:getTalentLevel(t))
+		return ([[Form a map of your surroundings in your mind in a radius of %d.]]):format(5 + self:combatTalentSpellDamage(t, 2, 12))
 	end,
 }
 
@@ -131,7 +106,7 @@ newTalent{
 	cooldown = 30,
 	activate = function(self, t)
 		-- There is an implicit +10, as it is the default radius
-		local rad = self:combatSpellpower(0.1) * self:getTalentLevel(t)
+		local rad = self:combatTalentSpellDamage(t, 2, 10)
 		game:playSoundNear(self, "talents/spell_generic")
 		return {
 			esp = self:addTemporaryValue("esp", {range=rad, all=1}),
@@ -146,6 +121,6 @@ newTalent{
 	info = function(self, t)
 		return ([[Allows you to sense the presence of foes, in a radius of %d.
 		This powerful spell will continuously drain mana while active.
-		The bonus will increase with the Magic stat]]):format(10 + self:combatSpellpower(0.1) * self:getTalentLevel(t))
+		The bonus will increase with the Magic stat]]):format(10 + self:combatTalentSpellDamage(t, 2, 10))
 	end,
 }
diff --git a/game/modules/tome/data/talents/spells/spells.lua b/game/modules/tome/data/talents/spells/spells.lua
index f4843c97b4d0eaeb90b490dcd7733e5e46637fcc..fde774fe4b4b037b8041573b1317745c5386cf12 100644
--- a/game/modules/tome/data/talents/spells/spells.lua
+++ b/game/modules/tome/data/talents/spells/spells.lua
@@ -31,13 +31,13 @@ newTalentType{ no_silence=true, is_spell=true, type="spell/air", name = "air", d
 newTalentType{ no_silence=true, is_spell=true, type="spell/storm", name = "storm", description = "Harness the power of the storm to incinerate your foes." }
 
 -- Various other magic schools
-newTalentType{ no_silence=true, is_spell=true, type="spell/conveyance", name = "conveyance", generic = true, description = "Conveyance is the school of travel. It allows you to travel faster and to track others." }
-newTalentType{ no_silence=true, is_spell=true, type="spell/nature", name = "nature", generic = true, description = "Summons the power of nature to rejuvenate yourself and the world." }
 newTalentType{ no_silence=true, is_spell=true, type="spell/meta", name = "meta", description = "Meta spells alter the working of magic itself." }
-newTalentType{ no_silence=true, is_spell=true, type="spell/divination", name = "divination", generic = true, description = "Divination allows the caster to sense its surroundings, find hidden things." }
 newTalentType{ no_silence=true, is_spell=true, type="spell/temporal", name = "temporal", description = "The school of time manipulation." }
 newTalentType{ no_silence=true, is_spell=true, type="spell/phantasm", name = "phantasm", description = "Control the power of tricks and illusions." }
 newTalentType{ no_silence=true, is_spell=true, type="spell/enhancement", name = "enhancement", description = "Magical enhancement of your body." }
+newTalentType{ no_silence=true, is_spell=true, type="spell/conveyance", name = "conveyance", generic = true, description = "Conveyance is the school of travel. It allows you to travel faster and to track others." }
+newTalentType{ no_silence=true, is_spell=true, type="spell/divination", name = "divination", generic = true, description = "Divination allows the caster to sense its surroundings, find hidden things." }
+newTalentType{ no_silence=true, is_spell=true, type="spell/nature", name = "nature", generic = true, description = "Summons the power of nature to rejuvenate yourself and the world." }
 
 -- Alchemist spells
 newTalentType{ no_silence=true, is_spell=true, type="spell/explosives", name = "explosive admixtures", description = "Manipulate gems to turn them into explosive magical bombs." }
diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua
index ff18a3423d97025ecd196868315f76ad665c24b1..48bf60fef293556f3069cd0577ffee93b5204c9c 100644
--- a/game/modules/tome/data/timed_effects.lua
+++ b/game/modules/tome/data/timed_effects.lua
@@ -648,6 +648,22 @@ newEffect{
 	end,
 }
 
+newEffect{
+	name = "ARCANE_EYE",
+	desc = "Arcane Eye",
+	long_desc = function(self, eff) return ("You have an arcane eye observing for you in a radius of %d."):format(eff.radius) end,
+	type = "magical",
+	status = "beneficial",
+	cancel_on_level_change = true,
+	parameters = { range=10, actor=1, object=0, trap=0 },
+	activate = function(self, eff)
+		game.level.map.changed = true
+	end,
+	deactivate = function(self, eff)
+		game.level.map.changed = true
+	end,
+}
+
 newEffect{
 	name = "ALL_STAT",
 	desc = "All stats increase",
diff --git a/ideas/spells.ods b/ideas/spells.ods
index 4d83b6faf5221639459d7a39e061b1651f355f75..873788b214ac42fee6e264601ea924fc9a765384 100644
Binary files a/ideas/spells.ods and b/ideas/spells.ods differ