diff --git a/game/engines/default/engine/Actor.lua b/game/engines/default/engine/Actor.lua
index a8313e7dae252786572d85fa1d5c08cd0bd9c18e..8b1c8651b293d5b8d7bdfe8aa7a909fa0c297337 100644
--- a/game/engines/default/engine/Actor.lua
+++ b/game/engines/default/engine/Actor.lua
@@ -90,6 +90,13 @@ end
 function _M:defineDisplayCallback()
 	if not self._mo then return end
 
+	-- Cunning trick here!
+	-- the callback we give to mo:displayCallback is a function that references self
+	-- but self contains mo so it would create a cyclic reference and prevent GC'ing
+	-- thus we store a reference to a weak table and put self into it
+	-- this way when self dies the weak reference dies and does not prevent GC'ing
+	local weak = setmetatable({[1]=self}, {__mode="v"})
+
 	local ps = self:getParticlesList()
 
 	local f_self = nil
@@ -104,7 +111,7 @@ function _M:defineDisplayCallback()
 			e = ps[i]
 			e:checkDisplay()
 			if e.ps:isAlive() then e.ps:toScreen(x + w / 2, y + h / 2, true, w / game.level.map.tile_w)
-			else self:removeParticles(e)
+			elseif weak[1] then weak[1]:removeParticles(e)
 			end
 		end
 	end
@@ -112,6 +119,8 @@ function _M:defineDisplayCallback()
 	local function tactical(x, y, w, h)
 		-- Tactical info
 		if game.level and game.level.map.view_faction then
+			local self = weak[1]
+			if not self then return end
 			local map = game.level.map
 
 			if not f_self then
diff --git a/game/engines/default/engine/Level.lua b/game/engines/default/engine/Level.lua
index 8831460050ef275cc2ab05e032d93b6c96f216ef..3d2e575d443207babab91be5669950790d643a51 100644
--- a/game/engines/default/engine/Level.lua
+++ b/game/engines/default/engine/Level.lua
@@ -23,6 +23,11 @@ local Map = require "engine.Map"
 --- Define a level
 module(..., package.seeall, class.make)
 
+-- Keep a list of currently existing maps
+-- this is a weak table so it doesn't prevents GC
+__level_store = {}
+setmetatable(__level_store, {__mode="k"})
+
 --- Initializes the level with a "level" and a map
 function _M:init(level, map)
 	self.level = level
@@ -150,6 +155,8 @@ function _M:save()
 	return class.save(self, {entities_list=true})
 end
 function _M:loaded()
+	__level_store[self] = true
+
 	-- Loading the game has defined new uids for entities, yet we hard referenced the old ones
 	-- So we fix it
 	local nes = {}
diff --git a/game/engines/default/engine/SavefilePipe.lua b/game/engines/default/engine/SavefilePipe.lua
index 10149b5f42a88f9e7b6871ac056f5c625c8349c5..a4fcadaee6bad972ecd0d8d45231ef9f3f84ee19 100644
--- a/game/engines/default/engine/SavefilePipe.lua
+++ b/game/engines/default/engine/SavefilePipe.lua
@@ -118,7 +118,7 @@ function _M:push(savename, type, object, class, on_end)
 	local Savefile = require(class)
 	local id = Savefile["nameSave"..type:lower():capitalize()](Savefile, object)
 
-	if #self.pipe == 0 then savefile_pipe.current_nb = 0 self:steamCleanup() end
+	if #self.pipe == 0 then savefile_pipe.start_time = os.time() savefile_pipe.current_nb = 0 self:steamCleanup() end
 
 	local clone, nb = object:cloneForSave()
 	self.pipe_types[type] = self.pipe_types[type] or {}
@@ -238,6 +238,10 @@ function _M:doThread()
 		p.fct()
 		table.remove(self.on_done, 1)
 	end
+
+	if savefile_pipe.start_time then
+		print("[SAVEFILE PIPE] saving took "..(os.time() - savefile_pipe.start_time).." seconds")
+	end
 end
 
 --- Force to wait for the saves
diff --git a/game/engines/default/engine/Zone.lua b/game/engines/default/engine/Zone.lua
index 6335ed3816cad996544e85049dee0c8d6d14748f..170d1411eaeb044926837db24c420b085eee4b8c 100644
--- a/game/engines/default/engine/Zone.lua
+++ b/game/engines/default/engine/Zone.lua
@@ -30,6 +30,10 @@ module(..., package.seeall, class.make)
 
 _no_save_fields = {temp_memory_levels=true, _tmp_data=true}
 
+-- Keep a list of currently existing maps
+-- this is a weak table so it doesn't prevents GC
+__zone_store = {}
+setmetatable(__zone_store, {__mode="k"})
 
 --- List of rules to run through when applying an ego to an entity.
 _M.ego_rules = {}
@@ -93,6 +97,8 @@ end
 --- Loads a zone definition
 -- @param short_name the short name of the zone to load, if should correspond to a directory in your module data/zones/short_name/ with a zone.lua, npcs.lua, grids.lua and objects.lua files inside
 function _M:init(short_name, dynamic)
