diff --git a/game/engines/default/engine/Quest.lua b/game/engines/default/engine/Quest.lua
index b74b3ef6ac680ea769486e8df02c8ebf1c47ee1c..194878bb6487a54648560c07b22d411192f45f8b 100644
--- a/game/engines/default/engine/Quest.lua
+++ b/game/engines/default/engine/Quest.lua
@@ -55,6 +55,17 @@ function _M:isCompleted(sub)
 	if self.status == COMPLETED then return true else return false end
 end
 
+--- Checks if the quest (or sub-objective) is failed
+-- @param sub a subobjective id or nil for the whole quest
+-- @return[1] false if objective is not failed
+-- @return[2] true if objective failed
+function _M:isFailed(sub)
+	if sub then
+		if self.objectives[sub] and self.objectives[sub] == FAILED then return true else return false end
+	end
+	if self.status == FAILED then return true else return false end
+end
+
 --- Checks if the quest is ended (DONE or FAILED)
 -- @return[1] false if quest didn't end
 -- @return[2] true if quest ended
diff --git a/game/engines/default/engine/generator/map/Maze.lua b/game/engines/default/engine/generator/map/Maze.lua
index d72efadd095186af604cd64a5ec0bb32fd0ddd8e..f086a69c9328ef493eedebeb2c62fff3e1e4523e 100644
--- a/game/engines/default/engine/generator/map/Maze.lua
+++ b/game/engines/default/engine/generator/map/Maze.lua
@@ -35,11 +35,17 @@ function _M:init(zone, map, grid_list, data)
 end
 
 function _M:generate(lev, old_lev)
+	local lastx, lasty = 0, 0
+
 	local do_tile = function(i, j, wall)
 		for ii = 0, self.data.widen_w-1 do for jj = 0, self.data.widen_h-1 do
 			self.map(i*self.data.widen_w+ii, j*self.data.widen_h+jj, Map.TERRAIN, self:resolve(wall and "wall" or "floor"))
 		end end
 		self.map.room_map[i][j].maze_wall = wall
+		if not wall then
+			lastx = math.max(lastx, i)
+			lasty = math.max(lasty, j)
+		end
 	end
 
 	for i = 0, self.data.w - 1 do for j = 0, self.data.h - 1 do
@@ -94,7 +100,8 @@ function _M:generate(lev, old_lev)
 	end
 	-- Always starts at 1, 1
 	local ux, uy = 1 * self.data.widen_w, 1 * self.data.widen_h
-	local dx, dy = math.floor(self.map.w/2)*2-1-2*(1-math.mod(self.map.w,2)), math.floor(self.map.h/2)*2-1-2*(1-math.mod(self.map.h,2))
+	local dx, dy = lastx * self.data.widen_w, lasty * self.data.widen_h
+	-- local dx, dy = math.floor(self.map.w/2)*2-1-2*(1-math.mod(self.map.w,2)), math.floor(self.map.h/2)*2-1-2*(1-math.mod(self.map.h,2))
 	self.map(ux, uy, Map.TERRAIN, self:resolve("up"))
 	self.map.room_map[ux][uy].special = "exit"
 	if lev < self.zone.max_level or self.data.force_last_stair then
diff --git a/game/engines/default/engine/generator/map/Static.lua b/game/engines/default/engine/generator/map/Static.lua
index 929d1153b77948de88587d4fe25fdb36b96a4ba6..873a714f945b3baf2dbffaad394a7f5baefa4647 100644
--- a/game/engines/default/engine/generator/map/Static.lua
+++ b/game/engines/default/engine/generator/map/Static.lua
@@ -75,20 +75,24 @@ function _M:getLoader(t)
 		trap_class = self.zone.trap_class,
 		npc_class = self.zone.npc_class,
 		object_class = self.zone.object_class,
-		specialList = function(kind, files, add_zone_lists) -- specify entity lists to use (add_zone_lists == add to current list)
+		specialList = function(kind, files, add_zone_lists) -- specify entity lists to use (add_zone_lists == include current list)
 			local elist
 			if kind == "terrain" then
 				if add_zone_lists then elist = table.clone(self.zone.grid_list) end
-				self.grid_list = self.zone.grid_class:loadList(files, nil, elist, nil, elist and elist.__loaded_files)
+				self.grid_list = self.zone.grid_class:loadList(files, nil, elist, nil, elist and table.clone(elist.__loaded_files))
+--				self.grid_list = self.zone.grid_class:loadList(files, nil, elist, nil, elist and elist.__loaded_files)
 			elseif kind == "trap" then
 				if add_zone_lists then elist = table.clone(self.zone.trap_list) end
-				self.trap_list = self.zone.trap_class:loadList(files, nil, elist, nil, elist and elist.__loaded_files)
+				self.trap_list = self.zone.trap_class:loadList(files, nil, elist, nil, elist and table.clone(elist.__loaded_files))
+--				self.trap_list = self.zone.trap_class:loadList(files, nil, elist, nil, elist and elist.__loaded_files)
 			elseif kind == "object" then
 				if add_zone_lists then elist = table.clone(self.zone.object_list) end
-				self.object_list = self.zone.object_class:loadList(files, nil, elist, nil, elist and elist.__loaded_files)
+				self.object_list = self.zone.object_class:loadList(files, nil, elist, nil, elist and table.clone(elist.__loaded_files))
+--				self.object_list = self.zone.object_class:loadList(files, nil, elist, nil, elist and elist.__loaded_files)
 			elseif kind == "actor" then
 				if add_zone_lists then elist = table.clone(self.zone.npc_list) end
-				self.npc_list = self.zone.npc_class:loadList(files, nil, elist, nil, elist and elist.__loaded_files)
+				self.npc_list = self.zone.npc_class:loadList(files, nil, elist, nil, elist and table.clone(elist.__loaded_files))
+--				self.npc_list = self.zone.npc_class:loadList(files, nil, elist, nil, elist and elist.__loaded_files)
 			else
 				error("kind unsupported")
 			end
@@ -196,24 +200,23 @@ function _M:tmxLoad(file)
 	end
 	for _, tileset in ipairs(map:findAll("tileset")) do
 		if tileset:findOne("properties") then for name, value in pairs(tileset:findOne("properties"):findAllAttrs("property", "name", "value")) do
-			-- setting .add_zone_lists = true in the file table causes the specified entity lists to be merged into a clone of the zone list
 			local elist
 			if name == "load_terrains" then
 				local list = self:loadLuaInEnv(g, nil, "return "..value) or {}
-				if list.add_zone_lists then elist = table.clone(self.zone.grid_list) end
-				self.grid_list = self.zone.grid_class:loadList(list, nil, elist, nil, elist and elist.__loaded_files)
+				elist = table.clone(self.zone.grid_list, false)
+				self.grid_list = self.zone.grid_class:loadList(list, nil, elist, nil, elist and table.clone(elist.__loaded_files))
 			elseif name == "load_traps" then
 				local list = self:loadLuaInEnv(g, nil, "return "..value) or {}
-				if list.add_zone_lists then elist = table.clone(self.zone.trap_list) end
-				self.trap_list = self.zone.trap_class:loadList(list, nil, elist, nil, elist and elist.__loaded_files)
+				elist = table.clone(self.zone.trap_list, false)
+				self.trap_list = self.zone.trap_class:loadList(list, nil, elist, nil, elist and table.clone(elist.__loaded_files))
 			elseif name == "load_objects" then
 				local list = self:loadLuaInEnv(g, nil, "return "..value) or {}
-				if list.add_zone_lists then elist = table.clone(self.zone.object_list) end
-				self.object_list = self.zone.object_class:loadList(list, nil, elist, nil, elist and elist.__loaded_files)
+				elist = table.clone(self.zone.object_list, false)
+				self.object_list = self.zone.object_class:loadList(list, nil, elist, nil, elist and table.clone(elist.__loaded_files))
 			elseif name == "load_actors" then
 				local list = self:loadLuaInEnv(g, nil, "return "..value) or {}
