diff --git a/game/engines/default/engine/generator/actor/Random.lua b/game/engines/default/engine/generator/actor/Random.lua
index 248d941b628f9df0922f755b22f6b557eb227875..d76ac1a0c317b1513bf256591d15fbac1988846c 100644
--- a/game/engines/default/engine/generator/actor/Random.lua
+++ b/game/engines/default/engine/generator/actor/Random.lua
@@ -47,22 +47,26 @@ function _M:generate()
 	if self.guardian_level then glevel = self.guardian_level end
 
 	if self.guardian and self.level.level == glevel then
-		local m = self.zone:makeEntityByName(self.level, "actor", self.guardian)
-		if m then
-			local x, y = rng.range(self.area.x1, self.area.x2), rng.range(self.area.y1, self.area.y2)
-			local tries = 0
-			while (not m:canMove(x, y) or self.map.room_map[x][y].special) and tries < 100 do
-				x, y = rng.range(self.area.x1, self.area.x2), rng.range(self.area.y1, self.area.y2)
-				tries = tries + 1
-			end
-			if tries < 100 then
-				self.spots[#self.spots+1] = {x=x, y=y, guardian=true, check_connectivity=(not self.guardian_no_connectivity) and "entrance" or nil}
-				self.zone:addEntity(self.level, m, "actor", x, y)
-				print("Guardian allocated: ", self.guardian, m.uid, m.name)
-			end
-		else
-			print("WARNING: Guardian not found: ", self.guardian)
+		self:generateGuardian(self.guardian)
+	end
+end
+
+function _M:generateGuardian(guardian)
+	local m = self.zone:makeEntityByName(self.level, "actor", guardian)
+	if m then
+		local x, y = rng.range(self.area.x1, self.area.x2), rng.range(self.area.y1, self.area.y2)
+		local tries = 0
+		while (not m:canMove(x, y) or self.map.room_map[x][y].special) and tries < 100 do
+			x, y = rng.range(self.area.x1, self.area.x2), rng.range(self.area.y1, self.area.y2)
+			tries = tries + 1
+		end
+		if tries < 100 then
+			self.spots[#self.spots+1] = {x=x, y=y, guardian=true, check_connectivity=(not self.guardian_no_connectivity) and "entrance" or nil}
+			self.zone:addEntity(self.level, m, "actor", x, y)
+			print("Guardian allocated: ", self.guardian, m.uid, m.name)
 		end
+	else
+		print("WARNING: Guardian not found: ", self.guardian)
 	end
 end
 
diff --git a/game/modules/tome/data/chats/temporal-rift-end.lua b/game/modules/tome/data/chats/temporal-rift-end.lua
new file mode 100644
index 0000000000000000000000000000000000000000..7b08b4c3edffb868ea427be73ad1caecbc9812e8
--- /dev/null
+++ b/game/modules/tome/data/chats/temporal-rift-end.lua
@@ -0,0 +1,44 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+newChat{ id="welcome",
+	text = [[#LIGHT_GREEN#*A tall man, glowing like a star appears out of nowhere.*#WHITE#
+You destroyed *it* both? I am sorry for my harsh tone when we first met, but repairing time threads is stressful.
+I can not stay, I still have much to do, but take this it should help you.
+#LIGHT_GREEN#*He disappears again before you can even reply. A rift opens, to Maj'Eyal you hope.*#WHITE#]],
+	answers = {
+		{"Ok...", action = function(npc, player)
+			local o = game.zone:makeEntityByName(game.level, "object", "RUNE_RIFT")
+			o:identify(true)
+			game.zone:addEntity(game.level, o, "object")
+			player:addObject(player.INVEN_INVEN, o)
+			game.log("The temporal warden gives you: %s.", o:getName{do_color=true})
+
+			game:setAllowedBuild("chronomancer")
+			game:setAllowedBuild("chronomancer_temporal_warden", true)
+
+			local g = game.zone:makeEntityByName(game.level, "terrain", "RIFT")
+			g.change_level = 4
+			g.change_zone = "daikara"
+			game.zone:addEntity(game.level, g, "terrain", player.x, player.y)
+		end},
+	}
+}
+
+return "welcome"
diff --git a/game/modules/tome/data/chats/temporal-rift-start.lua b/game/modules/tome/data/chats/temporal-rift-start.lua
new file mode 100644
index 0000000000000000000000000000000000000000..0989f87c44c5cd493c6473bf1e1f10920ca61894
--- /dev/null
+++ b/game/modules/tome/data/chats/temporal-rift-start.lua
@@ -0,0 +1,31 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+newChat{ id="welcome",
+	text = [[#LIGHT_GREEN#*A tall man, glowing like a star appears out of nowhere.*#WHITE#
+Oh no, an other *adventurer*! You should not mess with things beyond your comprehension!
+Do not meddle in the affairs of Time, for it passes swiftly and easily disrupted!
+#LIGHT_GREEN#*He looks at you more closely.*#WHITE#
+You seem capable. Help me, fight the abominations while I repair the timeline, it is your only way out!]],
+	answers = {
+		{"But what is all th...", action = function(npc, player) game:changeLevel(2) game.player:grantQuest("temporal-rift") end},
+	}
+}
+
+return "welcome"
diff --git a/game/modules/tome/data/general/grids/void.lua b/game/modules/tome/data/general/grids/void.lua
index 314a6206fa2719cd379cccafdfa16033ce7f9a79..26346ba40537e14db843ab458561a857ad039c4e 100644
--- a/game/modules/tome/data/general/grids/void.lua
+++ b/game/modules/tome/data/general/grids/void.lua
@@ -29,9 +29,18 @@ newEntity{
 }
 
 newEntity{
-	define_as = "SPACE_TURBULENCE",
+	define_as = "SPACE_TURBULENCE1",
 	name = "space turbulence",
-	display = '#', color=colors.WHITE, image="terrain/temporal_instability_yellow.png",
+	display = '#', color=colors.YELLOW, image="terrain/temporal_instability_yellow.png",
+	always_remember = true,
+	does_block_move = true,
+	_noalpha = false,
+}
+
+newEntity{
+	define_as = "SPACE_TURBULENCE2",
+	name = "space turbulence",
+	display = '#', color=colors.BLUE, image="terrain/temporal_instability_blue.png",
 	always_remember = true,
 	does_block_move = true,
 	_noalpha = false,
diff --git a/game/modules/tome/data/general/npcs/horror.lua b/game/modules/tome/data/general/npcs/horror.lua
index bc55e07bea0858aee80319210bc7e429b824376c..9ceb338897897578a1c0e8a3477ba6c3b4bdd821 100644
--- a/game/modules/tome/data/general/npcs/horror.lua
+++ b/game/modules/tome/data/general/npcs/horror.lua
@@ -674,79 +674,3 @@ newEntity{ base="BASE_NPC_HORROR", define_as = "GRGGLCK_TENTACLE",
 		end
 	end,
 }
-
-newEntity{ base="BASE_NPC_HORROR",
-	name = "Chronolith Twin", color=colors.VIOLET, unique = true,
-	subtype = "temporal",
-	desc = [[A six armed creature with black insect-like eyes dressed in robes.]],
-	level_range = {20, nil}, exp_worth = 2,
-	max_life = 150, life_rating = 15, fixed_rating = true,
-	rank = 4,
-	size_category = 3,
-	stats = { str=10, dex=12, cun=14, mag=25, wil=25, con=16 },
-
-	instakill_immune = 1,
-	blind_immune = 0.5,
-	silence_immune = 0.5,
-
-	body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 },
-	equipment = resolvers.equip{
-		{type="weapon", subtype="staff", ego_chance=100, autoreq=true},
-		{type="armor", subtype="cloth", ego_chance=100, autoreq=true},
-	},
-	resolvers.drops{chance=100, nb=4, {ego_chance=100} },
-	resolvers.drops{chance=100, nb=1, {unique=true} },
-
-	resists = { [DamageType.TEMPORAL] = 50, },
-
-	resolvers.talents{
-		[Talents.T_RETHREAD]=3,
-		[Talents.T_ECHOES_FROM_THE_PAST]=3,
-		[Talents.T_TURN_BACK_THE_CLOCK]=3,
-		[Talents.T_HASTE]=3,
-		[Talents.T_STATIC_HISTORY]=5,
-		[Talents.T_DIMENSIONAL_STEP]=5,
-		[Talents.T_FORESIGHT]=5,
-	},
-
-	autolevel = "warriormage",
-	ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_astar" },
-}
-
-newEntity{ base="BASE_NPC_HORROR",
-	name = "Chronolith Clone", color=colors.VIOLET, unique = true,
-	subtype = "temporal",
-	desc = [[A six armed creature with black insect-like eyes dressed in robes.]],
-	level_range = {20, nil}, exp_worth = 2,
-	max_life = 150, life_rating = 15, fixed_rating = true,
-	rank = 4,
-	size_category = 3,
-	stats = { str=10, dex=12, cun=14, mag=25, wil=25, con=16 },
-
-	instakill_immune = 1,
-	blind_immune = 0.5,
-	silence_immune = 0.5,
-
-	body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 },
-	equipment = resolvers.equip{
-		{type="weapon", subtype="staff", ego_chance=100, autoreq=true},
-		{type="armor", subtype="cloth", ego_chance=100, autoreq=true},
-	},
-	resolvers.drops{chance=100, nb=4, {ego_chance=100} },
-	resolvers.drops{chance=100, nb=1, {unique=true} },
-
-	resists = { [DamageType.TEMPORAL] = 50, },
-
-	resolvers.talents{
-		[Talents.T_TEMPORAL_WAKE]=3,
-		[Talents.T_TIME_SKIP]=3,
-		[Talents.T_TEMPORAL_FUGUE]=3,
-		[Talents.T_BORROWED_TIME]=3,
-		[Talents.T_DIMENSIONAL_STEP]=5,
-		[Talents.T_STATIC_HISTORY]=5,
-		[Talents.T_FORESIGHT]=5,
-	},
-
-	autolevel = "warriormage",
-	ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_astar" },
-}
\ No newline at end of file
diff --git a/game/modules/tome/data/general/npcs/telugoroth.lua b/game/modules/tome/data/general/npcs/telugoroth.lua
index cac43e1a691995bc991df254c22ceccbd858698e..fa97f6434e2de89444fd268f3aa9c5cdbb0dbed1 100644
--- a/game/modules/tome/data/general/npcs/telugoroth.lua
+++ b/game/modules/tome/data/general/npcs/telugoroth.lua
@@ -160,7 +160,7 @@ newEntity{ base = "BASE_NPC_TELUGOROTH",
 
 	resolvers.talents{
 		[Talents.T_ANOMALY_TEMPORAL_STORM]=1,
-		[Talents.T_TIME_SKIP]=5,
+		[Talents.T_QUANTUM_SPIKE]=5,
 		[Talents.T_DIMENSIONAL_STEP]=5,
 		[Talents.T_ANOMALY_REARRANGE]=1,
 		[Talents.T_TEMPORAL_WAKE]=4,
diff --git a/game/modules/tome/data/gfx/particles/weather_storm.lua b/game/modules/tome/data/gfx/particles/weather_storm.lua
index 366fbd5260b534adb6a1a2c17656972033e60864..7db48595b21e4b92f456a08d45d0b1d2c6f084b7 100644
--- a/game/modules/tome/data/gfx/particles/weather_storm.lua
+++ b/game/modules/tome/data/gfx/particles/weather_storm.lua
@@ -19,6 +19,9 @@
 
 rdir = math.rad(dir or 110)
 h_per_tick = math.sin(rdir)
+r = r or 1
+g = g or 1
+b = b or 1
 
 local first = true
 
@@ -38,9 +41,9 @@ return { generator = function()
 		dir = rdir, dirv = blur, dira = 0,
 		vel = vel, velv = 0, vela = 0,
 
-		r = 1, rv = 0, ra = 0,
-		g = 1, gv = 0, ga = 0,
-		b = 1, bv = 0, ba = 0,
+		r = r, rv = 0, ra = 0,
+		g = g, gv = 0, ga = 0,
+		b = b, bv = 0, ba = 0,
 		a = rng.float(alpha[1], alpha[2]), av = 0, aa = 0,
 	}
 end, },
diff --git a/game/modules/tome/data/gfx/shockbolt/terrain/demon_portal2.png b/game/modules/tome/data/gfx/shockbolt/terrain/demon_portal2.png
index 1edb0e1fe26aaeef2d92d16f9bff5918ed54c885..37e57bde9844f4ad0b0e16298776a94f64efcee3 100644
Binary files a/game/modules/tome/data/gfx/shockbolt/terrain/demon_portal2.png and b/game/modules/tome/data/gfx/shockbolt/terrain/demon_portal2.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/terrain/temporal_instability_blue.png b/game/modules/tome/data/gfx/shockbolt/terrain/temporal_instability_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..7c0d48d554931f105d9aa93fed62654fcc0c8554
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/terrain/temporal_instability_blue.png differ
diff --git a/game/modules/tome/data/gfx/terrain/demon_portal2.png b/game/modules/tome/data/gfx/terrain/demon_portal2.png
new file mode 100644
index 0000000000000000000000000000000000000000..1e3dd2d690bdd01afa7cdb015aca8d6761ff8f4c
Binary files /dev/null and b/game/modules/tome/data/gfx/terrain/demon_portal2.png differ
diff --git a/game/modules/tome/data/gfx/terrain/temporal_instability_blue.png b/game/modules/tome/data/gfx/terrain/temporal_instability_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..47fd2c3ec74a1a89d699c5e06bb696ef7b105b0c
Binary files /dev/null and b/game/modules/tome/data/gfx/terrain/temporal_instability_blue.png differ
diff --git a/game/modules/tome/data/gfx/terrain/temporal_instability_yellow.png b/game/modules/tome/data/gfx/terrain/temporal_instability_yellow.png
new file mode 100644
index 0000000000000000000000000000000000000000..fa030de630991a29255fb7c0bfd498413d896641
Binary files /dev/null and b/game/modules/tome/data/gfx/terrain/temporal_instability_yellow.png differ
diff --git a/game/modules/tome/data/quests/temporal-rift.lua b/game/modules/tome/data/quests/temporal-rift.lua
new file mode 100644
index 0000000000000000000000000000000000000000..84c436f04805014f6f28740af9c15fc579adccf8
--- /dev/null
+++ b/game/modules/tome/data/quests/temporal-rift.lua
@@ -0,0 +1,36 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+name = "Back and Back and Back to the Future"
+desc = function(self, who)
+	local desc = {}
+	desc[#desc+1] = "After passing through some kind of time anomaly you met a temporal warden who told you to destroy to abominations of this alternate timeline.\n"
+	return table.concat(desc, "\n")
+end
+
+on_status_change = function(self, who, status, sub)
+	if sub then
+		if self:isCompleted("twin") and self:isCompleted("clone") then
+			who:setQuestStatus(self.id, engine.Quest.DONE)
+			local Chat = require "engine.Chat"
+			local chat = Chat.new("temporal-rift-end", {name="Temporal Warden"}, who)
+			chat:invoke()
+		end
+	end
+end
diff --git a/game/modules/tome/data/talents/chronomancy/timetravel.lua b/game/modules/tome/data/talents/chronomancy/timetravel.lua
index f2d999fd3dabf3d50205fa34759e5a16011d0586..65e221d944e1ef084a6c2fbe977633d391875001 100644
--- a/game/modules/tome/data/talents/chronomancy/timetravel.lua
+++ b/game/modules/tome/data/talents/chronomancy/timetravel.lua
@@ -96,7 +96,7 @@ newTalent{
 		if tx then
 			target = game.level.map(tx, ty, engine.Map.ACTOR)
 		end
-		if target then
+		if target and not target.player then
 			local hit = self:checkHit(self:combatSpellpower(), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0))
 			if not hit then
 				game.logSeen(target, "The spell fizzles!")
diff --git a/game/modules/tome/data/talents/misc/inscriptions.lua b/game/modules/tome/data/talents/misc/inscriptions.lua
index 9487dfbc132d1d9a62c5518670c7705180c89472..f2353377760f1fe7566070d56feac6d039819589 100644
--- a/game/modules/tome/data/talents/misc/inscriptions.lua
+++ b/game/modules/tome/data/talents/misc/inscriptions.lua
@@ -573,3 +573,107 @@ newInscription{
 		return ([[%d%% regen over %d turns; %d instant mana]]):format(data.mana + data.inc_stat, data.dur, (data.mana + data.inc_stat) / 20)
 	end,
 }
+
+-- This is mostly a copy of Time Skip .. uuuglly
+newInscription{
+	name = "Rune of the Rift",
+	type = {"inscriptions/runes", 1},
+	points = 1,
+	is_spell = true,
+	tactical = { DISABLE = 2, ATTACK = 1 },
+	direct_hit = true,
+	reflectable = true,
+	requires_target = true,
+	getDamage = function(self, t) return 150 + self:getWil() * 4 end,
+	getDuration = function(self, t) return 4 end,
+	action = function(self, t)
+		-- Find the target and check hit
+		local tg = {type="hit", self:getTalentRange(t), talent=t}
+		local tx, ty, target = self:getTarget(tg)
+		if not tx or not ty then return nil end
+		local _ _, tx, ty = self:canProject(tg, tx, ty)
+		tx, ty = checkBackfire(self, tx, ty)
+		if tx then
+			target = game.level.map(tx, ty, engine.Map.ACTOR)
+		end
+		if target and not target.player then
+			local hit = self:checkHit(self:combatSpellpower(), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0))
+			if not hit then
+				game.logSeen(target, "The spell fizzles!")
+				return true
+			end
+		else
+			return
+		end
+
+		-- Keep the Actor from leveling on return
+		target.forceLevelup = false
+		-- Create an object to time the effect and store the creature
+		-- First, clone the terrain that we are replacing
+		local terrain = game.level.map(game.player.x, game.player.y, engine.Map.TERRAIN)
+		local temporal_instability = mod.class.Object.new{
+			old_feat = game.level.map(target.x, target.y, engine.Map.TERRAIN),
+			name = "temporal instability", type="temporal", subtype="anomaly",
+			display = '&', color=colors.LIGHT_BLUE,
+			temporary = t.getDuration(self, t),
+			canAct = false,
+			target = target,
+			act = function(self)
+				self:useEnergy()
+				self.temporary = self.temporary - 1
+				if self.temporary <= 0 then
+					game.level.map(self.target.x, self.target.y, engine.Map.TERRAIN, self.old_feat)
+					game.level:removeEntity(self)
+					local mx, my = util.findFreeGrid(self.target.x, self.target.y, 20, true, {[engine.Map.ACTOR]=true})
+					game.zone:addEntity(game.level, self.target, "actor", mx, my)
+					game.level.map:redisplay()
+				end
+			end,
+			summoner_gain_exp = true,
+			summoner = self,
+		}
+		-- Mixin the old terrain
+		table.update(temporal_instability, terrain)
+		-- Now update the display overlay
+		local overlay = engine.Entity.new{
+		--	image = "terrain/wormhole.png",
+			display = '&', color=colors.LIGHT_BLUE, image="object/temporal_instability.png",
+			display_on_seen = true,
+			display_on_remember = true,
+		}
+		if not temporal_instability.add_displays then
+			temporal_instability.add_displays = {overlay}
+		else
+			table.append(temporal_instability.add_displays, overlay)
+		end
+
+		self:project(tg, tx, ty, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t)))
+		game.level.map:particleEmitter(tx, ty, 1, "temporal_thrust")
+		game:playSoundNear(self, "talents/arcane")
+		-- Remove the target and place the temporal placeholder
+		if not target.dead then
+			if target ~= self then
+				target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self:combatSpellpower(0.3)})
+			end
+			game.logSeen(target, "%s has moved forward in time!", target.name:capitalize())
+			game.level:removeEntity(target)
+			game.level:addEntity(temporal_instability)
+			game.level.map(target.x, target.y, engine.Map.TERRAIN, temporal_instability)
+		else
+			game.logSeen(target, "%s has been killed by the temporal energy!", target.name:capitalize())
+		end
+
+		self:incParadox(-120)
+
+		return true
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[Inflicts %0.2f temporal damage.  If your target survives it will be sent %d turns into the future.
+		It will also lower your paradox by 120 (if you have any).]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration)
+	end,
+	short_info = function(self, t)
+		return ("%0.2f temporal damage, removed from time %d turns"):format(t.getDamage(self, t), t.getDuration(self, t))
+	end,
+}
diff --git a/game/modules/tome/data/texts/unlock-chronomancer_temporal_warden.lua b/game/modules/tome/data/texts/unlock-chronomancer_temporal_warden.lua
new file mode 100644
index 0000000000000000000000000000000000000000..eab621e146f1b6797d16ae630591217aea774131
--- /dev/null
+++ b/game/modules/tome/data/texts/unlock-chronomancer_temporal_warden.lua
@@ -0,0 +1,35 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+return "New Class: #LIGHT_GREEN#Temporal Warden (Chronomancer)",
+[[Time isn't a linear thing, it only appears to be.  In truth it's a fabric, the very fabric that holds the universe together.
+Like all great powers though there are those who seek to manipulate it and pervert it, but there are also those who seek to uphold rather then destroy.
+Those who have learned to bend rather then break the fabric so they can keep the balance against dark or power hungry forces.
+
+You have been taught the ways of chronomancy by a Temporal Warden and can now create new characters with the #LIGHT_GREEN#Temporal Warden class#WHITE#.
+
+Temporal Wardens are warriors proficient in both ranged and hand-to-hand combat.
+Class features:#YELLOW#
+- Duel-wield a medium sized and a small weapon or attack from afar with your ranged weapon skills
+- Manipulate space and time to speed yourself up, create wormholes, or even throw an opponent into the future
+- See into the future or even travel into the past#WHITE#
+
+Temporal Warden's use Paradox for their powers.  It represents how much damage they've done to the spacetime continuum.
+The higher their paradox the greater the effect of their powers but the harder they'll be to control.
+]]
\ No newline at end of file
diff --git a/game/modules/tome/data/zones/daikara/grids.lua b/game/modules/tome/data/zones/daikara/grids.lua
index 99b1e654130bea91a26a49d8544b090749e28304..76d090673d9fe7fe332cda3e6087a86446012434 100644
--- a/game/modules/tome/data/zones/daikara/grids.lua
+++ b/game/modules/tome/data/zones/daikara/grids.lua
@@ -19,3 +19,14 @@
 
 load("/data/general/grids/basic.lua")
 load("/data/general/grids/mountain.lua")