+	__zone_store[self] = true
+
 	if _M.persist_last_zones and _M.persist_last_zones[short_name] then
 		local zone = _M.persist_last_zones[short_name]
 		forceprint("[ZONE] loading from last persistent", short_name, zone)
@@ -647,6 +653,8 @@ end
 
 --- If we are loaded we need a new uid
 function _M:loaded()
+	__zone_store[self] = true
+
 	if type(self.reload_lists) ~= "boolean" or self.reload_lists then
 		self:loadBaseLists()
 	end
diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua
index eb2df64276bc956fa6486ad9cdf102ab1bcd7364..4fb29398ca25541130c9698bdaaa61dec236b49b 100644
--- a/game/engines/default/engine/utils.lua
+++ b/game/engines/default/engine/utils.lua
@@ -1782,6 +1782,49 @@ function util.loadfilemods(file, env)
 	return prev
 end
 
+--- Find a reference to the given value inside a table and all it contains
+function util.findAllReferences(t, what)
+	local seen = {}
+	local function recurs(t, data)
+		if seen[t] then return end
+		seen[t] = true
+		for k, e in pairs(t) do
+			if type(k) == "table" then
+				local data = table.clone(data)
+				data[#data+1] = "k:"..tostring(k)
+				recurs(k, data)
+			end
+			if type(e) == "table" then
+				local data = table.clone(data)
+				data[#data+1] = "e:"..tostring(k)
+				recurs(e, data)
+			end
+			if type(k) == "function" then
+				local fenv = getfenv(k)
+				local data = table.clone(data)
+				data[#data+1] = "k:fenv:"..tostring(k)
+				recurs(fenv, data)
+			end
+			if type(e) == "function" then
+				local fenv = getfenv(e)
+				local data = table.clone(data)
+				data[#data+1] = "e:fenv:"..tostring(k)
+				recurs(fenv, data)
+			end
+
+			if k == what then
+				print("KEY", table.concat(data, ", "))
+			end
+			if e == what then
+				print("VAL", table.concat(data, ", "))
+			end
+		end
+	end
+
+	local data = {}
+	recurs(t, data)
+end
+
 -- if these functions are ever desired elsewhere, don't be shy to make these accessible beyond utils.lua
 local function deltaCoordsToReal(dx, dy, source_x, source_y)
 	if util.isHex() then
diff --git a/game/modules/tome/class/WorldNPC.lua b/game/modules/tome/class/WorldNPC.lua
index bf591b09a35bb7b38cf18c55f5f86f598369de1e..ebd6443dcbbdb48c215e8612cc26de53f39a1d0f 100644
--- a/game/modules/tome/class/WorldNPC.lua
+++ b/game/modules/tome/class/WorldNPC.lua
@@ -70,23 +70,19 @@ end
 function _M:defineDisplayCallback()
 	if not self._mo then return end
 
-	local ps = self:getParticlesList()
+	-- Cunning trick here!
+	-- the callback we give to mo:displayCallback is a function that references self
+	-- but self contains mo so it would create a cyclic reference and prevent GC'ing
+	-- thus we store a reference to a weak table and put self into it
+	-- this way when self dies the weak reference dies and does not prevent GC'ing
+	local weak = setmetatable({[1]=self}, {__mode="v"})
 
-	local f_self = nil
-	local f_danger = nil
-	local f_powerful = nil
-	local f_friend = nil
-	local f_enemy = nil
-	local f_neutral = nil
-	local sf_self = nil
-	local sf_danger2 = nil
-	local sf_danger1 = nil
-	local sf_powerful = nil
-	local sf_friend = nil
-	local sf_enemy = nil
-	local sf_neutral = nil
+	local ps = self:getParticlesList()
 
 	self._mo:displayCallback(function(x, y, w, h, zoom, on_map)
+		local self = weak[1]
+		if not self then return end
+
 		if game.level and game.level.map.view_faction and game.always_target and game.always_target ~= "old" then
 			local map = game.level.map
 			if on_map then
diff --git a/game/modules/tome/data/general/objects/world-artifacts.lua b/game/modules/tome/data/general/objects/world-artifacts.lua
index ad936842cd0b7914920c9c78f1980c4787dc90af..343024acb3ca4f5c664e066f8de088938d4f66e5 100644
--- a/game/modules/tome/data/general/objects/world-artifacts.lua
+++ b/game/modules/tome/data/general/objects/world-artifacts.lua
@@ -1938,8 +1938,8 @@ newEntity{ base = "BASE_ARROW",
 		special_on_crit = {desc="dominate the target", fct=function(combat, who, target)
 			if not target or target == self then return end
 			if target:canBe("instakill") then
-				local check = math.max(src:combatSpellpower(), src:combatMindpower(), src:combatAttack())
-				target:setEffect(target.EFF_DOMINATE_ENTHRALL, 3, {src=who, apply_power=check()})
+				local check = math.max(who:combatSpellpower(), who:combatMindpower(), who:combatAttack())
+				target:setEffect(target.EFF_DOMINATE_ENTHRALL, 3, {src=who, apply_power=check})
 			end
 		end},
 	},