-				if list.add_zone_lists then elist = table.clone(self.zone.npc_list) end
-				self.npc_list = self.zone.npc_class:loadList(list, nil, elist, nil, elist and elist.__loaded_files)
+				elist = table.clone(self.zone.npc_list, false)
+				self.npc_list = self.zone.npc_class:loadList(list, nil, elist, nil, elist and table.clone(elist.__loaded_files))
 			end
 		end end
 
diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua
index 8f20b253ef1599b423689d5d33970479d4a9ae4a..1ff80c6541d381f8b391dd98f5136f881071d3de 100644
--- a/game/engines/default/engine/utils.lua
+++ b/game/engines/default/engine/utils.lua
@@ -65,6 +65,16 @@ function table.concatNice(t, sep, endsep)
 	return table.concat(t, sep, 1, #t - 1)..endsep..t[#t]
 end
 
+function ripairs(t)
+	local i = #t
+	return function()
+		if i == 0 then return nil end
+		local oi = i
+		i = i - 1
+		return oi, t[oi]
+	end
+end
+
 function table.count(t)
 	local i = 0
 	for k, v in pairs(t) do
@@ -2247,6 +2257,20 @@ function rng.poissonProcess(k, turn_scale, rate)
 	return math.exp(-rate*turn_scale) * ((rate*turn_scale) ^ k)/ util.factorial(k)
 end
 
+function rng.rarityTable(t, rarity_field)
+	if #t == 0 then return end
+
+	rarity_field = rarity_field or "rarity"
+	local max = 0
+	for _, e in ipairs(t) do max = max + e[rarity_field] end
+	local rt = {}
+	for _, e in ipairs(t) do
+		local nb = math.floor(max / e[rarity_field])
+		for i = 1, nb do rt[#rt+1] = e end
+	end
+	return rng.table(rt)
+end
+
 function util.show_function_calls()
 	debug.sethook(function(event, line)
 		local t = debug.getinfo(2)
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 3e2722f453b40747c519d3c52e0e0848adfe0504..aa212831af370166c82020e8abb0930b67510fcd 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -2529,7 +2529,7 @@ function _M:onTakeHit(value, src, death_note)
 			if self.clone_base then a = self.clone_base:clone() else a = self:clone() end
 			a.life = math.max(1, self.life - value / 2)
 			a.clone_on_hit.chance = math.ceil(self.clone_on_hit.chance / 2)
-			a.energy.val = 0
+			a.energy.value = 0
 			a.exp_worth = 0.1
 			a.inven = {}
 			a:removeAllMOs()
@@ -3148,6 +3148,10 @@ function _M:die(src, death_note)
 		elseif self.rank >= 3.2 and self.rank < 3.5 then p.all_kills_kind.rare = (p.all_kills_kind.rare or 0) + 1
 		elseif self.rank >= 3.5 then p.all_kills_kind.boss = (p.all_kills_kind.boss or 0) + 1
 		end
+
+		if game.level and game.level.data.record_player_kills then
+			game.level.data.record_player_kills = game.level.data.record_player_kills + 1
+		end
 	end
 
 	-- Ingredients
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 9975a8bd9293175930d298de1b13b9b95e089b76..8386e58940f1c015063f8ec0b9f512adc3f274f0 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -1780,7 +1780,7 @@ function _M:setupCommands()
 			print("===============")
 		end end,
 		[{"_g","ctrl"}] = function() if config.settings.cheat then
-			self:changeLevel(1, "orcs+steam-quarry")
+			self:changeLevel(game.level.level + 1)
 do return end
 			local o = game.zone:makeEntity(game.level, "object", {subtype="sling", random_object=true}, nil, true)
 			if o then
diff --git a/game/modules/tome/class/GameState.lua b/game/modules/tome/class/GameState.lua
index 9e0158042ababe868f55d331588ad51921f03912..e299eb8b9f14ade9c3951eea646048bd6df356f6 100644
--- a/game/modules/tome/class/GameState.lua
+++ b/game/modules/tome/class/GameState.lua
@@ -389,6 +389,7 @@ end
 -- @param data.lev = character level to generate for (affects point budget, #themes and #powers) <12-50>
 -- @param data.power_points_factor = lev based power points multiplier <1>
 -- @param data.nb_points_add = #extra budget points to spend on random powers <0>
+-- @param data.nb_powers_add = #extra random powers to add <0>
 -- @param data.powers_special = function(p) that must return true on each random power to add (from base.randart_able)
 -- @param data.nb_themes = #power themes (power groups) for random powers to use <scales to 5 with lev>
 -- @param data.force_themes = additional power theme(s) to use for random powers = {"attack", "arcane", ...}
@@ -1524,7 +1525,7 @@ function _M:entityFilterPost(zone, level, type, e, filter)
 								return (not e.unique and e.randart_able) and (not e.material_level or e.material_level >= 1) and true or false
 							end}
 						}
-						local o = game.state:generateRandart(fil,nil, true)
+						local o = game.state:generateRandart(fil, nil, true)
 						if o then
 --							print("[entityFilterPost]: Generated random object for", tostring(b.name))
 							o.unique, o.randart, o.rare = nil, nil, true
@@ -1544,21 +1545,25 @@ function _M:entityFilterPost(zone, level, type, e, filter)
 			e = self:createRandomBoss(e, table.merge(base, filter.random_elite, true))
 		end
 	elseif type == "object" then
-		if filter.random_object and not e.unique and e.randart_able then
-			local data = _G.type(filter.random_object) == "table" and filter.random_object or {}
-			local lev = math.max(1, game.zone:level_adjust_level(game.level, game.zone, "object"))
-			print("[entityFilterPost]: Generating obsolete random_object")
-			print(debug.traceback())
-			e = game.state:generateRandart{
-				lev = lev,
-				egos = 0,
-				nb_powers_add = data.nb_powers_add or 2, 
-				nb_points_add = data.nb_points_add or 4, -- ~1 ego Note: resolvers conflicts prevent specifying egos here
-				force_themes = data.force_themes or nil,
-				base = e,
-				post = function(o) o.rare = true o.unique = nil o.randart = nil end,
-				namescheme = 3
+		if filter.random_object and not e.unique and e.randart_able then -- convert the object to a (weak) Randart
+			local f_data = _G.type(filter.random_object) == "table" and filter.random_object or {}
+			-- default parameters
+			local data = {base = e, egos = 1, nb_powers_add = 1, nb_points_add = 2,
+				lev = math.max(1, game.zone:level_adjust_level(game.level, game.zone, "object")),
+				post = function(o)
+					if f_data.post then f_data.post(o) end
+					o.rare = true o.unique = nil o.randart = nil 
+				end,
+				namescheme = 3,
 			}
+			-- update with filter specifications
+			table.merge(data, f_data, false, {base=true, post=true, base_filter=true})
+			print("[entityFilterPost]: filter.random_object forcing conversion to Randart:", e.name) table.print(data)
+			e = game.state:generateRandart(data)
+			if not e then
+				print("[GameState:entityFilterPost] failed to generate random object, data:") table.print(data)
+				print("traceback:") print(debug.traceback())
+			end
 		end
 	end
 	return e
@@ -2251,8 +2256,8 @@ function _M:locationRevealAround(x, y)
 	game.level.map.lites(x, y, true)
 	game.level.map.remembers(x, y, true)
 	for _, c in pairs(util.adjacentCoords(x, y)) do
-		game.level.map.lites(x+c[1], y+c[2], true)
-		game.level.map.remembers(x+c[1], y+c[2], true)
+		game.level.map.lites(c[1], c[2], true)
+		game.level.map.remembers(c[1], c[2], true)
 	end
 end
 
@@ -2461,3 +2466,182 @@ function _M:allowOnlineEvent()
 	if self.birth.grab_online_event_forbid then return end
 	return true
 end
+
+function _M:infiniteDungeonChallenge(zone, lev, data, id_layout_name, id_grids_name)
+	if lev < 3 then return end
+	-- if not rng.percent(30 + lev) then return end
+
+	local challenges = {
+		{ id = "pacifist", rarity = 3 },
+		{ id = "exterminator", rarity = 1 },
+		{ id = "dream-horror", rarity = 10, min_lev = 15 },
+		{ id = "fast-exit", rarity = 3, min_lev = 8 },
+	}
+	self:triggerHook{"InfiniteDungeon:getChallenges", challenges=challenges}
+
+	for i, c in ripairs(challenges) do
+		if c.min_lev and lev < c.min_lev then table.remove(challenges, i) end
+	end
+
+	local challenge = rng.rarityTable(challenges)
+	data.id_challenge = challenge.id
+	data.id_layout_name = id_layout_name
+	data.id_grids_name = id_grids_name
+	print("[INFINITE DUNGEON] Selected challenge", data.id_challenge)
+end
+
+function _M:makeChallengeQuest(level, name, desc, data)
+	local q = {
+		id = "id-challenge-"..level.level,
+		name = "Level "..level.level.." Challenge: "..name,
+		challenge_desc = desc,
+		desc = function(self, who)
+			local desc = {}
+			desc[#desc+1] = self.challenge_desc
+			return table.concat(desc, "\n")
+		end,
+		on_status_change = function(self, who, status, sub)
+			if self:isCompleted() then
+				who:setQuestStatus(self.id, engine.Quest.DONE)
+				game:getPlayer(true):removeEffect(who.EFF_ZONE_AURA_CHALLENGE, true, true)
+				self:check("on_challenge_success", who)
+			elseif self:isFailed() then
+				game:getPlayer(true):removeEffect(who.EFF_ZONE_AURA_CHALLENGE, true, true)
+				self:check("on_challenge_failed", who)
+			end
+		end,
+		on_exit_level = function(self, who)
+			self:check("on_exit_check", who)
+			if self.status ~= self.DONE then
+				who:setQuestStatus(self.id, self.FAILED)
+			end
+		end,
+		on_challenge_success = function(self, who)
+			game.state:infiniteDungeonChallengeReward(self, who)
+		end,
+		popup_text = {},
+	}
+	table.merge(q, data)
+	local p = game:getPlayer(true)
+	p:grantQuest(q)
+	game:onTickEnd(function() p:setEffect(p.EFF_ZONE_AURA_CHALLENGE, 1, {id_challenge_quest = q.id}) end)
+	return q
+end
+
+
+function _M:infiniteDungeonChallengeFinish(zone, level)
+	local id_challenge = level.data.id_challenge
+	if not id_challenge then return end
+
+	if id_challenge == "pacifist" then
+		level.data.record_player_kills = 0
+		self:makeChallengeQuest(level, "Pacifist", "Get to the end of the level without killing a single creature.", {
+			on_exit_check = function(self, who)
+				if not self.check_level then return end
+				if self.check_level.data.record_player_kills == 0 then who:setQuestStatus(self.id, self.COMPLETED) end
+				self.check_level = nil
+			end,
+			on_kill_foe = function(self, who, target)
+				who:setQuestStatus(self.id, self.FAILED)
+			end,
+			check_level = level,
+		})
+	elseif id_challenge == "exterminator" then
+		self:makeChallengeQuest(level, "Exterminator", "Exit the level with no single foes left alive.", {
+			on_exit_check = function(self, who)
+				if not self.check_level then return end
+				local nb = 0
+				for uid, e in pairs(self.check_level.entities) do
+					if who:reactionToward(e) < 0 then nb = nb + 1 break end
+				end
+				if nb == 0 then who:setQuestStatus(self.id, self.COMPLETED) end
+				self.check_level = nil
+			end,
+			on_kill_foe = function(self, who, target)
+				if not self.check_level then return end
+				local nb = 0
+				for uid, e in pairs(self.check_level.entities) do
+					if who:reactionToward(e) < 0 then nb = nb + 1 break end
+				end
+				if nb == 0 then who:setQuestStatus(self.id, self.COMPLETED) end
+			end,
+			check_level = level,
+		})
+	elseif id_challenge == "fast-exit" then
+		local a = require("engine.Astar").new(level.map, game:getPlayer(true))
+		local path = a:calc(level.default_up.x, level.default_up.y, level.default_down.x, level.default_down.y)
+		if path and #path > 5 then
+			local turns = #path * 3
+			self:makeChallengeQuest(level, "Rush Hour ("..turns..")", "Leave the level in less than "..turns.." turns (exit is revealed on your map).", {
+				turns_left = turns,
+				desc = function(self, who)
+					local desc = {}
+					desc[#desc+1] = self.challenge_desc
+					desc[#desc+1] = ""
+					desc[#desc+1] = "Turns left: #LIGHT_GREEN#"..self.turns_left
+					return table.concat(desc, "\n")
+				end,
+				on_exit_check = function(self, who)
+					if self.turns_left >= 0 then who:setQuestStatus(self.id, self.COMPLETED) end
+				end,
+				on_act_base = function(self, who)
+					self.turns_left = self.turns_left - 1
+					if self.turns_left < 0 then
+						who:setQuestStatus(self.id, self.FAILED)
+					end
+				end,
+			})
+			self:locationRevealAround(level.default_down.x, level.default_down.y)
+		end
+	elseif id_challenge == "dream-horror" then
+		local m = zone:makeEntity(level, "actor", {name="dreaming horror", random_boss=true}, nil, true)
+		if m then
+			local x, y = rng.range(1, level.map.w - 2), rng.range(1, level.map.h - 2)
+			local tries = 0
+			while not m:canMove(x, y) and tries < 100 do
+				x, y = rng.range(1, level.map.w - 2), rng.range(1, level.map.h - 2)
+				tries = tries + 1
+			end
+			if tries < 100 then
+				local q = self:makeChallengeQuest(level, "Dream Hunter", "Wake up and kill the dreaming horror boss '"..m.name.."'.", {})
+				m.id_challenge_quest = q.id
+				m.on_die = function(self, who)
+					who:setQuestStatus(self.id_challenge_quest, engine.Quest.COMPLETED)
+				end
+				zone:addEntity(level, m, "actor", x, y)
+			end
+		end
+	else
+		self:triggerHook{"InfiniteDungeon:setupChallenge", id_challenge=id_challenge, zone=zone, level=level}
+	end
+end
+
+function _M:infiniteDungeonChallengeReward(quest, who)
+	local rewards = {
+		{name = "Random Artifact", rarity=1, give=function(who)
+			local tries = 100
+			while tries > 0 do
+				local o = game.zone:makeEntity(game.level, "object", {random_object={egos=rng.range(2,3), nb_powers_add=rng.range(10,30)}, properties={"randart_able"}}, nil, true)
+				if o then
+					o:identify(true)
+					who:addObject(who.INVEN_INVEN, o)
+					who:sortInven()
+					return "Random Artifact: "..o:getName{do_color=true}
+				end
+			end
+			-- Fallback
+			who.unused_stats = who.unused_stats + 3
+			return "+3 Stat Points"
+		end},
+		{name = "+3 Stat Points", rarity=3, give=function(who) who.unused_stats = who.unused_stats + 3 end},
+		{name = "+1 Class Point", rarity=5, give=function(who) who.unused_talents = who.unused_talents + 1 end},
+		{name = "+1 Generic Point", rarity=4, give=function(who) who.unused_generics = who.unused_generics + 1 end},
+		{name = "+1 Category Point", rarity=30, give=function(who) who.unused_talents_types = who.unused_talents_types + 1 end},
+		{name = "+1 Prodigy Point", rarity=60, give=function(who) who.unused_prodigies = who.unused_prodigies + 1 end},
+	}
+	self:triggerHook{"InfiniteDungeon:getRewards", rewards=rewards}
+
+	local reward = rng.rarityTable(rewards)
+	reward.name = reward.give(who) or reward.name
+	quest.popup_text[engine.Quest.DONE] = "#OLIVE_DRAB#Reward: "..reward.name
+end
diff --git a/game/modules/tome/data/gfx/effects/zone_aura_challenge.png b/game/modules/tome/data/gfx/effects/zone_aura_challenge.png
new file mode 100644
index 0000000000000000000000000000000000000000..fbe56c3cbda0e5bf317782a12d51ffae7e679188
Binary files /dev/null and b/game/modules/tome/data/gfx/effects/zone_aura_challenge.png differ
diff --git a/game/modules/tome/data/maps/vaults/test2.tmx b/game/modules/tome/data/maps/vaults/test2.tmx
index 8002a4e0245b4d8e6a6fe73538022b7ee724bc6f..fe72626aef0e5c41b2256e21db59f105deafc5cd 100644
--- a/game/modules/tome/data/maps/vaults/test2.tmx
+++ b/game/modules/tome/data/maps/vaults/test2.tmx
@@ -125,7 +125,7 @@
  <tileset firstgid="101" name="Trees" tilewidth="64" tileheight="64" tilecount="100">
   <properties>
    <property name="load_terrains" value="{&quot;/data/general/grids/forest.lua&quot;, &quot;/data/general/grids/autumn_forest.lua&quot;, &quot;/data/general/grids/snowy_forest.lua&quot;, &quot;/data/general/grids/lava.lua&quot;, add_zone_lists=true}"/>
-   <property name="load_actors" value="{&quot;/data/general/npcs/cold-drake.lua&quot;, &quot;/data/general/npcs/vampire.lua&quot;, add_zone_lists=false}"/>
+   <property name="load_actors" value="{&quot;/data/general/npcs/cold-drake.lua&quot;, &quot;/data/general/npcs/vampire.lua&quot;}"/>
   </properties>
   <image source="../../../../../../tiled-maps/gfx/vault_trees.png" width="640" height="640"/>
   <tile id="0">
diff --git a/game/modules/tome/data/talents/celestial/hymns.lua b/game/modules/tome/data/talents/celestial/hymns.lua
index 28de24d7c15ff79578478ae2f0c248f34b1b3de8..a9bd8e19bf902ed1d3b41c438b6aa61d74fa37dc 100644
--- a/game/modules/tome/data/talents/celestial/hymns.lua
+++ b/game/modules/tome/data/talents/celestial/hymns.lua
@@ -172,7 +172,7 @@ newTalent{
 		if self:getNegative() < t.getNegativeDrain(self, t) then return end
 
 		local tgts = {}
-		local grids = core.fov.circle_grids(self.x, self.y, 5, true)
+		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), true)
 		for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do
 			local a = game.level.map(x, y, Map.ACTOR)
 			if a and self:reactionToward(a) < 0 then
diff --git a/game/modules/tome/data/talents/misc/npcs.lua b/game/modules/tome/data/talents/misc/npcs.lua
index 31de27cb0a8c01c1ca8323a2bdd2770375d5bae3..da99a95ce0c53c5e9036bda91b205b9b4d1263a9 100644
--- a/game/modules/tome/data/talents/misc/npcs.lua
+++ b/game/modules/tome/data/talents/misc/npcs.lua
@@ -68,7 +68,7 @@ newTalent{
 		local a
 		if self.clone_base then a = self.clone_base:clone() else a = self:clone() end
 		a.can_multiply = a.can_multiply - 1
-		a.energy.val = 0
+		a.energy.value = 0
 		a.exp_worth = 0.1
 		a.inven = {}
 		a.x, a.y = nil, nil
diff --git a/game/modules/tome/data/timed_effects/other.lua b/game/modules/tome/data/timed_effects/other.lua
index 2426a14f2c1cb5fe0cb1f30e40a62b22cfc1870c..1dd999ba5000b23adcc34e0735313209e5de1654 100644
--- a/game/modules/tome/data/timed_effects/other.lua
+++ b/game/modules/tome/data/timed_effects/other.lua
@@ -3042,6 +3042,33 @@ newEffect{
 	end,
 }
 
+newEffect{
+	name = "ZONE_AURA_CHALLENGE",
+	desc = "Challenge",
+	no_stop_enter_worlmap = true,
+	long_desc = function(self, eff) if not eff.id_challenge_quest or not self:hasQuest(eff.id_challenge_quest) then return "???" else return self:hasQuest(eff.id_challenge_quest).name end end,
+	decrease = 0, no_remove = true,
+	type = "other",
+	subtype = { aura=true },
+	status = "neutral",
+	zone_wide_effect = true,
+	parameters = {},
+	activate = function(self, eff)
+	end,
+	deactivate = function(self, eff)
+		if not eff.id_challenge_quest or not self:hasQuest(eff.id_challenge_quest) then return end
+		self:hasQuest(eff.id_challenge_quest):check("on_exit_level", self)
+	end,
+	callbackOnKill = function(self, eff, who, death_note)
+		if not eff.id_challenge_quest or not self:hasQuest(eff.id_challenge_quest) then return end
+		self:hasQuest(eff.id_challenge_quest):check("on_kill_foe", self)
+	end,
+	callbackOnActBase = function(self, eff)
+		if not eff.id_challenge_quest or not self:hasQuest(eff.id_challenge_quest) then return end
+		self:hasQuest(eff.id_challenge_quest):check("on_act_base", self)
+	end,
+}
+
 newEffect{
 	name = "THROWING_KNIVES", image = "talents/throwing_knives.png",
 	desc = "Throwing Knives",  decrease = 0,
diff --git a/game/modules/tome/data/zones/infinite-dungeon/grids.lua b/game/modules/tome/data/zones/infinite-dungeon/grids.lua
index 09827e217f3b1f79ff8dff9458a24ddc22aaa230..f551229ce3707ec258157d8b4e338af7b40b1588 100644
--- a/game/modules/tome/data/zones/infinite-dungeon/grids.lua
+++ b/game/modules/tome/data/zones/infinite-dungeon/grids.lua
@@ -22,21 +22,279 @@ load("/data/general/grids/water.lua")
 load("/data/general/grids/forest.lua")
 load("/data/general/grids/lava.lua")
 load("/data/general/grids/sand.lua")
+load("/data/general/grids/underground.lua")
+load("/data/general/grids/slime.lua")
+load("/data/general/grids/jungle.lua")
+load("/data/general/grids/cave.lua")
+load("/data/general/grids/burntland.lua")
+load("/data/general/grids/mountain.lua")
+load("/data/general/grids/void.lua")
+load("/data/general/grids/autumn_forest.lua")
+load("/data/general/grids/snowy_forest.lua")
 
-newEntity{base = "FLOOR", define_as = "ITEMS_VAULT"}
-load("/data-items-vault/entities/fortress-grids.lua", function(e) if e.image == "terrain/solidwall/solid_floor1.png" then e.image = "terrain/marble_floor.png" end end)
+newEntity{
+	define_as = "GRASS_ROCK",
+	type = "wall", subtype = "grass",
+	name = "huge loose rock", image = "terrain/grass.png", add_mos = {{image="terrain/huge_rock.png"}},
+	display = '+', color=colors.GREY, back_color={r=44,g=95,b=43},
+	notice = true,
+	always_remember = true,
+	block_sight = true,
+	is_door = true,
+	door_opened = "GRASS",
+	dig = "GRASS",
+	nice_editer = { method="borders_def", def="grass"},
+}
+
+newEntity{
+	define_as = "UNDERGROUND_ROCK",
+	type = "wall", subtype = "underground",
+	name = "huge loose rock", image = "terrain/underground_floor.png", add_mos = {{image="terrain/huge_rock.png"}},
+	display = '+', color=colors.GREY, back_color={r=44,g=95,b=43},
+	notice = true,
+	always_remember = true,
+	block_sight = true,
+	is_door = true,
+	door_opened = "UNDERGROUND_FLOOR",
+	dig = "UNDERGROUND_FLOOR",
+}
+
+newEntity{
+	define_as = "CRYSTAL_ROCK",
+	type = "wall", subtype = "crystal",
+	name = "huge loose rock", image = "terrain/crystal_floor1.png", add_mos = {{image="terrain/huge_rock.png"}},
+	display = '+', color=colors.GREY, back_color={r=44,g=95,b=43},
+	notice = true,
+	always_remember = true,
+	block_sight = true,
+	is_door = true,
+	door_opened = "CRYSTAL_FLOOR",
+	dig = "CRYSTAL_FLOOR",
+}
+
+newEntity{
+	define_as = "DESERT_ROCK",
+	type = "wall", subtype = "sand",
+	name = "huge loose rock", image = "terrain/sandfloor.png", add_mos = {{image="terrain/huge_rock.png"}},
+	display = '+', color=colors.GREY, back_color={r=44,g=95,b=43},
+	notice = true,
+	always_remember = true,
+	block_sight = true,
+	is_door = true,
+	door_opened = "SAND",
+	dig = "SAND",
+}
 
-for id, i in ipairs{1, 10, 20, 30, 40} do
 newEntity{
-	define_as = "ID_HISTORY"..i,
-	name = "The Hunter and the Hunted", lore="infinite-dungeon-history-"..id,
-	desc = [[The infinite hunt.]],
-	image = "terrain/marble_floor.png",
-	display = '_', color=colors.GREEN, back_color=colors.DARK_GREY,
-	add_displays = {class.new{image="terrain/signpost.png"}},
+	define_as = "CAVE_ROCK",
+	type = "wall", subtype = "grass",
+	name = "huge loose rock", image = "terrain/cave/cave_floor_1_01.png", add_mos = {{image="terrain/huge_rock.png"}},
+	display = '+', color=colors.GREY, back_color={r=44,g=95,b=43},
+	notice = true,
 	always_remember = true,
-	on_move = function(self, x, y, who)
-		if who.player then game.party:learnLore(self.lore) end
-	end,
+	block_sight = true,
+	is_door = true,
+	door_opened = "CAVEFLOOR",
+	dig = "CAVEFLOOR",
 }
+
+newEntity{
+	define_as = "JUNGLE_ROCK",
+	type = "wall", subtype = "grass",
+	name = "huge loose rock", image = "terrain/jungle/jungle_grass_floor_01.png", add_mos = {{image="terrain/huge_rock.png"}},
+	display = '+', color=colors.GREY, back_color={r=44,g=95,b=43},
+	notice = true,
+	always_remember = true,
+	block_sight = true,
+	is_door = true,
+	door_opened = "JUNGLE_GRASS",
+	dig = "JUNGLE_GRASS",
+}
+
+-- add snowy door
+newEntity{
+	define_as = "SNOWY_GRASS_2",
+	type = "floor", subtype = "snowy_grass",
+	name = "snowy grass", image = "terrain/grass/snowy_grass_main_01.png",
+	display = '.', color=colors.LIGHT_GREEN, back_color={r=44,g=95,b=43},
+	grow = "SNOWY_TREE_2",
+	nice_tiler = { method="replace", base={"SNOWY_PATCH_2", 100, 1, 14}},
+	nice_editer = snowy_editer,
+}
+for i = 1, 14 do newEntity{ base = "SNOWY_GRASS_2", define_as = "SNOWY_PATCH_2"..i, image = ("terrain/grass/snowy_grass_main_%02d.png"):format(i) } end
+
+local snowy_treesdef = {
+	{"small_elm", {"shadow", "trunk", "foliage_winter"}},
+	{"elm", {tall=-1, "shadow", "trunk", "foliage_winter"}},
+}
+
+newEntity{
+	define_as = "SNOWY_TREE_2",
+	type = "wall", subtype = "snowy_grass",
+	name = "winter tree",
+	image = "terrain/snowy_tree.png",
+	display = '#', color=colors.LIGHT_GREEN, back_color={r=44,g=95,b=43},
+	always_remember = true,
+	can_pass = {pass_tree=1},
+	does_block_move = true,
+	block_sight = true,
+	dig = "SNOWY_GRASS_2",
+	nice_tiler = { method="replace", base={"SNOWY_TREE_2", 100, 1, 30}},
+	nice_editer = snowy_editer,
+}
+for i = 1, 30 do
+	newEntity(class:makeNewTrees({base="SNOWY_TREE_2", define_as = "SNOWY_TREE_2"..i, image = "terrain/grass/snowy_grass_main_01.png"}, snowy_treesdef))
 end
+
+newEntity{
+	define_as = "RIFT2",
+	name = "Temporal Rift", add_mos={{image="terrain/demon_portal2.png"}},
+	display = '&', color_r=255, color_g=0, color_b=220, back_color=colors.VIOLET,
+	notice = true,
+	always_remember = true,
+	show_tooltip = true,
+	desc = [[The rift leads to another floor of the dungeon.]],
+	change_level = 1,
+}
+
+local rift_editer = { method="sandWalls_def", def="rift"}
+
+newEntity{
+	define_as = "SPACETIME_RIFT2",
+	type = "wall", subtype = "rift",
+	name = "crack in spacetime",
+	display = '#', color=colors.YELLOW, image="terrain/rift/rift_inner_05_01.png",
+	always_remember = true,
+	does_block_move = true,
+	_noalpha = false,
+	dig = "VOID",
+	block_sight = true,
+	nice_editer = rift_editer,
+}
+
+------------------------------------------------------------
+-- To fake people out.
+------------------------------------------------------------
+
+newEntity{
+	define_as = "WATER_FLOOR_FAKE",
+	type = "floor", subtype = "underwater",
+	name = "underwater", image = "terrain/underwater/subsea_floor_02.png",
+	display = '.', color=colors.LIGHT_BLUE, back_color=colors.DARK_BLUE,
+	nice_tiler = { method="replace", base={"WATER_FLOOR_FAKE", 10, 1, 5}},
+}
+for i = 1, 5 do newEntity{ base="WATER_FLOOR_FAKE", define_as = "WATER_FLOOR_FAKE"..i, image = "terrain/underwater/subsea_floor_02"..string.char(string.byte('a')+i-1)..".png" } end
+
+newEntity{
+	define_as = "WATER_WALL_FAKE",
+	type = "wall", subtype = "underwater",
+	name = "coral wall", image = "terrain/underwater/subsea_granite_wall1.png",
+	display = '#', color=colors.AQUAMARINE, back_color=colors.DARK_BLUE,
+	always_remember = true,
+	can_pass = {pass_wall=1},
+	does_block_move = true,
+	block_sight = true,
+	z = 3,
+	nice_tiler = { method="wall3d", inner={"WATER_WALL_FAKE", 100, 1, 5}, north={"WATER_WALL_NORTH_FAKE", 100, 1, 5}, south={"WATER_WALL_SOUTH_FAKE", 10, 1, 14}, north_south="WATER_WALL_NORTH_SOUTH_FAKE", small_pillar="WATER_WALL_SMALL_PILLAR_FAKE", pillar_2="WATER_WALL_PILLAR_2_FAKE", pillar_8={"WATER_WALL_PILLAR_8_FAKE", 100, 1, 5}, pillar_4="WATER_WALL_PILLAR_4_FAKE", pillar_6="WATER_WALL_PILLAR_6_FAKE" },
+	always_remember = true,
+	does_block_move = true,
+	can_pass = {pass_wall=1},
+	block_sight = true,
+	dig = "WATER_FLOOR_FAKE",
+}
+
+for i = 1, 5 do
+	newEntity{ base = "WATER_WALL_FAKE", define_as = "WATER_WALL_FAKE"..i, image = "terrain/underwater/subsea_granite_wall1_"..i..".png", z = 3}
+	newEntity{ base = "WATER_WALL_FAKE", define_as = "WATER_WALL_NORTH_FAKE"..i, image = "terrain/underwater/subsea_granite_wall1_"..i..".png", z = 3, add_displays = {class.new{image="terrain/underwater/subsea_granite_wall3.png", z=18, display_y=-1}}}
+	newEntity{ base = "WATER_WALL_FAKE", define_as = "WATER_WALL_PILLAR_8_FAKE"..i, image = "terrain/underwater/subsea_granite_wall1_"..i..".png", z = 3, add_displays = {class.new{image="terrain/underwater/subsea_granite_wall_pillar_8.png", z=18, display_y=-1}}}
+end
+newEntity{ base = "WATER_WALL_FAKE", define_as = "WATER_WALL_NORTH_SOUTH_FAKE", image = "terrain/underwater/subsea_granite_wall2.png", z = 3, add_displays = {class.new{image="terrain/underwater/subsea_granite_wall3.png", z=18, display_y=-1}}}
+newEntity{ base = "WATER_WALL_FAKE", define_as = "WATER_WALL_SOUTH_FAKE", image = "terrain/underwater/subsea_granite_wall2.png", z = 3}
+for i = 1, 14 do newEntity{ base = "WATER_WALL", define_as = "WATER_WALL_SOUTH_FAKE"..i, image = "terrain/underwater/subsea_granite_wall2_"..i..".png", z = 3} end
+newEntity{ base = "WATER_WALL_FAKE", define_as = "WATER_WALL_SMALL_PILLAR_FAKE", image = "terrain/underwater/subsea_floor_02.png", z=1, add_displays = {class.new{image="terrain/underwater/subsea_granite_wall_pillar_small.png",z=3}, class.new{image="terrain/underwater/subsea_granite_wall_pillar_small_top.png", z=18, display_y=-1}}}
+newEntity{ base = "WATER_WALL_FAKE", define_as = "WATER_WALL_PILLAR_6_FAKE", image = "terrain/underwater/subsea_floor_02.png", z=1, add_displays = {class.new{image="terrain/underwater/subsea_granite_wall_pillar_3.png",z=3}, class.new{image="terrain/underwater/subsea_granite_wall_pillar_9.png", z=18, display_y=-1}}}
+newEntity{ base = "WATER_WALL_FAKE", define_as = "WATER_WALL_PILLAR_4_FAKE", image = "terrain/underwater/subsea_floor_02.png", z=1, add_displays = {class.new{image="terrain/underwater/subsea_granite_wall_pillar_1.png",z=3}, class.new{image="terrain/underwater/subsea_granite_wall_pillar_7.png", z=18, display_y=-1}}}
+newEntity{ base = "WATER_WALL_FAKE", define_as = "WATER_WALL_PILLAR_2_FAKE", image = "terrain/underwater/subsea_floor_02.png", z=1, add_displays = {class.new{image="terrain/underwater/subsea_granite_wall_pillar_2.png",z=3}}}
+
+
+newEntity{
+	define_as = "WATER_DOOR_FAKE",
+	type = "wall", subtype = "underwater",
+	name = "door", image = "terrain/underwater/subsea_stone_wall_door_closed.png",
+	display = '+', color_r=238, color_g=154, color_b=77, back_color=colors.DARK_UMBER,
+	nice_tiler = { method="door3d", north_south="WATER_DOOR_VERT_FAKE", west_east="WATER_DOOR_HORIZ_FAKE" },
+	notice = true,
+	always_remember = true,
+	block_sight = true,
+	is_door = true,
+	door_opened = "WATER_DOOR_OPEN_FAKE",
+	dig = "WATER_FLOOR_FAKE",
+}
+newEntity{
+	define_as = "WATER_DOOR_OPEN_FAKE",
+	type = "wall", subtype = "underwater",
+	name = "open door", image="terrain/underwater/subsea_granite_door1_open.png",
+	display = "'", color_r=238, color_g=154, color_b=77, back_color=colors.DARK_GREY,
+	always_remember = true,
+	is_door = true,
+	door_closed = "WATER_DOOR_FAKE",
+}
+newEntity{ base = "WATER_DOOR_FAKE", define_as = "WATER_DOOR_HORIZ_FAKE", image = "terrain/underwater/subsea_stone_wall_door_closed.png", add_displays = {class.new{image="terrain/underwater/subsea_granite_wall3.png", z=18, display_y=-1}}, door_opened = "WATER_DOOR_HORIZ_OPEN_FAKE"}
+newEntity{ base = "WATER_DOOR_OPEN_FAKE", define_as = "WATER_DOOR_HORIZ_OPEN_FAKE", image = "terrain/underwater/subsea_floor_02.png", add_displays = {class.new{image="terrain/underwater/subsea_stone_store_open.png", z=17}, class.new{image="terrain/underwater/subsea_granite_wall3.png", z=18, display_y=-1}}, door_closed = "WATER_DOOR_HORIZ_FAKE"}
+newEntity{ base = "WATER_DOOR_FAKE", define_as = "WATER_DOOR_VERT_FAKE", image = "terrain/underwater/subsea_floor_02.png", add_displays = {class.new{image="terrain/underwater/subsea_granite_door1_vert.png", z=17}, class.new{image="terrain/underwater/subsea_granite_door1_vert_north.png", z=18, display_y=-1}}, door_opened = "WATER_DOOR_OPEN_VERT_FAKE", dig = "WATER_DOOR_OPEN_VERT_FAKE"}
+newEntity{ base = "WATER_DOOR_OPEN_FAKE", define_as = "WATER_DOOR_OPEN_VERT_FAKE", image = "terrain/underwater/subsea_floor_02.png", add_displays = {class.new{image="terrain/underwater/subsea_granite_door1_open_vert.png", z=17}, class.new{image="terrain/underwater/subsea_granite_door1_open_vert_north.png", z=18, display_y=-1}}, door_closed = "WATER_DOOR_VERT_FAKE"}
+
+newEntity{
+	define_as = "WATER_DOWN_FAKE",
+	image = "terrain/underwater/subsea_floor_02.png", add_mos = {{image="terrain/underwater/subsea_stair_down_03_64.png"}},
+	name = "next level",
+	display = '>', color_r=255, color_g=255, color_b=0,
+	notice = true,
+	always_remember = true,
+	change_level = 1,
+}
+
+local molten_lava_editer = {method="borders_def", def="molten_lava"}
+local lava_editer = {method="borders_def", def="lava"}
+local lava_mountain_editer = {method="borders_def", def="lava_mountain"}
+
+newEntity{
+	define_as = "LAVA_FLOOR_FAKE",
+	type = "floor", subtype = "lava",
+	name = "lava floor", image = "terrain/lava_floor.png",
+	display = '.', color=colors.RED, back_color=colors.DARK_GREY,
+	shader = "lava",
+	nice_tiler = { method="replace", base={"LAVA_FLOOR_FAKE", 100, 1, 16}},
+	nice_editer = lava_editer,
+}
+for i = 1, 16 do newEntity{ base = "LAVA_FLOOR_FAKE", define_as = "LAVA_FLOOR_FAKE"..i, image = "terrain/lava/lava_floor"..i..".png" } end
+
+newEntity{
+	define_as = "LAVA_WALL_FAKE",
+	type = "wall", subtype = "lava",
+	name = "lava wall", image = "terrain/lava/lava_mountain5.png",
+	display = '#', color=colors.RED, back_color=colors.DARK_GREY,
+	always_remember = true,
+	does_block_move = true,
+	block_sight = true,
+	nice_editer = lava_mountain_editer,
+	nice_tiler = { method="replace", base={"LAVA_WALL_FAKE", 70, 1, 6} },
+	dig = "LAVA_FLOOR_FAKE",
+}
+for i = 1, 6 do newEntity{ base="LAVA_WALL_FAKE", define_as = "LAVA_WALL_FAKE"..i, image = "terrain/lava/lava_mountain5_"..i..".png"} end
+
+newEntity{
+	define_as = "LAVA_DOWN_FAKE",
+	type = "floor", subtype = "lava",
+	name = "next level", image = "terrain/lava_floor.png", add_mos = {{image="terrain/stair_down.png"}},
+	display = '.', color=colors.RED, back_color=colors.DARK_GREY,
+	shader = "lava",
+	nice_tiler = { method="replace", base={"LAVA_FLOOR_FAKE", 100, 1, 16}},
+	nice_editer = lava_editer,
+	notice = true,
+	always_remember = true,
+	change_level = 1,
+}
+
+newEntity{base = "FLOOR", define_as = "ITEMS_VAULT"}
+load("/data-items-vault/entities/fortress-grids.lua", function(e) if e.image == "terrain/solidwall/solid_floor1.png" then e.image = "terrain/marble_floor.png" end end)
diff --git a/game/modules/tome/data/zones/infinite-dungeon/objects.lua b/game/modules/tome/data/zones/infinite-dungeon/objects.lua
index 2db5eaafa68549c97c4ddb6bd054e89063a936fb..fa570ece0be64d02349d06abf3f02208d3d64ff9 100644
--- a/game/modules/tome/data/zones/infinite-dungeon/objects.lua
+++ b/game/modules/tome/data/zones/infinite-dungeon/objects.lua
@@ -20,6 +20,17 @@
 load("/data/general/objects/objects-maj-eyal.lua")
 load("/data/general/objects/objects-far-east.lua")
 
+for id, i in ipairs{1, 10, 20, 30, 40} do
+newEntity{ base = "BASE_LORE",
+	define_as = "ID_HISTORY"..i,
+	name = "The Hunter and the Hunted", lore="infinite-dungeon-history-"..id,
+	desc = [[The infinite hunt.]],
+	image = "terrain/signpost.png",
+	rarity = false,
+	encumberance = 0,
+}
+end
+
 newEntity{
 	power_source = {technique=true},
 	unique = true,
diff --git a/game/modules/tome/data/zones/infinite-dungeon/zone.lua b/game/modules/tome/data/zones/infinite-dungeon/zone.lua
index 62dac442bc4a3bb2d5e4d413685a39613f8ec976..0474b67fb28b354b8d12fc4b34157a2226a97bd7 100644
--- a/game/modules/tome/data/zones/infinite-dungeon/zone.lua
+++ b/game/modules/tome/data/zones/infinite-dungeon/zone.lua
@@ -95,6 +95,135 @@ return {
 			nb_trap = {0, 0},
 		},
 	},
+	alter_level_data = function(zone, lev, data)
+		if lev < 3 or rng.percent(30) then game.state:infiniteDungeonChallenge(zone, lev, data, "default", "default") return end
+
+		-- Randomize the size of the dungeon, increasing it slightly as the game progresses.
+		-- Also change enemy count to fit with the new size.		
+		local size = 50
+		local vx = math.ceil(math.random(0.75, 1.25) * size)
+		local vy = math.ceil(math.random(0.75, 1.25) * size)
+		
+		-- Takent from random zone generation, modified slightly for use here.
+		-- Grab a random layout for the floor.		
+		local layouts = {
+			{
+				id_layout_name = "forest",
+				class = "engine.generator.map.Forest",
+				edge_entrances = rng.table{{2,8}, {4,6}, {6,4}, {8,2}},
+				zoom = rng.range(2,6),
+				sqrt_percent = rng.range(30, 50),
+				sqrt_percent = rng.range(5, 10),
+				noise = "fbm_perlin",
+			},
+			{
+				id_layout_name = "cavern",
+				class = "engine.generator.map.Cavern",
+				zoom = math.random(10, 20),
+				min_floor = math.floor(rng.range(vx * vy * 0.4 / 2, vx * vy * 0.4)),
+			},
+			{
+				id_layout_name = "default",
+				class = "engine.generator.map.Roomer",
+				nb_rooms = 14,
+				rooms = {"random_room", {"pit",3}, {"greater_vault",7}},
+				rooms_config = {pit={filters={}}},
+				lite_room_chance = 50,
+			},
+			{
+				id_layout_name = "maze",
+				class = "engine.generator.map.Maze",
+				widen_w = math.random(1,7), widen_h = math.random(1,7),
+			},
+			{
+				id_layout_name = "town",
+				class = "engine.generator.map.Town",
+				building_chance = math.random(50,90),
+				max_building_w = math.random(5,11), max_building_h = math.random(5,11),
+				edge_entrances = {6,4},
+				nb_rooms = math.random(1,2),
+				rooms = {{"greater_vault",2}},
+			},
+			{
+				id_layout_name = "building",
+				class = "engine.generator.map.Building",
+				lite_room_chance = rng.range(0, 100),
+				max_block_w = rng.range(7, 20), max_block_h = rng.range(7, 20),
+				max_building_w = rng.range(2, 8), max_building_h = rng.range(2, 8),
+			},
+			{
+				id_layout_name = "octopus",
+				class = "engine.generator.map.Octopus",
+				main_radius = {0.3, 0.4},
+				arms_radius = {0.1, 0.2},
+				arms_range = {0.7, 0.8},
+				nb_rooms = {3, 9},
+			},
+			{
+				id_layout_name = "hexa",
+				class = "engine.generator.map.Hexacle",
+				segment_wide_chance = 70,
+				nb_segments = 8,
+				nb_layers = 6,
+				segment_miss_percent = 10,
+				force_square_size = true,
+			},
+		}
+		zone:triggerHook{"InfiniteDungeon:getLayouts", layouts=layouts}
+		
+		local layout = rng.table(layouts)
+		data.generator.map = layout
+		
+		local vgrids = {
+			{id_grids_name="tree", floor="GRASS", wall="TREE", door="GRASS_ROCK", down="GRASS_DOWN2"},
+			{id_grids_name="wall", floor="FLOOR", wall="WALL", door="DOOR", down="DOWN"},
+			{id_grids_name="underground", floor="UNDERGROUND_FLOOR", wall="UNDERGROUND_TREE", door="UNDERGROUND_ROCK", down="UNDERGROUND_LADDER_DOWN"},
+			{id_grids_name="crystals", floor="CRYSTAL_FLOOR", wall={"CRYSTAL_WALL","CRYSTAL_WALL2","CRYSTAL_WALL3","CRYSTAL_WALL4","CRYSTAL_WALL5","CRYSTAL_WALL6","CRYSTAL_WALL7","CRYSTAL_WALL8","CRYSTAL_WALL9","CRYSTAL_WALL10","CRYSTAL_WALL11","CRYSTAL_WALL12","CRYSTAL_WALL13","CRYSTAL_WALL14","CRYSTAL_WALL15","CRYSTAL_WALL16","CRYSTAL_WALL17","CRYSTAL_WALL18","CRYSTAL_WALL19","CRYSTAL_WALL20",}, door="CRYSTAL_ROCK", down="CRYSTAL_LADDER_DOWN"},
+			{id_grids_name="sand", floor="UNDERGROUND_SAND", wall="SANDWALL", door="UNDERGROUND_SAND", down="SAND_LADDER_DOWN"},
+			{id_grids_name="desert", floor="SAND", wall="PALMTREE", door="DESERT_ROCK", down="SAND_DOWN2"},
+			{id_grids_name="slime", floor="SLIME_FLOOR", wall="SLIME_WALL", door="SLIME_DOOR", down="SLIME_DOWN"},
+			{id_grids_name="jungle", floor="JUNGLE_GRASS", wall="JUNGLE_TREE", door="JUNGLE_ROCK", down="JUNGLE_GRASS_DOWN2"},
+			{id_grids_name="cave", floor="CAVEFLOOR", wall="CAVEWALL", door="CAVE_ROCK", down="CAVE_LADDER_DOWN"},
+			{id_grids_name="burntland", floor="BURNT_GROUND", wall="BURNT_TREE", door="BURNT_GROUND", down="BURNT_DOWN6"},
+			{id_grids_name="mountain", floor="ROCKY_GROUND", wall="MOUNTAIN_WALL", door="ROCKY_GROUND", down="ROCKY_DOWN2"},
+			{id_grids_name="mountain_forest", floor="ROCKY_GROUND", wall="ROCKY_SNOWY_TREE", door="ROCKY_GROUND", down="ROCKY_DOWN2"},
+			{id_grids_name="snowy_forest", floor="SNOWY_GRASS_2", wall="SNOWY_TREE_2", door="SNOWY_GRASS_2", down="snowy_DOWN2"},
+			{id_grids_name="temporal_void", floor="VOID", wall="SPACETIME_RIFT2", door="VOID", down="RIFT2"},
+			{id_grids_name="water", floor="WATER_FLOOR_FAKE", wall="WATER_WALL_FAKE", door="WATER_DOOR_FAKE", down="WATER_DOWN_FAKE"},
+			{id_grids_name="lava", floor="LAVA_FLOOR_FAKE", wall="LAVA_WALL_FAKE", door="LAVA_FLOOR_FAKE", down="LAVA_DOWN_FAKE"},
+			{id_grids_name="autumn_forest", floor="AUTUMN_GRASS", wall="AUTUMN_TREE", door="AUTUMN_GRASS", down="AUTUMN_GRASS_DOWN2"},
+		}
+		zone:triggerHook{"InfiniteDungeon:getGrids", grids=vgrids}
+		local vgrid = rng.table(vgrids)
+		
+		data.generator.map.floor = vgrid.floor
+		data.generator.map['.'] = vgrid.floor
+		data.generator.map.external_floor = vgrid.floor
+		data.generator.map.wall = vgrid.wall
+		data.generator.map['#'] = vgrid.wall
+		data.generator.map.up = vgrid.floor
+		data.generator.map.down = vgrid.down
+		data.generator.map.door = vgrid.door
+		data.generator.map["'"] = vgrid.door
+
+		data.width = vx
+		data.height = vy
+		if data.generator.map.widen_w then
+			-- Special sanity check. Maze generation tends to... mess up if their height/width values aren't multiplies of the tunnel sizes.
+			while data.width % data.generator.map.widen_w ~= 0 do data.width = data.width + 1 end
+			while data.height % data.generator.map.widen_h ~= 0 do data.height = data.height + 1 end
+		end
+
+		if layout.force_square_size then
+			data.width = math.max(vx, vy)
+			data.height = data.width
+		end
+
+		local enemy_count = math.ceil((vx + vy) * 0.35)
+		data.generator.actor.nb_npc = {enemy_count-5, enemy_count+5}
+
+		game.state:infiniteDungeonChallenge(zone, lev, data, data.generator.map.id_layout_name, vgrid.id_grids_name)
+	end,
 	post_process = function(level)
 		-- Provide some achievements
 		if level.level == 10 then world:gainAchievement("INFINITE_X10", game.player)
@@ -119,15 +248,17 @@ return {
 
 		-- Some lore
 		if level.level == 1 or level.level == 10 or level.level == 20 or level.level == 30 or level.level == 40 then
-			local l = game.zone:makeEntityByName(level, "terrain", "ID_HISTORY"..level.level)
+			local l = game.zone:makeEntityByName(level, "object", "ID_HISTORY"..level.level)
 			if not l then return end
 			for _, coord in pairs(util.adjacentCoords(level.default_up.x, level.default_up.y)) do
 				if game.level.map:isBound(coord[1], coord[2]) and (i ~= 0 or j ~= 0) and not game.level.map:checkEntity(coord[1], coord[2], engine.Map.TERRAIN, "block_move") then
-					game.zone:addEntity(level, l, "terrain", coord[1], coord[2])
+					game.zone:addEntity(level, l, "object", coord[1], coord[2])
 					return
 				end
 			end
 		end
+
+		game.state:infiniteDungeonChallengeFinish(game.zone, level)
 	end,
 }
 
diff --git a/game/modules/tome/data/zones/test/zone.lua b/game/modules/tome/data/zones/test/zone.lua
index 285fa1b5ddb950fbbcfe10abf255344d93ba9dab..85a813776e270f02c12d8c0ede597357dc733e0e 100644
--- a/game/modules/tome/data/zones/test/zone.lua
+++ b/game/modules/tome/data/zones/test/zone.lua
@@ -80,8 +80,9 @@ return {
 			nb_rooms = 10,
 			edge_entrances = {4,6},
 			rooms = {"forest_clearing"},
---			required_rooms = {"greater_vault"}, -- triggers level generation failure
-			greater_vaults_list = {"portal-vault"},
+			required_rooms = {"greater_vault", "greater_vault", "greater_vault"}, -- triggers level generation failure
+--			greater_vaults_list = {"portal-vault"},
+			greater_vaults_list = {"test2", "test"},
 			['.'] = {"GRASS"},
 			['#'] = "TREE",
 			up = "GRASS_UP4",
diff --git a/game/modules/tome/dialogs/Birther.lua b/game/modules/tome/dialogs/Birther.lua
index 1e72ff009d981ab978e4a5160aa75a37eaaffcaa..9aa40c94e5e50be2e77260c1cb95766551c1e250 100644
--- a/game/modules/tome/dialogs/Birther.lua
+++ b/game/modules/tome/dialogs/Birther.lua
@@ -330,7 +330,8 @@ end
 --- Make a default character when using cheat mode, for easier testing
 function _M:makeDefault()
 	self:setDescriptor("sex", "Female")
-	self:setDescriptor("world", "Maj'Eyal")
+	-- self:setDescriptor("world", "Maj'Eyal")
+	self:setDescriptor("world", "Infinite")
 	self:setDescriptor("difficulty", "Normal")
 	self:setDescriptor("permadeath", "Adventure")
 	self:setDescriptor("race", "Human")
diff --git a/game/modules/tome/dialogs/GameOptions.lua b/game/modules/tome/dialogs/GameOptions.lua
index 453bc9aeb32764fd612b0283adc0abd815dcc6cf..835fed26fb8d1c76e49e55502a8cdd9b99654204 100644
--- a/game/modules/tome/dialogs/GameOptions.lua
+++ b/game/modules/tome/dialogs/GameOptions.lua
@@ -538,7 +538,7 @@ function _M:generateListGameplay()
 		return tostring(config.settings.tome.tinker_auto_switch and "enabled" or "disabled")
 	end, fct=function(item)
 		config.settings.tome.tinker_auto_switch = not config.settings.tome.tinker_auto_switch
-		game:saveSettings("tome.tinker_auto_switch", ("tome.tinker_auto_switch = %s\n"):format(tostring(config.settings.tome.rest_before_explore)))
+		game:saveSettings("tome.tinker_auto_switch", ("tome.tinker_auto_switch = %s\n"):format(tostring(config.settings.tome.tinker_auto_switch)))
 		self.c_list:drawItem(item)
 	end,}
 
diff --git a/game/modules/tome/dialogs/QuestPopup.lua b/game/modules/tome/dialogs/QuestPopup.lua
index bb8b95916c6ac0cd4802a8427374322686d1f371..6a77b5c59a67c7d4f48389cc3aa81b0b7708f5e0 100644
--- a/game/modules/tome/dialogs/QuestPopup.lua
+++ b/game/modules/tome/dialogs/QuestPopup.lua
@@ -39,6 +39,9 @@ function _M:init(quest, status)
 	self.ui = "quest"
 	Dialog.init(self, "", 666, 150)
 
+	local add = ''
+	if quest.popup_text and quest.popup_text[status] then add = quest.popup_text[status].."\n" end
+
 	self.blight = self:getUITexture("ui/dialogframe_backglow.png")
 
 	local f, fs = FontPackage:getFont("bold")
@@ -46,7 +49,7 @@ function _M:init(quest, status)
 	quest:setTextShadow(3)
 	quest:setShadowShader(Shader.default.textoutline and Shader.default.textoutline.shad, 2)
 
-	local info = Textzone.new{auto_width=true, auto_height=true, text='#ANTIQUE_WHITE#(See your Journal for further details or click here)', font={f, math.ceil(fs)}}
+	local info = Textzone.new{auto_width=true, auto_height=true, text=add..'#ANTIQUE_WHITE#(See your Journal for further details or click here)', font={f, math.ceil(fs)}}
 	info:setTextShadow(3)
 	info:setShadowShader(Shader.default.textoutline and Shader.default.textoutline.shad, 2)