+
+newEntity{
+	define_as = "RIFT",
+	name = "Temporal Rift", image = "terrain/rocky_ground.png", add_displays = {class.new{image="terrain/temporal_instability_yellow.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 somewhere ..]],
+	change_level = 1, change_zone = "temporal-rift",
+}
diff --git a/game/modules/tome/data/zones/daikara/zone.lua b/game/modules/tome/data/zones/daikara/zone.lua
index 9166613ff294f6c2e5510fc5366a320785e4ba5d..e30affffa5b8d40a524e94ac417b9748b90784a0 100644
--- a/game/modules/tome/data/zones/daikara/zone.lua
+++ b/game/modules/tome/data/zones/daikara/zone.lua
@@ -71,6 +71,20 @@ return {
 		-- Place a lore note on each level
 		game:placeRandomLoreObject("NOTE"..level.level)
 
+		-- Temporal rift on level 4
+		if level.level == 4 then
+			local g = game.zone:makeEntityByName(game.level, "terrain", "RIFT")
+			local x, y = rng.range(0, game.level.map.w-1), rng.range(0, game.level.map.h-1)
+			local tries = 0
+				while game.level.map:checkEntity(x, y, engine.Map.TERRAIN, "block_move") and tries < 100 do
+				x, y = rng.range(0, game.level.map.w-1), rng.range(0, game.level.map.h-1)
+				tries = tries + 1
+			end
+			if tries < 100 then
+				game.zone:addEntity(game.level, g, "terrain", x, y)
+			end
+		end
+
 		game.state:makeWeather(level, 6, {max_nb=7, chance=1, dir=120, speed={0.1, 0.9}, alpha={0.2, 0.4}, particle_name="weather/grey_cloud_%02d"})
 	end,
 }
diff --git a/game/modules/tome/data/zones/temporal-rift/grids.lua b/game/modules/tome/data/zones/temporal-rift/grids.lua
index 31bdb805e7ff8074c1f904b4be52d993c1a3174a..186df6ee5574afad20029a00e1c5c992e5ebba86 100644
--- a/game/modules/tome/data/zones/temporal-rift/grids.lua
+++ b/game/modules/tome/data/zones/temporal-rift/grids.lua
@@ -18,4 +18,32 @@
 -- darkgod@te4.org
 
 load("/data/general/grids/basic.lua")
+load("/data/general/grids/forest.lua")
+load("/data/general/grids/water.lua")
+load("/data/general/grids/mountain.lua")
+load("/data/general/grids/sand.lua")
 load("/data/general/grids/void.lua")
+
+-- Override exits
+newEntity { base = "GRASS", define_as = "GRASS_UP_WILDERNESS" }
+newEntity { base = "DEEP_OCEAN_WATER", define_as = "WATER_DOWN" }
+newEntity { base = "GRASS", define_as = "OLD_FOREST" }
+
+newEntity{
+	define_as = "RIFT",
+	name = "Temporal Rift", 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 somewhere ..]],
+	change_level = 1,
+	change_level_check = function()
+		if game.level.level > 1 then return end
+		local p = game.party:findMember{main=true}
+		local Chat = require "engine.Chat"
+		local chat = Chat.new("temporal-rift-start", {name="Temporal Warden"}, p)
+		chat:invoke()
+		return true
+	end,
+}
diff --git a/game/modules/tome/data/zones/temporal-rift/npcs.lua b/game/modules/tome/data/zones/temporal-rift/npcs.lua
index f11c4d416a5211f715d8f634821970347da13cac..ae6ab647d8242e3a14e11674791a7ba68bd5e821 100644
--- a/game/modules/tome/data/zones/temporal-rift/npcs.lua
+++ b/game/modules/tome/data/zones/temporal-rift/npcs.lua
@@ -18,3 +18,189 @@
 -- darkgod@te4.org
 
 load("/data/general/npcs/telugoroth.lua", rarity(0))
