diff --git a/game/engine/Entity.lua b/game/engine/Entity.lua
index 357103247f50b04395a15206926810ef301223aa..6bfee61256a98ad48a79a6eddc6b5f8d2d5a5447 100644
--- a/game/engine/Entity.lua
+++ b/game/engine/Entity.lua
@@ -30,6 +30,7 @@ local entities_load_functions = {}
 
 _M.__mo_repo = {}
 _M.__mo_final_repo = {}
+_M._no_save_fields = {}
 
 -- Setup the uids & MO repository as a weak value table, when the entities are no more used anywhere else they disappear from there too
 setmetatable(__uids, {__mode="v"})
diff --git a/game/engine/Tooltip.lua b/game/engine/Tooltip.lua
index a8cb4766911a0079b48754d72db5abccf6b43fc6..d317d0f29edbca57929e5665d13f89fedefbabda 100644
--- a/game/engine/Tooltip.lua
+++ b/game/engine/Tooltip.lua
@@ -114,11 +114,11 @@ function _M:displayAtMap(tmx, tmy, mx, my)
 		local tt = {}
 		local seen = game.level.map.seens(tmx, tmy)
 		local remember = game.level.map.remembers(tmx, tmy)
-		tt[#tt+1] = seen and game.level.map:checkEntity(tmx, tmy, Map.PROJECTILE, "tooltip")
-		tt[#tt+1] = seen and game.level.map:checkEntity(tmx, tmy, Map.ACTOR, "tooltip")
-		tt[#tt+1] = seen and game.level.map:checkEntity(tmx, tmy, Map.OBJECT, "tooltip")
-		tt[#tt+1] = seen and game.level.map:checkEntity(tmx, tmy, Map.TRAP, "tooltip")
-		tt[#tt+1] = remember and game.level.map:checkEntity(tmx, tmy, Map.TERRAIN, "tooltip")
+		tt[#tt+1] = seen and game.level.map:checkEntity(tmx, tmy, Map.PROJECTILE, "tooltip", game.level.map.actor_player)
+		tt[#tt+1] = seen and game.level.map:checkEntity(tmx, tmy, Map.ACTOR, "tooltip", game.level.map.actor_player)
+		tt[#tt+1] = seen and game.level.map:checkEntity(tmx, tmy, Map.OBJECT, "tooltip", game.level.map.actor_player)
+		tt[#tt+1] = seen and game.level.map:checkEntity(tmx, tmy, Map.TRAP, "tooltip", game.level.map.actor_player)
+		tt[#tt+1] = remember and game.level.map:checkEntity(tmx, tmy, Map.TERRAIN, "tooltip", game.level.map.actor_player)
 		if #tt > 0 then
 			self:set("%s", table.concat(tt, "\n---\n"))
 			self:display()
diff --git a/game/engine/class.lua b/game/engine/class.lua
index 7220f6a313030264973ef6c94a1343509c9f2040..ecd0023394a39e03a5a47d49bef4430580429974 100644
--- a/game/engine/class.lua
+++ b/game/engine/class.lua
@@ -185,8 +185,10 @@ end
 
 function _M:save(filter, allow, savefile)
 	filter = filter or {}
+	if self._no_save_fields then table.merge(filter, self._no_save_fields) end
 	if not allow then
 		filter.new = true
+		filter._no_save_fields = true
 		filter._mo = true
 		filter._mo_final = true
 	else
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 40580aaa3118353defec3c5bfc4cdfc6a145289c..40daac61afb94b6782021919847b2a2c9ef0bd06 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -53,6 +53,9 @@ module(..., package.seeall, class.inherit(
 	mod.class.interface.Combat
 ))
 
+-- Dont save the can_see_cache
+_M._no_save_fields.can_see_cache = true
+
 function _M:init(t, no_default)
 	-- Define some basic combat stats
 	self.combat_def = 0
@@ -140,6 +143,8 @@ function _M:init(t, no_default)
 	engine.interface.ActorStats.init(self, t)
 	engine.interface.ActorLevel.init(self, t)
 	engine.interface.ActorFOV.init(self, t)
+
+	self:resetCanSeeCache()
 end
 
 function _M:act()
@@ -202,6 +207,9 @@ function _M:act()
 	-- Still not dead ?
 	if self.dead then return false end
 
+	-- Ok reset the seen cache
+	self:resetCanSeeCache()
+
 	return true
 end
 
@@ -376,7 +384,8 @@ function _M:TextSizeCategory()
 	return sizecat
 end
 
-function _M:tooltip()
+function _M:tooltip(x, y, seen_by)
+	if seen_by and not seen_by:canSee(self) then return end
 	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"
@@ -1055,7 +1064,7 @@ 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)
+function _M:canSeeNoCache(actor, def, def_pct)
 	if not actor then return false, 0 end
 
 	-- ESP, see all, or only types/subtypes
@@ -1108,6 +1117,30 @@ function _M:canSee(actor, def, def_pct)
 	end
 end
 
+function _M:canSee(actor, def, def_pct)
+	if not actor then return false, 0 end
+
+	self.can_see_cache = self.can_see_cache or {}
+	local s = tostring(def).."/"..tostring(def_pct)
+
+	if self.can_see_cache[actor] and self.can_see_cache[actor][s] then return self.can_see_cache[actor][s][1], self.can_see_cache[actor][s][2] end
+	self.can_see_cache[actor] = self.can_see_cache[actor] or {}
+	self.can_see_cache[actor][s] = self.can_see_cache[actor][s] or {}
+
+	local res, chance = self:canSeeNoCache(actor, def, def_pct)
+	self.can_see_cache[actor][s] = {res,chance}
+
+	-- Make sure the display updates
+	if self.player and type(def) == "nil" and actor._mo then actor._mo:onSeen(res) end
+
+	return res, chance
+end
+
+function _M:resetCanSeeCache()
+	self.can_see_cache = {}
+	setmetatable(self.can_see_cache, {__mode="k"})
+end
+
 --- Can the target be applied some effects
 -- @param what a string describing what is being tried
 function _M:canBe(what)
diff --git a/game/modules/tome/class/NPC.lua b/game/modules/tome/class/NPC.lua
index c1a38c2e8c180b23b633b387edf31a9fc4c9361d..dde58a4eb47a07f89b72bf72017f7f26fc8b695e 100644
--- a/game/modules/tome/class/NPC.lua
+++ b/game/modules/tome/class/NPC.lua
@@ -117,8 +117,9 @@ function _M:die(src)
 	return mod.class.Actor.die(self, src)
 end
 
-function _M:tooltip()
-	local str = mod.class.Actor.tooltip(self)
+function _M:tooltip(x, y, seen_by)
+	local str = mod.class.Actor.tooltip(self, x, y, seen_by)
+	if not str then return end
 	return str..([[
 
 Target: %s
diff --git a/src/map.c b/src/map.c
index 5865dfbf23350ac8ca6de5934957c8efa3b94fa9..9762ba5b525d8db96ae3f20f146f61e86d35647e 100644
--- a/src/map.c
+++ b/src/map.c
@@ -80,6 +80,17 @@ static int map_object_free(lua_State *L)
 	return 1;
 }
 
+static int map_object_on_seen(lua_State *L)
+{
+	map_object *obj = (map_object*)auxiliar_checkclass(L, "core{mapobj}", 1);
+	if (lua_isboolean(L, 2))
+	{
+		obj->on_seen = lua_toboolean(L, 2);
+	}
+	lua_pushboolean(L, obj->on_seen);
+	return 1;
+}
+
 static int map_object_texture(lua_State *L)
 {
 	map_object *obj = (map_object*)auxiliar_checkclass(L, "core{mapobj}", 1);
@@ -792,6 +803,7 @@ static const struct luaL_reg map_object_reg[] =
 	{"shader", map_object_shader},
 	{"invalidate", map_object_invalid},
 	{"isValid", map_object_is_valid},
+	{"onSeen", map_object_on_seen},
 	{NULL, NULL},
 };