+load("/data/general/npcs/horror.lua", function(e) if e.rarity then e.horror_rarity, e.rarity = e.rarity, nil end end)
+
+local Talents = require("engine.interface.ActorTalents")
+
+newEntity{ define_as = "BEN_CRUTHDAR",
+	type = "humanoid", subtype = "temporal", unique = true,
+	name = "Ben Cruthdar, the Abomination",
+	display = "p", color=colors.VIOLET,
+	desc = [[This crazed madman seems twisted and corrupted by temporal energy, his body shifting and phasing in and out of reality.]],
+	level_range = {15, nil}, exp_worth = 2,
+	max_life = 270, life_rating = 17, fixed_rating = true,
+	max_stamina = 85,
+	stats = { str=20, dex=15, wil=18, con=20 },
+	rank = 3.5,
+	size_category = 2,
+	infravision = 20,
+	instakill_immune = 1,
+	move_others=true,
+
+	resists = { [DamageType.COLD] = 25 },
+
+	body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 },
+	resolvers.equip{ {type="weapon", subtype="battleaxe", ego_chance=100, autoreq=true}, },
+	resolvers.drops{chance=100, nb=1, {ego_chance=100} },
+
+	resolvers.talents{
+		[Talents.T_GLOOM]=3,
+		[Talents.T_WEAKNESS]=3,
+		[Talents.T_TORMENT]=3,
+		[Talents.T_UNNATURAL_BODY]=4,
+		[Talents.T_DOMINATE]=1,
+		[Talents.T_BLINDSIDE]=3,
+		[Talents.T_SLASH]=3,
+		[Talents.T_RECKLESS_CHARGE]=1,
+
+		[Talents.T_DAMAGE_SMEARING]=5,
+		[Talents.T_HASTE]=3,
+		[Talents.T_BORROWED_TIME]=3,
+	},
+	resolvers.sustains_at_birth(),
+
+	autolevel = "warriorwill",
+	ai = "tactical", ai_state = { talent_in=1, ai_move="move_astar", },
+	ai_tactic = resolvers.tactic"melee",
+	resolvers.inscriptions(1, "healing infusion"),
+
+	-- On die needs to make stairs back to the Rift
+	on_die = function(self, who)
+		game.level.data.portal_next(self)
+	end,
+}
+
+newEntity{ define_as = "ABOMINATION_RANTHA",
+	type = "dragon", subtype = "temporal", unique = true,
+	name = "Rantha the Abomination",
+	display = "D", color=colors.VIOLET,
+	desc = [[Claws and teeth. Ice and death. Dragons are not all extinct it seems...  and this one seems to have been corrupted by the time rift.]],
+	level_range = {15, nil}, exp_worth = 2,
+	max_life = 220, life_rating = 15, fixed_rating = true,
+	max_stamina = 85,
+	max_mana = 200,
+	stats = { str=25, dex=10, cun=8, mag=20, wil=20, con=20 },
+	rank = 3.5,
+	size_category = 5,
+	combat_armor = 17, combat_def = 14,
+	infravision = 20,
+	instakill_immune = 1,
+	move_others=true,
+
+	resists = { [DamageType.FIRE] = -20, [DamageType.COLD] = 100,  [DamageType.TEMPORAL] = 25, },
+
+	body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 },
+
+	-- Frost Treads drop should be changed.
+	resolvers.drops{chance=100, nb=5, {type="gem"} },
+	resolvers.drops{chance=100, nb=10, {type="money"} },
+
+	resolvers.talents{
+		[Talents.T_KNOCKBACK]=3,
+
+		[Talents.T_ICE_STORM]=2,
+		[Talents.T_FREEZE]=3,
+
+		[Talents.T_ICE_CLAW]=4,
+		[Talents.T_ICY_SKIN]=3,
+		[Talents.T_ICE_BREATH]=4,
+
+		[Talents.T_BODY_REVERSION]=4,
+		[Talents.T_ECHOES_FROM_THE_PAST]=4,
+		[Talents.T_QUANTUM_SPIKE]=4,
+	},
+	resolvers.sustains_at_birth(),
+
+	autolevel = "warriormage",
+	ai = "tactical", ai_state = { talent_in=1, ai_move="move_astar", },
+	resolvers.inscriptions(1, "infusion"),
+
+	-- On die needs to make stairs back to the Rift
+	on_die = function(self, who)
+		game.level.data.portal_next(self)
+	end,
+}
+
+newEntity{ base="BASE_NPC_HORROR", define_as = "CHRONOLITH_TWIN",
+	name = "Chronolith Twin", color=colors.VIOLET, unique = true,
+	subtype = "temporal",
+	desc = [[A six armed creature with black insect-like eyes dressed in robes.]],
+	level_range = {20, nil}, exp_worth = 2,
+	max_life = 150, life_rating = 15, fixed_rating = true,
+	rank = 4,
+	size_category = 3,
+	stats = { str=10, dex=12, cun=14, mag=25, wil=25, con=16 },
+
+	instakill_immune = 1,
+	blind_immune = 0.5,
+	silence_immune = 0.5,
+
+	body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 },
+	equipment = resolvers.equip{
+		{type="weapon", subtype="staff", ego_chance=100, autoreq=true},
+		{type="armor", subtype="cloth", ego_chance=100, autoreq=true},
+	},
+	resolvers.drops{chance=100, nb=4, {ego_chance=100} },
+	resolvers.drops{chance=100, nb=1, {unique=true} },
+
+	resists = { [DamageType.TEMPORAL] = 50, },
+
+	resolvers.talents{
+		[Talents.T_RETHREAD]=3,
+		[Talents.T_ECHOES_FROM_THE_PAST]=3,
+		[Talents.T_TURN_BACK_THE_CLOCK]=3,
+		[Talents.T_HASTE]=3,
+		[Talents.T_STATIC_HISTORY]=5,
+		[Talents.T_DIMENSIONAL_STEP]=5,
+		[Talents.T_FORESIGHT]=5,
+	},
+
+	autolevel = "warriormage",
+	ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_astar" },
+
+	on_die = function(self, who)
+		game.player:resolveSource():setQuestStatus("temporal-rift", engine.Quest.COMPLETED, "twin")
+	end,
+}
+
+newEntity{ base="BASE_NPC_HORROR", define_as = "CHRONOLITH_CLONE",
+	name = "Chronolith Clone", color=colors.VIOLET, unique = true,
+	subtype = "temporal",
+	desc = [[A six armed creature with black insect-like eyes dressed in robes.]],
+	level_range = {20, nil}, exp_worth = 2,
+	max_life = 150, life_rating = 15, fixed_rating = true,
+	rank = 4,
+	size_category = 3,
+	stats = { str=10, dex=12, cun=14, mag=25, wil=25, con=16 },
+
+	instakill_immune = 1,
+	blind_immune = 0.5,
+	silence_immune = 0.5,
+
+	body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 },
+	equipment = resolvers.equip{
+		{type="weapon", subtype="staff", ego_chance=100, autoreq=true},
+		{type="armor", subtype="cloth", ego_chance=100, autoreq=true},
+	},
+	resolvers.drops{chance=100, nb=4, {ego_chance=100} },
+	resolvers.drops{chance=100, nb=1, {unique=true} },
+
+	resists = { [DamageType.TEMPORAL] = 50, },
+
+	resolvers.talents{
+		[Talents.T_TEMPORAL_WAKE]=3,
+		[Talents.T_QUANTUM_SPIKE]=3,
+		[Talents.T_TEMPORAL_FUGUE]=3,
+		[Talents.T_BORROWED_TIME]=3,
+		[Talents.T_DIMENSIONAL_STEP]=5,
+		[Talents.T_STATIC_HISTORY]=5,
+		[Talents.T_FORESIGHT]=5,
+	},
+
+	autolevel = "warriormage",
+	ai = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_astar" },
+
+	on_die = function(self, who)
+		game.player:resolveSource():setQuestStatus("temporal-rift", engine.Quest.COMPLETED, "clone")
+	end,
+}
diff --git a/game/modules/tome/data/zones/temporal-rift/objects.lua b/game/modules/tome/data/zones/temporal-rift/objects.lua
index 4e7b8c6c6405bb629806f2279b08ee9e45700cf7..ed7b218554658f16d1aaff3a47094b7bb8680414 100644
--- a/game/modules/tome/data/zones/temporal-rift/objects.lua
+++ b/game/modules/tome/data/zones/temporal-rift/objects.lua
@@ -18,3 +18,14 @@
 -- darkgod@te4.org
 
 load("/data/general/objects/objects.lua")
+
+newEntity{ base = "BASE_RUNE", define_as = "RUNE_RIFT",
+	name = "Rune of the Rift", unique = true, identified = true,
+	cost = 100,
+	material_level = 3,
+
+	inscription_data = {
+		cooldown = 14,
+	},
+	inscription_talent = "RUNE_OF_THE_RIFT",
+}
diff --git a/game/modules/tome/data/zones/temporal-rift/zone.lua b/game/modules/tome/data/zones/temporal-rift/zone.lua
index d61c4d0f5883715e36c9e3ec21fff3eef524401f..01959b45e3b50c4275e0383a7ace214099012a86 100644
--- a/game/modules/tome/data/zones/temporal-rift/zone.lua
+++ b/game/modules/tome/data/zones/temporal-rift/zone.lua
@@ -19,44 +19,131 @@
 
 return {
 	name = "Temporal Rift",
+	display_name = function(x, y)
+		if game.level.level == 2 then return "Temporal Rift: Lumberjack village"
+		elseif game.level.level == 3 then return "Temporal Rift: Daikara"
+		elseif game.level.level == 2 then return "Temporal Rift: Lake of Nur"
+		end
+		return "Temporal Rift"
+	end,
+	variable_zone_name = true,
 	level_range = {16, 30},
 	level_scheme = "player",
 	max_level = 4,
 	decay = {300, 800},
 	actor_adjust_level = function(zone, level, e) return zone.base_level + e:getRankLevelAdjust() + level.level-1 + rng.range(-1,2) end,
-	width = 90, height = 90,
-	all_remembered = true,
+	width = 60, height = 25,
+--	all_remembered = true,
 	all_lited = true,
 	persistent = "zone",
 	generator =  {
-		map = {
-			class = "engine.generator.map.Forest",
-			floor = "VOID",
-			wall = "SPACE_TURBULENCE",
-			up = "VOID",
-			down = "VOID",
-		},
-		actor = {
-			class = "engine.generator.actor.Random",
-			nb_npc = {10, 20},
-		},
---[[
-		object = {
-			class = "engine.generator.object.Random",
-			nb_object = {12, 16},
-		},
-		trap = {
-			class = "engine.generator.trap.Random",
-			nb_trap = {20, 30},
-		},
-]]
 	},
+	levels =
+	{
+		[1] = { generator = {
+			map = {
+				class = "engine.generator.map.Forest",
+				floor = "VOID",
+				wall = {"SPACE_TURBULENCE1","SPACE_TURBULENCE2"},
+				up = "VOID",
+				down = "RIFT",
+				edge_entrances = {4,6},
+			},
+			actor = {
+				class = "engine.generator.actor.Random",
+				nb_npc = {15, 25},
+			},
+		} },
+		[2] = {
+			width = 25, height = 25,
+			generator = {
+			map = {
+				class = "engine.generator.map.Static",
+				map = "towns/lumberjack-village",
+			},
+			actor = {
+				class = "engine.generator.actor.Random",
+				nb_npc = {3, 3},
+			},
+		} },
+		[3] = {
+			width = 50, height = 50,
+			generator = {
+			map = {
+				class = "engine.generator.map.Roomer",
+				nb_rooms = 10,
+				edge_entrances = {2,8},
+				rooms = {"forest_clearing","rocky_snowy_trees"},
+				['.'] = "ROCKY_GROUND",
+				['T'] = "ROCKY_SNOWY_TREE",
+				['#'] = "MOUNTAIN_WALL",
+				up = "ROCKY_GROUND",
+				down = "ROCKY_GROUND",
+				door = "ROCKY_GROUND",
+			},
+			actor = {
+				class = "engine.generator.actor.Random",
+				nb_npc = {15, 25},
+			},
+		} },
+		[4] = {
+			width = 50, height = 50,
+			generator = {
+			map = {
+				class = "engine.generator.map.Static",
+				map = "zones/lake-nur",
+			},
+			actor = {
+				class = "engine.generator.actor.Random",
+				nb_npc = {0, 0},
+			},
+		} },
+	},
+
 	post_process = function(level)
-		local Map = require "engine.Map"
-		level.background_particle = require("engine.Particles").new("starfield", 1, {width=Map.viewport.width, height=Map.viewport.height})
+		if level.level == 1 then
+			local Map = require "engine.Map"
+			level.background_particle = require("engine.Particles").new("starfield", 1, {width=Map.viewport.width, height=Map.viewport.height})
+		end
+
+		if level.level <= 2 then
+			game.state:makeWeather(level, 6, {max_nb=7, chance=1, dir=120, speed={0.1, 0.9}, r=0.2, g=0.4, b=1, alpha={0.2, 0.4}, particle_name="weather/grey_cloud_%02d"})
+		else
+			game.state:makeWeather(level, 6, {max_nb=12, chance=1, dir=120, speed={0.1, 0.9}, r=0.2, g=0.4, b=1, alpha={0.2, 0.4}, particle_name="weather/grey_cloud_%02d"})
+		end
+	end,
+
+	on_enter = function(lev, old_lev, newzone)
+		local Dialog = require("engine.ui.Dialog")
+		if lev == 1 and not game.level.shown_warning then
+			Dialog:simplePopup("Temporal Rift", "Space and time distort and lose meaning as you pass through the rift. This place is alien.")
+			game.level.shown_warning = true
+		elseif lev == 2 and not game.level.shown_warning then
+			Dialog:simplePopup("Temporal Rift", "This looks like Maj'Eyal's forest but it looks strangely distorbed, beware...")
+			game.level.shown_warning = true
+		elseif lev == 3 and not game.level.shown_warning then
+			Dialog:simplePopup("Temporal Rift", "As you pass the rift you see what seems to be the Daikara mountains, yet they are not.")
+			game.level.shown_warning = true
+			require("engine.generator.actor.Random").new(game.zone, game.level.map, game.level, {}):generateGuardian("ABOMINATION_RANTHA")
+		elseif lev == 4 and not game.level.shown_warning then
+			Dialog:simplePopup("Temporal Rift", "The peace of this place has been disturbed.")
+			game.level.shown_warning = true
+
+			local m = game.zone:makeEntityByName(game.level, "actor", "CHRONOLITH_TWIN")
+			game.zone:addEntity(game.level, m, "actor", 26, 8)
+			local m = game.zone:makeEntityByName(game.level, "actor", "CHRONOLITH_CLONE")
+			game.zone:addEntity(game.level, m, "actor", 29, 8)
+		end
+	end,
+
+	portal_next = function(npc)
+		local g = game.zone:makeEntityByName(game.level, "terrain", "RIFT")
+		game.zone:addEntity(game.level, g, "terrain", npc.x, npc.y)
 	end,
 
 	background = function(level, x, y, nb_keyframes)
+		if level.level ~= 1 then return end
+
 		local Map = require "engine.Map"
 
 		for i = 1, nb_keyframes do