diff --git a/game/engines/default/engine/Projectile.lua b/game/engines/default/engine/Projectile.lua
index 10d8d32b194b52ff1f48198e7a6dae1843e04e43..bb4acc084d6c3ed9de1f6f256f613d281dbd5d7f 100644
--- a/game/engines/default/engine/Projectile.lua
+++ b/game/engines/default/engine/Projectile.lua
@@ -250,8 +250,15 @@ function _M:on_move(x, y, target)
 	self.src.__project_source = self -- intermediate projector source
 	if self.project and self.project.def.typ.line then self.src:projectDoAct(self.project.def.typ, self.project.def.tg, self.project.def.damtype, self.project.def.dam, self.project.def.particles, self.x, self.y, self.tmp_proj) end
 	if self.project and self.project.def.typ.stop_block then
-
 		self.src:projectDoStop(self.project.def.typ, self.project.def.tg, self.project.def.damtype, self.project.def.dam, self.project.def.particles, self.x, self.y, self.tmp_proj, self.x, self.y, self)
+	elseif self.homing then
+		if (self.x == self.homing.target.x and self.y == self.homing.target.y) then
+			game.level:removeEntity(self, true)
+			self.dead = true
+			self.homing.on_hit(self, self.src, self.homing.target)
+		else
+			self.homing.on_move(self, self.src)
+		end
 	end
 	self.src.__project_source = nil
 end
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 40f03a3ce4dbd4a1083e1f20ae52732c6bcbdf4e..f3f8a812a11c1838ed60107e6ef1deadd4dbd80c 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -1388,10 +1388,6 @@ function _M:move(x, y, force)
 			local eff = self:hasEffect(self.EFF_CURSE_OF_SHROUDS)
 			if eff then eff.moved = true end
 
-			if self:knowTalent(self.T_CELERITY) then
-				self:callTalent(self.T_CELERITY, "doCelerity")
-			end
-
 			if self:attr("move_stamina_instead_of_energy") and self:getStamina() > self:attr("move_stamina_instead_of_energy") then
 				self:incStamina(-self:attr("move_stamina_instead_of_energy"))
 			else
@@ -1453,11 +1449,15 @@ function _M:move(x, y, force)
 		self:forceUseTalent(self.T_BODY_OF_STONE, {ignore_energy=true})
 	end
 
-	-- Chronomancy auras
-
+	-- Celerity
+	if moved and ox and oy and (ox ~= self.x or oy ~= self.y) and self:knowTalent(self.T_CELERITY) then
+		self:callTalent(self.T_CELERITY, "doCelerity")
+	end
 
+	-- Break channels
 	if moved then
 		self:breakPsionicChannel()
+		self:breakSpacetimeTuning()
 	end
 
 	if not force and moved and ox and oy and (ox ~= self.x or oy ~= self.y) and self:knowTalent(self.T_LIGHT_OF_FOOT) then
@@ -1468,6 +1468,7 @@ function _M:move(x, y, force)
 		local blur = 0
 		if game.level.data.zero_gravity then blur = 2 end
 		if self:attr("lightning_speed") or self:attr("step_up") or self:attr("wild_speed") then blur = 3 end
+		if self:hasEffect(self.EFF_CELERITY) then local eff = self:hasEffect(self.EFF_CELERITY) blur = eff.charges end
 		self:setMoveAnim(ox, oy, config.settings.tome.smooth_move, blur, 8, config.settings.tome.twitch_move and 0.15 or 0)
 	end
 
@@ -1496,6 +1497,16 @@ function _M:waitTurn()
 			self:reloadQS()
 		end
 	end
+	
+	-- Tune paradox up or down
+	if not self:hasEffect(self.EFF_SPACETIME_TUNING) and self.preferred_paradox and (self:getParadox() ~= self:getMinParadox() or self.preferred_paradox > self:getParadox()) then
+		local power = 0
+		if math.abs(self:getParadox() - self.preferred_paradox) > 1 then
+			local duration = self:callTalent(self.T_SPACETIME_TUNING, "getDuration")
+			power = (self.preferred_paradox - self:getParadox())/duration
+			self:setEffect(self.EFF_SPACETIME_TUNING, duration, {power=power})
+		end
+	end
 
 	self:useEnergy()
 end
@@ -1583,23 +1594,44 @@ function _M:teleportRandom(x, y, dist, min_dist)
 	if game.level.data.no_teleport_south and y + dist > self.y then
 		y = self.y - dist
 	end
+	
+	-- Dimensional Anchor, prevent teleports and deal damage
 	if self:hasEffect(self.EFF_DIMENSIONAL_ANCHOR) then
 		local p = self:hasEffect(self.EFF_DIMENSIONAL_ANCHOR)
-		DamageType:get(DamageType.MATTER).projector(p.src or self, self.x, self.y, DamageType.MATTER, p.damage)
+		DamageType:get(DamageType.WARP).projector(p.src or self, self.x, self.y, DamageType.WARP, p.damage)
 		return
 	end
+	
 	local ox, oy = self.x, self.y
 	local ret = engine.Actor.teleportRandom(self, x, y, dist, min_dist)
 	if self.x ~= ox or self.y ~= oy then
+			-- Phase Pulse
+		if self:isTalentActive(self.T_PHASE_PULSE) then
+			self:callTalent(self.T_PHASE_PULSE, "doPulse", ox, oy)
+		end	
+	
 		self.x, self.y, ox, oy = ox, oy, self.x, self.y
 		self:dropNoTeleportObjects()
 		if self:attr("defense_on_teleport") or self:attr("resist_all_on_teleport") or self:attr("effect_reduction_on_teleport") then
 			self:setEffect(self.EFF_OUT_OF_PHASE, 5, {defense=self:attr("defense_on_teleport") or 0, resists=self:attr("resist_all_on_teleport") or 0, effect_reduction=self:attr("effect_reduction_on_teleport") or 0})
 		end
-		if self:knowTalent(self.T_PHASE_SHIFT) then
-			self:callTalent(self.T_PHASE_SHIFT, "doPhaseShift")
+		
+		-- Dimensional shift, chance to clear effects on teleport
+		if self:knowTalent(self.T_DIMENSIONAL_SHIFT) then
+			self:callTalent(self.T_DIMENSIONAL_SHIFT, "doShift")
 		end
+		
+		-- Teleportation does not clear Time Dilation
+		if self:isTalentActive(self.T_TIME_DILATION) then
+			self:callTalent(self.T_TIME_DILATION, "updateOnTeleport", ox, oy)
+		end			
+		
 		self.x, self.y, ox, oy = ox, oy, self.x, self.y
+	else
+		-- Phase Blast failure
+		if self:isTalentActive(self.T_PHASE_PULSE) then
+			self:callTalent(self.T_PHASE_PULSE, "doPulse", ox, oy, true)
+		end
 	end
 	return ret
 end
@@ -2131,6 +2163,21 @@ function _M:onTakeHit(value, src, death_note)
 			return 0
 		end
 	end
+	if self:attr("phase_shift_chrono") and not self.turn_procs.phase_shift_chrono and value > self.max_life *0.1 then
+		self.turn_procs.phase_shift_chrono = true
+		local nx, ny = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true})
+		if nx then
+			local ox, oy = self.x, self.y
+			if not self:teleportRandom(nx, ny, 0) then
+				game.logSeen(self, "The spell fizzles!")
+			else
+				game.level.map:particleEmitter(ox, oy, 1, "temporal_teleport")
+				game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
+				game:delayedLogDamage(src or {}, self, 0, ("#STEEL_BLUE#(%d shifted)#LAST#"):format(value/2), nil)
+				value = value/2
+			end
+		end
+	end
 
 	if self:attr("retribution") then
 	-- Absorb damage into the retribution
@@ -2261,7 +2308,7 @@ function _M:onTakeHit(value, src, death_note)
 			self:removeEffect(self.EFF_PSI_DAMAGE_SHIELD)
 		end
 	end
-
+	
 	if value > 0 and self:attr("shadow_empathy") then
 		-- Absorb damage into a random shadow
 		local shadow = self:callTalent(self.T_SHADOW_EMPATHY, "getRandomShadow")
@@ -2329,6 +2376,11 @@ function _M:onTakeHit(value, src, death_note)
 			value = 0
 		end
 	end
+	
+	-- Paradox Shield
+	if value > 0 and self:isTalentActive(self.T_PRESERVE_PATTERN) then
+		value = self:callTalent(self.T_PRESERVE_PATTERN, "doPerservePattern", src, value)
+	end
 
 	if value <=0 then return 0 end
 	if self.knowTalent and (self:knowTalent(self.T_SEETHE) or self:knowTalent(self.T_GRIM_RESOLVE)) then
@@ -2989,7 +3041,7 @@ function _M:die(src, death_note)
 	if src and src.attr and src:attr("psi_per_kill") then
 		src:incPsi(src:attr("psi_per_kill"))
 	end
-
+	
 	-- Increases blood frenzy
 	if src and src.knowTalent and src:knowTalent(src.T_BLOOD_FRENZY) and src:isTalentActive(src.T_BLOOD_FRENZY) then
 		src.blood_frenzy = src.blood_frenzy + src:callTalent(src.T_BLOOD_FRENZY,"bonuspower")
@@ -3008,7 +3060,7 @@ function _M:die(src, death_note)
 			rsrc.changed = true
 		end
 	end
-
+	
 	-- handle hate changes on kill
 	if src and src.knowTalent and src:knowTalent(src.T_HATE_POOL) then
 		local t = src:getTalentFromId(src.T_HATE_POOL)
@@ -3077,6 +3129,7 @@ function _M:die(src, death_note)
 		p.src:project({type="ball", radius=4, x=self.x, y=self.y}, self.x, self.y, DamageType.ACID, p.explosion, {type="acid"})
 	end
 
+	-- Chronomancy stuff
 	if self:hasEffect(self.EFF_TEMPORAL_DESTABILIZATION) then
 		local p = self:hasEffect(self.EFF_TEMPORAL_DESTABILIZATION)
 		if self:hasEffect(self.EFF_CONTINUUM_DESTABILIZATION) then
@@ -3105,6 +3158,11 @@ function _M:die(src, death_note)
 			end
 		end)
 	end
+	
+	if self:hasEffect(self.EFF_TRIM_THREADS) then
+		local p = self:hasEffect(self.EFF_TRIM_THREADS)
+		p.src:incParadox(-p.reduction)
+	end	
 
 	if self:hasEffect(self.EFF_GHOUL_ROT) then
 		local p = self:hasEffect(self.EFF_GHOUL_ROT)
@@ -3218,6 +3276,7 @@ function _M:resetToFull()
 	self.air = self.max_air
 	self.psi = self.max_psi
 	self.hate = self.max_hate
+	self.paradox = self.preferred_paradox or 300
 end
 
 -- Level up talents to match actor level
@@ -4341,7 +4400,7 @@ end
 
 --- Paradox checks
 function _M:getModifiedParadox()
-	local will_modifier = 2 + self:callTalent(self.T_PARADOX_MASTERY,"WilMult")
+	local will_modifier = (1 + (self:attr("paradox_will_multi") or 0)) * 2
 	will_modifier = (self:getWil() + (self:attr("paradox_reduce_anomalies") or 0)) * will_modifier
 	local sustain_modifier = self:getMinParadox()
 	local modified_paradox = math.max(0, self:getParadox() - will_modifier + sustain_modifier)
@@ -4375,8 +4434,9 @@ function _M:paradoxDoAnomaly(reduction, anomaly_type, chance, target, silent)
 	end
 
 	-- See if we create an anomaly
-	if not game.zone.no_anomalies and not self:attr("no_paradox_fail") and not self.turn_procs.anomalies_checked then
-		self.turn_procs.anomalies_checked = true -- This is so players can't chain cancel out of targeting to trigger anomalies on purpose, we clear it out in postUse
+	if not game.zone.no_anomalies and not self:attr("no_paradox_fail") then
+		if not forced and self.turn_procs.anomalies_checked then return false end  -- This is so players can't chain cancel out of targeting to trigger anomalies on purpose, we clear it out in postUse
+		if not forced then self.turn_procs.anomalies_checked = true end
 
 		-- return true if we roll an anomly
 		if rng.percent(chance) then
@@ -4385,43 +4445,46 @@ function _M:paradoxDoAnomaly(reduction, anomaly_type, chance, target, silent)
 				anomaly_type = "major"
 			else
 				-- Check for Bias?
-				if self.anomaly_bias and rng.percent(self.anomaly_bias.chance) then
-					anomaly_type = self.anomaly_bias.type
-				end
+				if self.anomaly_bias and rng.percent(self.anomaly_bias.chance) then anomaly_type = self.anomaly_bias.type end
+				-- Revert no-major to random
+				if anomaly_type == "no-major" then anomaly_type = "random" end
 			end
 
 			-- Now pick anomalies filtered by type
 			local ts = {}
 			for id, t in pairs(self.talents_def) do
-				if anomaly_type ~= "random" and anomaly_type ~= "no-major" then
-					if t.type[1] == "chronomancy/anomalies" and t.anomaly_type and t.anomaly_type == anomaly_type then ts[#ts+1] = id end
+				if anomaly_type ~= "random" then
+					if t.type[1] == "chronomancy/anomalies" and t.anomaly_type and t.anomaly_type == anomaly_type and not self:isTalentCoolingDown(t) then ts[#ts+1] = id end
 				else
-					if t.type[1] == "chronomancy/anomalies" and t.anomaly_type and t.anomaly_type ~= "major" then ts[#ts+1] = id end
+					if t.type[1] == "chronomancy/anomalies" and t.anomaly_type and t.anomaly_type ~= "major" and not self:isTalentCoolingDown(t) then ts[#ts+1] = id end
 				end
 			end
 
 			-- Did we find anomalies?
 			if ts[1] then
 				-- Do we have a target?  If not we pass to anomaly targeting
-				-- The ignore energy calls here allow anomalies to be cast even when it's not the players turn (i.e. Preserve Pattern)
+				-- The ignore energy calls here allow anomalies to be cast even when it's not the players turn
 				if target then
-					self:forceUseTalent(rng.table(ts), {ignore_cooldown=true, ignore_energy=true, force_target=target})
+					self:attr("anomaly_forced_target", 1)
+					self:forceUseTalent(rng.table(ts), {ignore_energy=true, force_target=target})
+					self:attr("anomaly_forced_target", -1)
 				else
-					self:forceUseTalent(rng.table(ts), {ignore_cooldown=true, ignore_energy=true})
+					self:forceUseTalent(rng.table(ts), {ignore_energy=true})
 				end
-				-- Drop some game messages
-				if not silent then
-					if forced then
-						game.logPlayer(self, "#STEEL_BLUE#You've moved to another time thread.")
-					else
-						game.logPlayer(self, "#LIGHT_RED#You lose control and unleash an anomaly!")
-					end
-				end
-				-- Reduce Paradox
-				if reduction and reduction > 0 then
-					self:incParadox(-reduction)
+			end
+			
+			-- Drop some game messages; these happen so Paradox gets reduced even if an anomaly isn't found
+			if not silent then
+				if forced then
+					game.logPlayer(self, "#STEEL_BLUE#You've moved to another time thread.")
+				else
+					game.logPlayer(self, "#LIGHT_RED#You lose control and unleash an anomaly!")
 				end
 			end
+			-- Reduce Paradox
+			if reduction and reduction > 0 then
+				self:incParadox(-reduction)
+			end
 
 			return true
 		end
@@ -4443,9 +4506,15 @@ function _M:incParadox(paradox)
 	if self:getModifiedParadox() < 600 and self:getModifiedParadox() + paradox >= 600 then
 		game.logPlayer(self, "#LIGHT_RED#Spacetime fights against your control!")
 	end
-	if self:getParadox() > 600 and self:getParadox() + paradox <= 600 then
+	if self:getModifiedParadox() > 600 and self:getModifiedParadox() + paradox <= 600 then
 		game.logPlayer(self, "#LIGHT_BLUE#Spacetime has calmed...  somewhat.")
 	end
+	
+	-- Cosmic Cycle
+	if self:isTalentActive(self.T_COSMIC_CYCLE) then
+		self:callTalent(self.T_COSMIC_CYCLE, "doCosmicCycle")
+	end
+	
 	return previous_incParadox(self, paradox)
 end
 
@@ -4839,7 +4908,11 @@ function _M:getTalentSpeed(t)
 	local speed_type = self:getTalentSpeedType(t)
 	local speed = self:getSpeed(speed_type)
 
-	-- EDGE: Put Quicken Here?
+	-- Quicken
+	local p = self:isTalentActive(self.T_QUICKEN)
+	if p and p.talent == t.id then
+		speed = math.max(0.1, speed - self:callTalent(self.T_QUICKEN, "getPower"))
+	end
 
 	local hd = {"Actor:getTalentSpeed", talent = t, speed_type = speed_type, speed = speed,}
 	if self:triggerHook(hd) then speed = hd.speed end
@@ -5038,6 +5111,7 @@ function _M:postUseTalent(ab, ret, silent)
 	if ab.id ~= self.T_GATHER_THE_THREADS and ab.is_spell then self:breakChronoSpells() end
 	if not ab.no_reload_break then self:breakReloading() end
 	self:breakStepUp()
+	self:breakSpacetimeTuning()
 	--if not (util.getval(ab.no_energy, self, ab) or ab.no_break_channel) and not (ab.mode == "sustained" and self:isTalentActive(ab.id)) then self:breakPsionicChannel(ab.id) end
 
 	for tid, _ in pairs(self.sustain_talents) do
@@ -5047,12 +5121,6 @@ function _M:postUseTalent(ab, ret, silent)
 		end
 	end
 
-	if ab.id ~= self.T_REDUX and self:hasEffect(self.EFF_REDUX) and ab.type[1]:find("^chronomancy/") and ab.mode == "activated" and self:getTalentLevel(self.T_REDUX) >= self:getTalentLevel(ab.id) then
-		self:removeEffect(self.EFF_REDUX)
-		-- this still consumes energy but works as the talent suggests it does
-		self:forceUseTalent(ab.id, {ignore_energy=true, ignore_cd = true})
-	end
-
 	if not ab.innate and self:hasEffect(self.EFF_RAMPAGE) and ab.id ~= self.T_RAMPAGE and ab.id ~= self.T_SLAM then
 		local eff = self:hasEffect(self.EFF_RAMPAGE)
 		value = self.tempeffect_def[self.EFF_RAMPAGE].do_postUseTalent(self, eff, value)
@@ -5141,9 +5209,6 @@ function _M:breakStepUp()
 	if self:hasEffect(self.EFF_SKIRMISHER_DIRECTED_SPEED) then
 		self:removeEffect(self.EFF_SKIRMISHER_DIRECTED_SPEED)
 	end
-	if self:hasEffect(self.EFF_CELERITY) then
-		self:removeEffect(self.EFF_CELERITY)
-	end
 end
 
 --- Breaks lightning speed if active
@@ -5160,6 +5225,12 @@ function _M:breakChronoSpells()
 	end
 end
 
+function _M:breakSpacetimeTuning()
+	if self:hasEffect(self.EFF_SPACETIME_TUNING) then
+		self:removeEffect(self.EFF_SPACETIME_TUNING)
+	end
+end
+
 --- Break Psionic Channels
 function _M:breakPsionicChannel(talent)
 	if self:isTalentActive(self.T_MIND_STORM) and talent ~= self.T_MIND_STORM then
@@ -5212,7 +5283,7 @@ function _M:getTalentFullDescription(t, addlevel, config, fake_mastery)
 		if t.positive then d:add({"color",0x6f,0xff,0x83}, "Positive energy cost: ", {"color",255, 215, 0}, ""..math.round(util.getval(t.positive, self, t) * (100 + self:combatFatigue()) / 100, 0.1), true) end
 		if t.negative then d:add({"color",0x6f,0xff,0x83}, "Negative energy cost: ", {"color", 127, 127, 127}, ""..math.round(util.getval(t.negative, self, t) * (100 + self:combatFatigue()) / 100, 0.1), true) end
 		if t.hate then d:add({"color",0x6f,0xff,0x83}, "Hate cost:  ", {"color", 127, 127, 127}, ""..math.round(util.getval(t.hate, self, t) * (100 + 2 * self:combatFatigue()) / 100, 0.1), true) end
-		if t.paradox then d:add({"color",0x6f,0xff,0x83}, "Paradox cost: ", {"color",  176, 196, 222}, ""..math.round(util.getval(t.paradox, self, t), 0.1), true) end
+		if t.paradox then d:add({"color",0x6f,0xff,0x83}, "Paradox cost: ", {"color",  176, 196, 222}, ""..math.round(util.getval(t.paradox, self, t)), true) end
 		if t.psi then d:add({"color",0x6f,0xff,0x83}, "Psi cost: ", {"color",0x7f,0xff,0xd4}, ""..math.round(util.getval(t.psi, self, t) * (100 + 2 * self:combatFatigue()) / 100, 0.1), true) end
 		if t.feedback then d:add({"color",0x6f,0xff,0x83}, "Feedback cost: ", {"color",0xFF, 0xFF, 0x00}, ""..math.round(util.getval(t.feedback, self, t) * (100 + 2 * self:combatFatigue()) / 100, 0.1), true) end
 		if t.fortress_energy then d:add({"color",0x6f,0xff,0x83}, "Fortress Energy cost: ", {"color",0x00,0xff,0xa0}, ""..math.round(t.fortress_energy, 0.1), true) end
@@ -5236,7 +5307,7 @@ function _M:getTalentFullDescription(t, addlevel, config, fake_mastery)
 		else d:add({"color",0x6f,0xff,0x83}, "Range: ", {"color",0xFF,0xFF,0xFF}, "melee/personal", true)
 		end
 		if not config.ignore_ressources then
-			if self:getTalentCooldown(t) then d:add({"color",0x6f,0xff,0x83}, "Cooldown: ", {"color",0xFF,0xFF,0xFF}, ""..self:getTalentCooldown(t), true) end
+			if self:getTalentCooldown(t) then d:add({"color",0x6f,0xff,0x83}, ("%sCooldown: "):format(t.fixed_cooldown and "Fixed " or ""), {"color",0xFF,0xFF,0xFF}, ""..self:getTalentCooldown(t), true) end
 		end
 		local speed = self:getTalentProjectileSpeed(t)
 		if speed then d:add({"color",0x6f,0xff,0x83}, "Travel Speed: ", {"color",0xFF,0xFF,0xFF}, ""..(speed * 100).."% of base", true)
@@ -5273,7 +5344,7 @@ function _M:getTalentFullDescription(t, addlevel, config, fake_mastery)
 		end
 	else
 		if not config.ignore_ressources then
-			if self:getTalentCooldown(t) then d:add({"color",0x6f,0xff,0x83}, "Cooldown: ", {"color",0xFF,0xFF,0xFF}, ""..self:getTalentCooldown(t), true) end
+			if self:getTalentCooldown(t) then d:add({"color",0x6f,0xff,0x83}, ("%sCooldown: "):format(t.fixed_cooldown and "Fixed " or ""), {"color",0xFF,0xFF,0xFF}, ""..self:getTalentCooldown(t), true) end
 		end
 	end
 
@@ -5320,7 +5391,12 @@ function _M:getTalentCooldown(t)
 	if eff and not self:attr("talent_reuse") then
 		cd = 1 + cd * eff.power
 	end
-
+	
+	local p = self:isTalentActive(self.T_MATRIX)
+	if p and p.talent == t.id then
+		cd = math.floor(cd * (1 - self:callTalent(self.T_MATRIX, "getPower")))
+	end
+	
 	if t.is_spell then
 		return math.ceil(cd * (1 - (self.spell_cooldown_reduction or 0)))
 	elseif t.is_summon then
@@ -5340,6 +5416,12 @@ function _M:startTalentCooldown(t, v)
 		self.talents_cd[t.id] = math.max(v, self.talents_cd[t.id] or 0)
 	else
 		if not t.cooldown then return end
+		if t.id ~= self.T_REDUX and self:hasEffect(self.EFF_REDUX) then
+			local eff = self:hasEffect(self.EFF_REDUX) 
+			if t.type[1]:find("^chronomancy/") and self:getTalentCooldown(t) <= eff.max_cd and t.mode == "activated" and not t.fixed_cooldown then
+				return
+			end
+		end
 		self.talents_cd[t.id] = self:getTalentCooldown(t)
 	end
 	if self.talents_cd[t.id] <= 0 then self.talents_cd[t.id] = nil end
diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua
index 9eb86b3b9a795d41d8a87bb22753556003e20fbb..f694ff0e1abd43a928544389cf42e7f1b873c2b9 100644
--- a/game/modules/tome/class/Player.lua
+++ b/game/modules/tome/class/Player.lua
@@ -1216,7 +1216,7 @@ function _M:playerUseItem(object, item, inven)
 				self:breakStepUp()
 				self:breakStealth()
 				self:breakLightningSpeed()
-				--	self:breakPsionicChannel()
+				self:breakSpacetimeTuning()
 				return true
 			end
 
@@ -1224,7 +1224,7 @@ function _M:playerUseItem(object, item, inven)
 			self:breakStealth()
 			self:breakLightningSpeed()
 			self:breakReloading()
-			--	self:breakPsionicChannel()
+			self:breakSpacetimeTuning()
 			self.changed = true
 		end)
 		local ok, ret = coroutine.resume(co)
diff --git a/game/modules/tome/class/interface/Archery.lua b/game/modules/tome/class/interface/Archery.lua
index 832baa330b01188b753675c9799f59072b826fb6..ffde75dff37d7678769022b812fb629e4f60bdbf 100644
--- a/game/modules/tome/class/interface/Archery.lua
+++ b/game/modules/tome/class/interface/Archery.lua
@@ -429,12 +429,9 @@ local function archery_projectile(tx, ty, tg, self, tmp)
 	-- Temporal cast
 	if hitted and self:knowTalent(self.T_WEAPON_FOLDING) and self:isTalentActive(self.T_WEAPON_FOLDING) then
 		local dam = self:callTalent(self.T_WEAPON_FOLDING, "getDamage")
-		local burst_damage = 0
-		local burst_radius = 0
 		if self:knowTalent(self.T_FRAYED_THREADS) then
-			burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent")
-			burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius")
-			dam = dam - burst_damage
+			local burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent")
+			local burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius")
 			self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.TEMPORAL, burst_damage)
 		end
 		if dam > 0 and not target.dead then
@@ -444,13 +441,10 @@ local function archery_projectile(tx, ty, tg, self, tmp)
 	if hitted and self:knowTalent(self.T_IMPACT) and self:isTalentActive(self.T_IMPACT) then
 		local dam = self:callTalent(self.T_IMPACT, "getDamage")
 		local power = self:callTalent(self.T_IMPACT, "getApplyPower")
-		local burst_damage = 0
-		local burst_radius = 0
 		if self:knowTalent(self.T_FRAYED_THREADS) then
-			burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent")
-			burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius")
-			dam = dam - burst_damage
-			self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.IMPACT, {dam=burst_damage, daze=dam/2, power_check=power})
+			local burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent")
+			local burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius")
+			self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.IMPACT, {dam=burst_damage, daze=burst_damage/2, power_check=power})
 		end
 		if dam > 0 and not target.dead then
 			DamageType:get(DamageType.IMPACT).projector(self, target.x, target.y, DamageType.IMPACT, {dam=dam, daze=dam/2, power_check=power}, tmp)
@@ -492,7 +486,7 @@ local function archery_projectile(tx, ty, tg, self, tmp)
 	-- Resource regen on hit
 	if hitted and self:attr("stamina_regen_on_hit") then self:incStamina(self.stamina_regen_on_hit) end
 	if hitted and self:attr("mana_regen_on_hit") then self:incMana(self.mana_regen_on_hit) end
-
+	
 	-- Ablative armor
 	if hitted and not target.dead and target:attr("carbon_spikes") then
 		if target.carbon_armor >= 1 then
diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua
index 59a621c71da1ed15f385b3021669c190c5c10aef..a62b7e6026581dd0c557b597e28f39ba1c11743f 100644
--- a/game/modules/tome/class/interface/Combat.lua
+++ b/game/modules/tome/class/interface/Combat.lua
@@ -89,6 +89,7 @@ function _M:attackTarget(target, damtype, mult, noenergy, force_unharmed)
 
 	-- Break before we do the blow, because it might start step up, we dont want to insta-cancel it
 	self:breakStepUp()
+	self:breakSpacetimeTuning()
 
 	if self:attr("feared") then
 		if not noenergy then
@@ -625,9 +626,8 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
 		local burst_damage = 0
 		local burst_radius = 0
 		if self:knowTalent(self.T_FRAYED_THREADS) then
-			burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent")
-			burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius")
-			dam = dam - burst_damage
+			local burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent")
+			local burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius")
 			self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.TEMPORAL, burst_damage)
 		end
 		if dam > 0 and not target.dead then
@@ -637,13 +637,10 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
 	if hitted and self:knowTalent(self.T_IMPACT) and self:isTalentActive(self.T_IMPACT) then
 		local dam = self:callTalent(self.T_IMPACT, "getDamage")
 		local power = self:callTalent(self.T_IMPACT, "getApplyPower")
-		local burst_damage = 0
-		local burst_radius = 0
 		if self:knowTalent(self.T_FRAYED_THREADS) then
-			burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent")
-			burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius")
-			dam = dam - burst_damage
-			self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.IMPACT, {dam=burst_damage, daze=dam/2, power_check=power})
+			local burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent")
+			local burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius")
+			self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.IMPACT, {dam=burst_damage, daze=burst_damage/2, power_check=power})
 		end
 		if dam > 0 and not target.dead then
 			DamageType:get(DamageType.IMPACT).projector(self, target.x, target.y, DamageType.IMPACT, {dam=dam, daze=dam/2, power_check=power}, tmp)
@@ -886,6 +883,16 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
 	if hitted and self:attr("mana_regen_on_hit") then self:incMana(self.mana_regen_on_hit) end
 	if hitted and self:attr("psi_regen_on_hit") then self:incPsi(self.psi_regen_on_hit) end
 
+	-- Ablative armor
+	if hitted and not target.dead and target:attr("carbon_spikes") then
+		if target.carbon_armor >= 1 then
+			target.carbon_armor = target.carbon_armor - 1
+		else
+			-- Deactivate without loosing energy
+			target:forceUseTalent(target.T_CARBON_SPIKES, {ignore_energy=true})
+		end
+	end
+	
 	if hitted and not target.dead and target:knowTalent(target.T_STONESHIELD) then
 		local t = target:getTalentFromId(target.T_STONESHIELD)
 		local m, mm, e, em = t.getValues(self, t)
@@ -893,12 +900,6 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam)
 		target:incEquilibrium(-math.min(dam * e, em))
 	end
 
-	-- Ablative Armor
-	if hitted and not target.dead and target:attr("carbon_spikes") then
-		local t = target:getTalentFromId(target.T_CARBON_SPIKES)
-		t.do_carbonLoss(target, t)
-	end
-
 	-- Set Up
 	if not hitted and not target.dead and not evaded and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:hasEffect(target.EFF_DEFENSIVE_MANEUVER) then
 		local t = target:getTalentFromId(target.T_SET_UP)
@@ -1190,12 +1191,12 @@ function _M:combatArmor()
 			add = add + ga.getArmor(self, ga)
 		end
 	end
-	if self:knowTalent(self.T_CARBON_SPIKES) and self:isTalentActive(self.T_CARBON_SPIKES) then
-		add = add + self.carbon_armor
-	end
 	if self:knowTalent(self.T_ARMOUR_OF_SHADOWS) and not game.level.map.lites(self.x, self.y) then
 		add = add + self:callTalent(self.T_ARMOUR_OF_SHADOWS,"ArmourBonus")
 	end
+	if self:knowTalent(self.T_CARBON_SPIKES) and self:isTalentActive(self.T_CARBON_SPIKES) then
+		add = add + self.carbon_armor
+	end
 	return self.combat_armor + add
 end
 
diff --git a/game/modules/tome/class/interface/TooltipsData.lua b/game/modules/tome/class/interface/TooltipsData.lua
index 00265d6f6cc41f4f87f02b1c201ae36e8bec829b..6171a93766ed57bb367608c23dcddac8efac065d 100644
--- a/game/modules/tome/class/interface/TooltipsData.lua
+++ b/game/modules/tome/class/interface/TooltipsData.lua
@@ -112,7 +112,7 @@ All afflicted talents are based on Hate, and many are more effective at higher l
 TOOLTIP_PARADOX = [[#GOLD#Paradox#LAST#
 Paradox represents how stable the current timeline is. 
 As your Paradox grows so does your Spellpower, but you may be shifted to a more stable timeline when you attempt to use your magic.
-When your Paradox is above 600 the shifts will become more violent.
+When your modified Paradox is above 600 the shifts will become more violent.
 Your control over chronomancy spells increases with your Willpower.
 ]]
 
diff --git a/game/modules/tome/data/birth/classes/chronomancer.lua b/game/modules/tome/data/birth/classes/chronomancer.lua
index 149154ecf1d4f778a093e655c380841358f07f68..c3feec1c452ca56b1c21e50bc4eb92fcc30cdc6f 100644
--- a/game/modules/tome/data/birth/classes/chronomancer.lua
+++ b/game/modules/tome/data/birth/classes/chronomancer.lua
@@ -69,25 +69,32 @@ newBirthDescriptor{
 	random_rarity = 2,
 	stats = { mag=5, wil=2, con=2, },
 	talents_types = {
-		["chronomancy/age-manipulation"]={true, 0.3},
-	--	["chronomancy/anomalies"]={true, 0},
-		["chronomancy/chronomancy"]={true, 0.3},
-		["chronomancy/energy"]={true, 0.3},
+		-- class
 		["chronomancy/fate-threading"]={true, 0.3},
 		["chronomancy/gravity"]={true, 0.3},
-		["chronomancy/matter"]={true, 0.3},
-		["chronomancy/paradox"]={false, 0.3},
+		["chronomancy/spacetime-folding"]={true, 0.3},
 		["chronomancy/speed-control"]={true, 0.3},
-		["chronomancy/timeline-threading"]={false, 0.3},
+		["chronomancy/timeline-threading"]={true, 0.3},
 		["chronomancy/timetravel"]={true, 0.3},
+		
+		-- locked class
+		["chronomancy/spellbinding"]={false, 0.3},
+		["chronomancy/spatial-tears"]={false, 0.3},
+
+		-- generic
+		["chronomancy/chronomancy"]={true, 0.3},
+		["chronomancy/fate-weaving"]={true, 0.3},
 		["chronomancy/spacetime-weaving"]={true, 0.3},
-		["cunning/survival"]={false, 0},
+
+		-- locked generic
+		["chronomancy/energy"]={false, 0.3},
+		["cunning/survival"]={false, 0},	
 	},
 	talents = {
-		[ActorTalents.T_DISENTANGLE] = 1,
+		[ActorTalents.T_TEMPORAL_BOLT] = 1,
 		[ActorTalents.T_DIMENSIONAL_STEP] = 1,
-		[ActorTalents.T_DUST_TO_DUST] = 1,
-		[ActorTalents.T_TURN_BACK_THE_CLOCK] = 1,
+		[ActorTalents.T_RETHREAD] = 1,
+		[ActorTalents.T_DISENTANGLE] = 1,
 	},
 	copy = {
 		max_life = 90,
@@ -119,21 +126,25 @@ newBirthDescriptor{
 		-- class
 		["chronomancy/blade-threading"]={true, 0.3},
 		["chronomancy/bow-threading"]={true, 0.3},
-		["chronomancy/fate-threading"]={true, 0.3},
+		["chronomancy/fate-threading"]={true, 0.1},
 		["chronomancy/spacetime-folding"]={true, 0.3},
-		["chronomancy/speed-control"]={true, 0.1},
+		["chronomancy/speed-control"]={true, 0.3},
 		["chronomancy/guardian"]={true, 0.3},
 		
+		-- class locked
 		["chronomancy/threaded-combat"]={false, 0.3},
 		["chronomancy/temporal-hounds"]={false, 0.3},
 		["chronomancy/timetravel"]={false, 0.1},
 		
 		-- generic
-		["cunning/survival"]={false, 0},
 		["technique/combat-training"]={true, 0.3},
+		
 		["chronomancy/chronomancy"]={true, 0.3},
-		["chronomancy/fate-weaving"]={false, 0.1},
 		["chronomancy/spacetime-weaving"]={true, 0.3},
+		
+		-- generic locked
+		["chronomancy/fate-weaving"]={false, 0.1},
+		["cunning/survival"]={false, 0},
 	},
 	birth_example_particles = "temporal_focus",
 	talents = {
diff --git a/game/modules/tome/data/chats/escort-quest.lua b/game/modules/tome/data/chats/escort-quest.lua
index e0ebbba00f98e8e6a2a16d180f21d2448f584853..2d956450b5580ac99c8851350fb312be8faeeb18 100644
--- a/game/modules/tome/data/chats/escort-quest.lua
+++ b/game/modules/tome/data/chats/escort-quest.lua
@@ -164,11 +164,11 @@ local reward_types = {
 		types = {
 			["chronomancy/chronomancy"] = 0.8,
 		},
-		talents = {								--- Note to Darkgod; burn this comment after reading
-			[Talents.T_CONTINGENCY] = 1,		--- Quick explanation; Precognition is really similar to track now so there's the overlap argument
-			[Talents.T_SEE_THE_THREADS] = 1,	--- The other thought is that these are both some pretty cool effects and may help to hook players that otherwise
-		--	[Talents.T_PRECOGNITION] = 1,		--- wouldn't stick around long enough to unlock Chronomancers.  Anyway that's my thoughts on it, if it doesn't feel right
-		},										--- feel free to change it.
+		talents = {
+			[Talents.T_CONTINGENCY] = 1,
+		--	[Talents.T_SEE_THE_THREADS] = 1,
+			[Talents.T_PRECOGNITION] = 1,
+		},										
 		stats = {
 			[Stats.STAT_MAG] = 2,
 			[Stats.STAT_CUN] = 1,
diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua
index cb1c8cf1e54e38c9d7dc3b6f43c4e27d8774dfa5..7fd060785c8eb7a9092a49a1a7633da3ba6ae475 100644
--- a/game/modules/tome/data/damage_types.lua
+++ b/game/modules/tome/data/damage_types.lua
@@ -246,17 +246,6 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 
 		--target.T_STONE_FORTRESS could be checked/applied here (ReduceDamage function in Dwarven Fortress talent)
 
-		-- Damage Smearing
-		if dam > 0 and type ~= DamageType.TEMPORAL and target.isTalentActive and target:isTalentActive(target.T_DAMAGE_SMEARING) then
-			local percent = target:callTalent(target.T_DAMAGE_SMEARING, "getPercent")
-			local duration = target:callTalent(target.T_DAMAGE_SMEARING, "getDuration")
-			local smear = dam * percent
-			local type = DamageType.TEMPORAL
-			target:setEffect(target.EFF_SMEARED, duration, {src=src, power=smear/duration, no_ct_effect=true})
-			game:delayedLogDamage(src, target, 0, ("%s(%d smeared)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", smear), false)
-			dam = dam - smear
-		end
-
 		-- affinity healing, we store it to apply it after damage is resolved
 		local affinity_heal = 0
 		if target.damage_affinity then
@@ -328,13 +317,6 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 			if lastdam - dam  > 0 then game:delayedLogDamage(src, target, 0, ("%s(%d antimagic)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", lastdam - dam), false) end
 		end
 
-		if dam > 0 and target.isTalentActive and target:isTalentActive(target.T_ENERGY_DECOMPOSITION) then
-			local t = target:getTalentFromId(target.T_ENERGY_DECOMPOSITION)
-			lastdam = dam
-			dam = t.on_damage(target, t, type, dam)
-			if lastdam - dam  > 0 then game:delayedLogDamage(src, target, 0, ("%s(%d dissipated)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", lastdam - dam), false) end
-		end
-
 		-- Flat damage reduction ("armour")
 		if dam > 0 and target.flat_damage_armor then
 			local dec = math.min(dam, (target.flat_damage_armor.all or 0) + (target.flat_damage_armor[type] or 0))
@@ -362,7 +344,7 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 			print("[PROJECTOR] numbed dam", dam)
 		end
 		if src:attr("generic_damage_penalty") then
-			dam = dam - dam * src:attr("generic_damage_penalty") / 100
+			dam = dam - dam * math.min(100, src:attr("generic_damage_penalty")) / 100
 			print("[PROJECTOR] generic dam", dam)
 		end
 
@@ -372,6 +354,11 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 			local def = src.tempeffect_def[src.EFF_CURSE_OF_MISFORTUNE]
 			dam = def.doUnfortunateEnd(src, eff, target, dam)
 		end
+		
+		if src and src.hasEffect and src:hasEffect(src.EFF_SEAL_FATE) then
+			src:callEffect(src.EFF_SEAL_FATE, "doDamage", target)
+		end
+
 
 		if src:attr("crushing_blow") and (dam * (1.25 + (src.combat_critical_power or 0)/200)) > target.life then
 			dam = dam * (1.25 + (src.combat_critical_power or 0)/200)
@@ -467,31 +454,7 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 		end
 		-- Braided damage
 		if dam > 0 and target:hasEffect(target.EFF_BRAIDED) then
-			local p = target:hasEffect(target.EFF_BRAIDED)
-			local braid_damage = dam * p.power/ 100
-			if p.braid_one and not p.braid_one.dead and p.braid_one:hasEffect(p.braid_one.EFF_BRAIDED) then
-				game:delayedLogMessage(p.src, p.braid_one, "braided", "#CRIMSON##Source# damages #Target# through the Braid!")
-				game:delayedLogDamage(p.src, p.braid_one, braid_damage, ("#PINK#%d braided #LAST#"):format(braid_damage), false)
-				p.braid_one:takeHit(braid_damage, p.src)
-			end
-			if p.braid_two and not p.braid_two.dead and p.braid_two:hasEffect(p.braid_two.EFF_BRAIDED) then
-				game:delayedLogMessage(p.src, p.braid_two, "braided", "#CRIMSON##Source# damages #Target# through the Braid!")
-				game:delayedLogDamage(p.src, p.braid_two, braid_damage, ("#PINK#%d braided #LAST#"):format(braid_damage), false)
-				p.braid_two:takeHit(braid_damage, p.src)
-			end
-		end
-
-		if dam > 0 and src ~= target and target.knowTalent and target:knowTalent(target.T_SPIN_FATE) then
-			if target.turn_procs and not target.turn_procs.spin_fate then
-				target:callTalent(target.T_SPIN_FATE, "doSpinFate")
-				if target.hasEffect and target:hasEffect(target.EFF_WEBS_OF_FATE) and not target.turn_procs.webs_of_fate then
-					target.turn_procs.webs_of_fate = true
-				elseif target.hasEffect and target:hasEffect(target.EFF_SEAL_FATE) and not target.turn_procs.seal_fate then
-					target.turn_procs.seal_fate = true
-				else
-					target.turn_procs.spin_fate = true
-				end
-			end
+			game:onTickEnd(function()target:callEffect(target.EFF_BRAIDED, "doBraid", dam)end)
 		end
 
 		if target.knowTalent and target:knowTalent(target.T_RESOLVE) then local t = target:getTalentFromId(target.T_RESOLVE) t.on_absorb(target, t, type, dam) end
@@ -608,7 +571,7 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 				end
 			end
 		end
-
+		
 		if src.turn_procs and src.turn_procs.is_crit then
 			if src.knowTalent and src:knowTalent(src.T_ELEMENTAL_SURGE) then
 				src:triggerTalent(src.T_ELEMENTAL_SURGE, nil, target, type, dam)
@@ -616,7 +579,7 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 
 			src.turn_procs.is_crit = nil
 		end
-
+		
 		if src.turn_procs and not src.turn_procs.dazing_damage and src.hasEffect and src:hasEffect(src.EFF_DAZING_DAMAGE) then
 			if target:canBe("stun") then
 				local power = math.max(src:combatSpellpower(), src:combatMindpower(), src:combatPhysicalpower())
@@ -2500,22 +2463,28 @@ newDamageType{
 newDamageType{
 	name = "gravity", type = "GRAVITY",
 	projector = function(src, x, y, type, dam)
+		if _G.type(dam) == "number" then dam = {dam=dam} end
 		local target = game.level.map(x, y, Map.ACTOR)
 		if not target then return end
-		if target and target:attr("never_move") then
-			dam = dam * 1.5
+		if target then
+			if target:isTalentActive(target.T_GRAVITY_LOCUS) then return end
+			if dam.slow then
+				target:setEffect(target.EFF_GRAVITY_SLOW, dam.dur, {dam.slow, apply_power=apply, no_ct_effect=true})
+			end
+			if dam.anti then
+				target:setEffect(target.EFF_ANTI_GRAVITY, dam.dur, {apply_power=apply, no_ct_effect=true})
+			end
 		end
-		DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, dam)
+		DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, dam.dam)
 	end,
 }
 
 newDamageType{
 	name = "gravity pin", type = "GRAVITYPIN",
 	projector = function(src, x, y, type, dam)
-		DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, dam)
 		local target = game.level.map(x, y, Map.ACTOR)
 		local reapplied = false
-		if target then
+		if target and not target:isTalentActive(target.T_GRAVITY_LOCUS) then
 			-- silence the apply message if the target already has the effect
 			for eff_id, p in pairs(target.tmp) do
 				local e = target.tempeffect_def[eff_id]
@@ -2528,6 +2497,7 @@ newDamageType{
 			else
 				game.logSeen(target, "%s resists the pin!", target.name:capitalize())
 			end
+			DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, dam)
 		end
 	end,
 }
@@ -2825,17 +2795,6 @@ newDamageType{
 	end,
 }
 
-newDamageType{
-	name = "temporal echo", type = "TEMPORAL_ECHO",
-	projector = function(src, x, y, type, dam)
-		local target = game.level.map(x, y, Map.ACTOR)
-		if target then
-			dam = (target.max_life - target.life) * dam
-			DamageType:get(DamageType.TEMPORAL).projector(src, x, y, DamageType.TEMPORAL, dam)
-		end
-	end,
-}
-
 newDamageType{
 	name = "draining physical", type = "DEVOUR_LIFE",
 	projector = function(src, x, y, type, dam)
@@ -3385,7 +3344,17 @@ newDamageType{
 	end,
 }
 
+-- Prevents Teleportation
 newDamageType{
+	name = "dimensional anchor", type = "DIMENSIONAL_ANCHOR",
+	projector = function(src, x, y, type, dam)
+		if _G.type(dam) == "number" then dam = {dam=dam, apply_power=apply_power or src:combatSpellpower()} end
+		local target = game.level.map(x, y, Map.ACTOR)
+		if target then
+			target:setEffect(target.EFF_DIMENSIONAL_ANCHOR, 1, {damage=dam.dam, src=src, apply_power=dam.apply_power, no_ct_effect=true})
+		end
+	end,
+}newDamageType{
 	name = "brain storm", type = "BRAINSTORM",
 	projector = function(src, x, y, type, dam)
 		local target = game.level.map(x, y, Map.ACTOR)
@@ -3437,3 +3406,63 @@ newDamageType{
 		end
 	end,
 }
+
+-- Temporal/Physical damage with possible chance to debalitate
+newDamageType{
+	name = "warp", type = "WARP",
+	projector = function(src, x, y, type, dam)
+		local target = game.level.map(x, y, Map.ACTOR)
+		if not target then return end
+		if _G.type(dam) == "number" then dam = {dam=dam, chance=chance or 0, dur=dur or 3, apply_power=apply_power or src:combatSpellpower()} end
+		
+		-- Factor in fractured space bonuses
+		local fracture = false
+		if src.isTalentActive and src:isTalentActive(src.T_FRACTURED_SPACE) then
+			fracture = src:isTalentActive(src.T_FRACTURED_SPACE)
+		end
+		if fracture then
+			dam.chance = math.min(100, dam.chance + (src:callTalent(src.T_FRACTURED_SPACE, "getChance")*fracture.charges))
+			dam.dam = dam.dam * (1 + (src:callTalent(src.T_FRACTURED_SPACE, "getDamage")*fracture.charges)/100)
+		end
+
+		-- Deal Damage
+		DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, dam.dam / 2)
+		DamageType:get(DamageType.TEMPORAL).projector(src, x, y, DamageType.TEMPORAL, dam.dam / 2)
+
+		 -- Increase fracture charges and refresh decay rate
+		 if fracture then
+			fracture.charges = math.min(6, fracture.charges + 1)
+			fracture.decay = 0
+		end
+		
+		-- Pull random effect
+		if rng.percent(dam.chance) then
+			local effect = rng.range(1, 4)
+			if effect == 1 then
+				if target:canBe("stun") then
+					target:setEffect(target.EFF_STUNNED, dam.dur, {apply_power=dam.apply_power})
+				else
+					game.logSeen(target, "%s resists the stun!", target.name:capitalize())
+				end
+			elseif effect == 2 then
+				if target:canBe("blind") then
+					target:setEffect(target.EFF_BLINDED, dam.dur, {apply_power=dam.apply_power})
+				else
+					game.logSeen(target, "%s resists the blindness!", target.name:capitalize())
+				end
+			elseif effect == 3 then
+				if target:canBe("pin") then
+					target:setEffect(target.EFF_PINNED, dam.dur, {apply_power=dam.apply_power})
+				else
+					game.logSeen(target, "%s resists the pin!", target.name:capitalize())
+				end
+			elseif effect == 4 then
+				if target:canBe("confusion") then
+					target:setEffect(target.EFF_CONFUSED, dam.dur, {power=50, apply_power=dam.apply_power})
+				else
+					game.logSeen(target, "%s resists the confusion!", target.name:capitalize())
+				end
+			end
+		end
+	end,
+}
\ No newline at end of file
diff --git a/game/modules/tome/data/general/objects/world-artifacts-far-east.lua b/game/modules/tome/data/general/objects/world-artifacts-far-east.lua
index b894dd7ac9800b0acc47d8e1821de68d3bf569c7..ea54c24a282f6766138bc59df8bdeaf0b74d9e52 100644
--- a/game/modules/tome/data/general/objects/world-artifacts-far-east.lua
+++ b/game/modules/tome/data/general/objects/world-artifacts-far-east.lua
@@ -554,13 +554,11 @@ newEntity{ base = "BASE_STAFF",
 			[Talents.T_PARADOX_CLONE] = 7,
 			[Talents.T_TEMPORAL_CLONE] = 5,
 			[Talents.T_TIME_STOP] = 5,
-		--	[Talents.T_GRAVITY_WELL] = 6,  -- thematically wrong
 			[Talents.T_BODY_REVERSION] = 2,
 		},
 		talents_types_mastery = {
-			["chronomancy/timeline-threading"] = 0.2,
+			["chronomancy/fate-threading"] = 0.2,
 			["chronomancy/timetravel"] = 0.2,
-			["chronomancy/paradox"] = 0.2,
 			["spell/temporal"] = 0.2,
 		},
 	},
diff --git a/game/modules/tome/data/gfx/particles/bolt_paradox.lua b/game/modules/tome/data/gfx/particles/bolt_paradox.lua
new file mode 100644
index 0000000000000000000000000000000000000000..ff1885d3623c24934478c30a1687341196cd5bb3
--- /dev/null
+++ b/game/modules/tome/data/gfx/particles/bolt_paradox.lua
@@ -0,0 +1,51 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 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 { generator = function()
+	local radius = 0
+	local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2
+	local ad = rng.float(0, 360)
+	local a = math.rad(ad)
+	local r = rng.float(0, sradius / 6)
+	local x = r * math.cos(a)
+	local y = r * math.sin(a)
+	local bx = math.floor(x / engine.Map.tile_w)
+	local by = math.floor(y / engine.Map.tile_h)
+	local static = rng.percent(40)
+
+	return {
+		trail = 1,
+		life = 6,
+		size = 3, sizev = 0, sizea = 0,
+
+		x = x, xv = 0, xa = 0,
+		y = y, yv = 0, ya = 0,
+		dir = a, dirv = 0, dira = 0,
+		vel = sradius / 2 / 6, velv = 0, vela = 0,
+
+		r = rng.range(250, 255)/255,  rv = 0, ra = 0,
+		g = rng.range(120, 128)/255,  gv = 0, ga = 0,
+		b = rng.range(114, 147)/255,      bv = 0, ba = 0,
+		a = rng.range(80, 220)/255,   av = 0, aa = 0,
+	}
+end, },
+function(self)
+	self.ps:emit(30)
+end,
+30*6
\ No newline at end of file
diff --git a/game/modules/tome/data/gfx/particles/temporal_teleport.lua b/game/modules/tome/data/gfx/particles/temporal_teleport.lua
index 5de134fdd73c3f63a8555107f29fe0cfa5c8fb44..65395be791aa029da9722f883c091ed388431274 100644
--- a/game/modules/tome/data/gfx/particles/temporal_teleport.lua
+++ b/game/modules/tome/data/gfx/particles/temporal_teleport.lua
@@ -27,9 +27,9 @@ return {
 	life = { 20, 30 },
 	size = { 3, 7 }, sizev = {0, 0}, sizea = {0, 0},
 
-	r = {70, 250}, rv = {0, 10}, ra = {0, 0},
-	g = {130, 250}, gv = {0, 0}, ga = {0, 0},
-	b = {180, 0}, bv = {0, 10}, ba = {0, 0},
+	r = {0, 176}, rv = {0, 10}, ra = {0, 0},
+	g = {196, 255}, gv = {0, 0}, ga = {0, 0},
+	b = {222, 255}, bv = {0, 10}, ba = {0, 0},
 	a = {25, 255}, av = {0, 0}, aa = {0, 0},
 
 }, function(self)
diff --git a/game/modules/tome/data/gfx/particles/trail_paradox.lua b/game/modules/tome/data/gfx/particles/trail_paradox.lua
new file mode 100644
index 0000000000000000000000000000000000000000..c443c258465b5df2368d18b14fd7a96a0834d4a9
--- /dev/null
+++ b/game/modules/tome/data/gfx/particles/trail_paradox.lua
@@ -0,0 +1,56 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 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
+
+local nb = 0
+
+return { generator = function()
+	local radius = 0
+	local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2
+	local ad = rng.float(0, 360)
+	local a = math.rad(ad)
+	local r = rng.float(0.1, sradius / 2)
+	local x = r * math.cos(a)
+	local y = r * math.sin(a)
+	local bx = math.floor(x / engine.Map.tile_w)
+	local by = math.floor(y / engine.Map.tile_h)
+	local static = rng.percent(40)
+
+	return {
+		trail = 1,
+		life = 3 + 9 * (sradius - r) / sradius,
+		size = 3, sizev = 0, sizea = 0,
+
+		x = x, xv = 0, xa = 0,
+		y = y, yv = 0, ya = 0,
+		dir = 0, dirv = 0, dira = 0,
+		vel = 0, velv = 0, vela = 0,
+
+		r = rng.range(250, 255)/255,  rv = 0, ra = 0,
+		g = rng.range(120, 128)/255,  gv = 0.005, ga = 0.0005,
+		b = rng.range(114, 147)/255,      bv = 0, ba = 0,
+		a = rng.range(80, 220)/255,   av = static and -0.034 or 0, aa = 0.005,
+	}
+end, },
+function(self)
+	if nb < 1 then
+		self.ps:emit(30)
+	end
+	nb = nb + 1
+end,
+30
diff --git a/game/modules/tome/data/gfx/particles_images/spatial_fragment.png b/game/modules/tome/data/gfx/particles_images/spatial_fragment.png
new file mode 100644
index 0000000000000000000000000000000000000000..a666db2d2dd99c08b079780a253eb9be573cd9a8
Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/spatial_fragment.png differ
diff --git a/game/modules/tome/data/gfx/particles_images/temporal_bolt.png b/game/modules/tome/data/gfx/particles_images/temporal_bolt.png
new file mode 100644
index 0000000000000000000000000000000000000000..0ffab2efe3eab1d2ee2ebe373a28a36ec5a54d66
Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/temporal_bolt.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_01.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_01.png
new file mode 100644
index 0000000000000000000000000000000000000000..f7faecde70ab285d38b2767d9f35642dff19582a
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_01.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_02.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_02.png
new file mode 100644
index 0000000000000000000000000000000000000000..a8407740aef3247a22dd19681f7798cd0dc78551
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_02.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_03.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_03.png
new file mode 100644
index 0000000000000000000000000000000000000000..37d51af69fb3b9b8270090de89481dcc0658a30c
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_03.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_04.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_04.png
new file mode 100644
index 0000000000000000000000000000000000000000..34effaa41d50f87ec8e879e5dad28f19b8691a9a
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_04.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_01.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_01.png
new file mode 100644
index 0000000000000000000000000000000000000000..cbf85a7f483427aaf0faba4c89f70acae01f845a
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_01.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_02.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_02.png
new file mode 100644
index 0000000000000000000000000000000000000000..6d087dc041682fbed1bc6407b97232707f7da60f
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_02.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_03.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_03.png
new file mode 100644
index 0000000000000000000000000000000000000000..9ec3e0c40fa9c7c255c4a537865998b5976aeb71
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_03.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_04.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_04.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d9a404462c3010db185d61c206ac82924616f34
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_04.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_01.png b/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_01.png
deleted file mode 100644
index c6964845c5d5e5cee71c8ce6c7bf5e935b478ce3..0000000000000000000000000000000000000000
Binary files a/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_01.png and /dev/null differ
diff --git a/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_02.png b/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_02.png
deleted file mode 100644
index 88f787802024f201258a852ea35241e2d2c338b8..0000000000000000000000000000000000000000
Binary files a/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_02.png and /dev/null differ
diff --git a/game/modules/tome/data/gfx/talents/banish.png b/game/modules/tome/data/gfx/talents/banish.png
index 8dcf8a09e7ecc1af4a57fdc5ece5e499dbea88c1..4be985d8817006a5ea8d64776125fde7e5915eae 100644
Binary files a/game/modules/tome/data/gfx/talents/banish.png and b/game/modules/tome/data/gfx/talents/banish.png differ
diff --git a/game/modules/tome/data/gfx/talents/braid_lifelines.png b/game/modules/tome/data/gfx/talents/braid_lifelines.png
new file mode 100644
index 0000000000000000000000000000000000000000..f486ca16e3e626d84e44e72a91418e20e1c496e6
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/braid_lifelines.png differ
diff --git a/game/modules/tome/data/gfx/talents/carbon_spikes.png b/game/modules/tome/data/gfx/talents/carbon_spikes.png
deleted file mode 100644
index 4684f1c67b77be09d8954151faca0b02894ed3fd..0000000000000000000000000000000000000000
Binary files a/game/modules/tome/data/gfx/talents/carbon_spikes.png and /dev/null differ
diff --git a/game/modules/tome/data/gfx/talents/cease_to_exist.png b/game/modules/tome/data/gfx/talents/cease_to_exist.png
index d06442d3522a6067e20689f1efa2fbae86f06834..1b1fab471aaafecaf1e1c8117ddcaae88b13046f 100644
Binary files a/game/modules/tome/data/gfx/talents/cease_to_exist.png and b/game/modules/tome/data/gfx/talents/cease_to_exist.png differ
diff --git a/game/modules/tome/data/gfx/talents/damage_smearing.png b/game/modules/tome/data/gfx/talents/damage_smearing.png
index 0795f2634ad83c14d74ca0241718d03b6f07424f..2824e199284aca281eeeb3f1a8ab0a097243dc4c 100644
Binary files a/game/modules/tome/data/gfx/talents/damage_smearing.png and b/game/modules/tome/data/gfx/talents/damage_smearing.png differ
diff --git a/game/modules/tome/data/gfx/talents/dimensional_anchor.png b/game/modules/tome/data/gfx/talents/dimensional_anchor.png
index 13631e942c011c64603118117696e48d42b86001..b83df653229172b6faf094aa019d953bf785e9df 100644
Binary files a/game/modules/tome/data/gfx/talents/dimensional_anchor.png and b/game/modules/tome/data/gfx/talents/dimensional_anchor.png differ
diff --git a/game/modules/tome/data/gfx/talents/dimensional_shift.png b/game/modules/tome/data/gfx/talents/dimensional_shift.png
new file mode 100644
index 0000000000000000000000000000000000000000..a3a3f2e3c7c839615c3f9f7f1d0b0dfae7ba0972
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/dimensional_shift.png differ
diff --git a/game/modules/tome/data/gfx/talents/dimensional_step.png b/game/modules/tome/data/gfx/talents/dimensional_step.png
index 1a5ec6cfcaa24e3ef8d423a4e8e6213091ec2a96..203657d98d94d531379bd4934b481341099833db 100644
Binary files a/game/modules/tome/data/gfx/talents/dimensional_step.png and b/game/modules/tome/data/gfx/talents/dimensional_step.png differ
diff --git a/game/modules/tome/data/gfx/talents/discontinuity.png b/game/modules/tome/data/gfx/talents/discontinuity.png
new file mode 100644
index 0000000000000000000000000000000000000000..9fb4e35106a124e7b35d40018dd7bef4d8d37e0e
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/discontinuity.png differ
diff --git a/game/modules/tome/data/gfx/talents/echoes_from_the_past.png b/game/modules/tome/data/gfx/talents/echoes_from_the_past.png
index 9b013bf3c8fc01fd3fd40f6bcb2d1e56fd2273ee..1370f0481a851736ab4e959263113504aee5ca0d 100644
Binary files a/game/modules/tome/data/gfx/talents/echoes_from_the_past.png and b/game/modules/tome/data/gfx/talents/echoes_from_the_past.png differ
diff --git a/game/modules/tome/data/gfx/talents/empower.png b/game/modules/tome/data/gfx/talents/empower.png
new file mode 100644
index 0000000000000000000000000000000000000000..fa2f4c042119607d0c128f4578a545407a6fd7e0
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/empower.png differ
diff --git a/game/modules/tome/data/gfx/talents/energy_absorption.png b/game/modules/tome/data/gfx/talents/energy_absorption.png
index e196a0475a05d6efe9bf247891cab2e862e7f7cd..aaea4e17c21152f433d3f1bffaeedbb38883e14b 100644
Binary files a/game/modules/tome/data/gfx/talents/energy_absorption.png and b/game/modules/tome/data/gfx/talents/energy_absorption.png differ
diff --git a/game/modules/tome/data/gfx/talents/energy_decomposition.png b/game/modules/tome/data/gfx/talents/energy_decomposition.png
index ac7fd54da287366c2a144d54059a701e643dab03..b480066e721148758d7c336b47f363da2662ed94 100644
Binary files a/game/modules/tome/data/gfx/talents/energy_decomposition.png and b/game/modules/tome/data/gfx/talents/energy_decomposition.png differ
diff --git a/game/modules/tome/data/gfx/talents/entropy.png b/game/modules/tome/data/gfx/talents/entropy.png
new file mode 100644
index 0000000000000000000000000000000000000000..af9afe0b78ff555944d648432ed5ab1a3aa50c1d
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/entropy.png differ
diff --git a/game/modules/tome/data/gfx/talents/extension.png b/game/modules/tome/data/gfx/talents/extension.png
new file mode 100644
index 0000000000000000000000000000000000000000..050833e330fb249bebe115b491733f6a21e882b0
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/extension.png differ
diff --git a/game/modules/tome/data/gfx/talents/fateweaver.png b/game/modules/tome/data/gfx/talents/fateweaver.png
index 3a0ae723fc5b3a8af99faf4c50b4cb6c4d1c471a..c1cfbed29aecde24d08b39080affdf321e8a7151 100644
Binary files a/game/modules/tome/data/gfx/talents/fateweaver.png and b/game/modules/tome/data/gfx/talents/fateweaver.png differ
diff --git a/game/modules/tome/data/gfx/talents/fractured_space.png b/game/modules/tome/data/gfx/talents/fractured_space.png
new file mode 100644
index 0000000000000000000000000000000000000000..7b5338e75a13c3d1109b716f62c2035d221781c7
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/fractured_space.png differ
diff --git a/game/modules/tome/data/gfx/talents/gravity_locus.png b/game/modules/tome/data/gfx/talents/gravity_locus.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ed4b10b269de270ef08a98e8f763a868ec5e4a1
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/gravity_locus.png differ
diff --git a/game/modules/tome/data/gfx/talents/gravity_spike.png b/game/modules/tome/data/gfx/talents/gravity_spike.png
index a986352e653a5b88e1058e71e471bde61867d3d4..8a6e7b09c4f6775e3db14ebe6477b48eef8bd418 100644
Binary files a/game/modules/tome/data/gfx/talents/gravity_spike.png and b/game/modules/tome/data/gfx/talents/gravity_spike.png differ
diff --git a/game/modules/tome/data/gfx/talents/gravity_well.png b/game/modules/tome/data/gfx/talents/gravity_well.png
index b374335def95a3798280179a3c9a5770a829f215..4dd85288e0ef465cb65692269174294077a3f634 100644
Binary files a/game/modules/tome/data/gfx/talents/gravity_well.png and b/game/modules/tome/data/gfx/talents/gravity_well.png differ
diff --git a/game/modules/tome/data/gfx/talents/matrix.png b/game/modules/tome/data/gfx/talents/matrix.png
new file mode 100644
index 0000000000000000000000000000000000000000..91200083eace7401c16943a80b30b9afaac7efaf
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/matrix.png differ
diff --git a/game/modules/tome/data/gfx/talents/paradox_mastery.png b/game/modules/tome/data/gfx/talents/paradox_mastery.png
deleted file mode 100644
index b2da719851cfd4f744979047dd045ede6b8d2748..0000000000000000000000000000000000000000
Binary files a/game/modules/tome/data/gfx/talents/paradox_mastery.png and /dev/null differ
diff --git a/game/modules/tome/data/gfx/talents/phase_pulse.png b/game/modules/tome/data/gfx/talents/phase_pulse.png
new file mode 100644
index 0000000000000000000000000000000000000000..0085e5161bdfadff2a626634e166e143b39aec69
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/phase_pulse.png differ
diff --git a/game/modules/tome/data/gfx/talents/phase_shift.png b/game/modules/tome/data/gfx/talents/phase_shift.png
index 8e5480bdf4cc6faffcc6ba34dadbeaec3efccf70..2cc474bb2d431d825ea80898644f06efc95d11d0 100644
Binary files a/game/modules/tome/data/gfx/talents/phase_shift.png and b/game/modules/tome/data/gfx/talents/phase_shift.png differ
diff --git a/game/modules/tome/data/gfx/talents/polarity_shift.png b/game/modules/tome/data/gfx/talents/polarity_shift.png
new file mode 100644
index 0000000000000000000000000000000000000000..51a9c0583c651c8b97bbc9b9175c3c67ae32dcc7
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/polarity_shift.png differ
diff --git a/game/modules/tome/data/gfx/talents/quicken.png b/game/modules/tome/data/gfx/talents/quicken.png
new file mode 100644
index 0000000000000000000000000000000000000000..6ac9df4f244bee41c6a7785f21734b1f648084f3
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/quicken.png differ
diff --git a/game/modules/tome/data/gfx/talents/redux.png b/game/modules/tome/data/gfx/talents/redux.png
index 34a7bb4dbcab6b6595f3adf70dafff9b6294e9cc..7ae45948d9b28d77854f9e485bf68e56b17f120f 100644
Binary files a/game/modules/tome/data/gfx/talents/redux.png and b/game/modules/tome/data/gfx/talents/redux.png differ
diff --git a/game/modules/tome/data/gfx/talents/repulsion_blast.png b/game/modules/tome/data/gfx/talents/repulsion_blast.png
index 4ed66410bf53ce914cb41f85a661132b459085a1..5556b8f7fbeaa5e3c44dda9e95bac04897aa22f9 100644
Binary files a/game/modules/tome/data/gfx/talents/repulsion_blast.png and b/game/modules/tome/data/gfx/talents/repulsion_blast.png differ
diff --git a/game/modules/tome/data/gfx/talents/rethread.png b/game/modules/tome/data/gfx/talents/rethread.png
index 3f53a62d6595abf1755a058f030806d61a8dafea..41fdca00a7bd57c5503239ff36ed4ca17a3a6142 100644
Binary files a/game/modules/tome/data/gfx/talents/rethread.png and b/game/modules/tome/data/gfx/talents/rethread.png differ
diff --git a/game/modules/tome/data/gfx/talents/seal_fate.png b/game/modules/tome/data/gfx/talents/seal_fate.png
index 629d3fdcddce8916d5b12e32405c9b799cccf6a5..3258ceabba00e268e241d538d6401b89051b000e 100644
Binary files a/game/modules/tome/data/gfx/talents/seal_fate.png and b/game/modules/tome/data/gfx/talents/seal_fate.png differ
diff --git a/game/modules/tome/data/gfx/talents/spatial_fragments.png b/game/modules/tome/data/gfx/talents/spatial_fragments.png
new file mode 100644
index 0000000000000000000000000000000000000000..b44f8480cee0d961924105c21a8bb204e2fe519e
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/spatial_fragments.png differ
diff --git a/game/modules/tome/data/gfx/talents/spatial_tether.png b/game/modules/tome/data/gfx/talents/spatial_tether.png
new file mode 100644
index 0000000000000000000000000000000000000000..a7144cefc368706fb9e0addedaa8d4f94bff6f99
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/spatial_tether.png differ
diff --git a/game/modules/tome/data/gfx/talents/sphere_of_destruction.png b/game/modules/tome/data/gfx/talents/sphere_of_destruction.png
new file mode 100644
index 0000000000000000000000000000000000000000..237f62b5b78300ee98a39ca59249376a543bde9e
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/sphere_of_destruction.png differ
diff --git a/game/modules/tome/data/gfx/talents/spin_fate.png b/game/modules/tome/data/gfx/talents/spin_fate.png
index a95fe19d9122b4933f281802dbdd37fec873f1b9..9fb6a19b51833415c41aebf3e5c8d2a2031c309d 100644
Binary files a/game/modules/tome/data/gfx/talents/spin_fate.png and b/game/modules/tome/data/gfx/talents/spin_fate.png differ
diff --git a/game/modules/tome/data/gfx/talents/spint_fate.png b/game/modules/tome/data/gfx/talents/spint_fate.png
deleted file mode 100644
index 25f7666e887a6e27d523eb2e116324e3f1f9eee1..0000000000000000000000000000000000000000
Binary files a/game/modules/tome/data/gfx/talents/spint_fate.png and /dev/null differ
diff --git a/game/modules/tome/data/gfx/talents/swap.png b/game/modules/tome/data/gfx/talents/swap.png
index afb0b6a4a934df7c7d81dde32b03782236e5d54e..9f90da389b4a557bd88d4808cad0630fbedd5967 100644
Binary files a/game/modules/tome/data/gfx/talents/swap.png and b/game/modules/tome/data/gfx/talents/swap.png differ
diff --git a/game/modules/tome/data/gfx/talents/temporal_bolt.png b/game/modules/tome/data/gfx/talents/temporal_bolt.png
new file mode 100644
index 0000000000000000000000000000000000000000..f4bb163da34d6b9213ddd8959484c53f2ab5266b
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/temporal_bolt.png differ
diff --git a/game/modules/tome/data/gfx/talents/temporal_clone.png b/game/modules/tome/data/gfx/talents/temporal_clone.png
index 69f316bbdd60df547f97602658c861095d4477b6..27871178fac6951c2276ccf10a5ef098d4ac49df 100644
Binary files a/game/modules/tome/data/gfx/talents/temporal_clone.png and b/game/modules/tome/data/gfx/talents/temporal_clone.png differ
diff --git a/game/modules/tome/data/gfx/talents/temporal_fugue.png b/game/modules/tome/data/gfx/talents/temporal_fugue.png
index c3a614cc0436d9a57ce1ed054ebc48634109c722..2f0a4d4680122ab2dd6267cc5e1f95fd82b39d80 100644
Binary files a/game/modules/tome/data/gfx/talents/temporal_fugue.png and b/game/modules/tome/data/gfx/talents/temporal_fugue.png differ
diff --git a/game/modules/tome/data/gfx/talents/temporal_reprieve.png b/game/modules/tome/data/gfx/talents/temporal_reprieve.png
index 95fc78fc24d2b9b472ac985d96034902916914b5..b31b9f88e35c13e70bade128ecaa75aa452405a4 100644
Binary files a/game/modules/tome/data/gfx/talents/temporal_reprieve.png and b/game/modules/tome/data/gfx/talents/temporal_reprieve.png differ
diff --git a/game/modules/tome/data/gfx/talents/temporal_wake.png b/game/modules/tome/data/gfx/talents/temporal_wake.png
index bbfed84cd90360e718a3548016311d26914b29ec..278bb53e28d4e665ef6893fd975fa4f83c37a9e1 100644
Binary files a/game/modules/tome/data/gfx/talents/temporal_wake.png and b/game/modules/tome/data/gfx/talents/temporal_wake.png differ
diff --git a/game/modules/tome/data/gfx/talents/time_skip.png b/game/modules/tome/data/gfx/talents/time_skip.png
index 5ead5acaaf2bc0eca84ee38592af06766a072b8f..5b59b98746d469aa7dcd7d667ea8304ab4b92ac7 100644
Binary files a/game/modules/tome/data/gfx/talents/time_skip.png and b/game/modules/tome/data/gfx/talents/time_skip.png differ
diff --git a/game/modules/tome/data/gfx/talents/warp.png b/game/modules/tome/data/gfx/talents/warp.png
new file mode 100644
index 0000000000000000000000000000000000000000..470815ea4e452f4ab4197c0d996929ce2fd7af9c
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/warp.png differ
diff --git a/game/modules/tome/data/gfx/talents/warp_mastery.png b/game/modules/tome/data/gfx/talents/warp_mastery.png
new file mode 100644
index 0000000000000000000000000000000000000000..b9a1d90c628701939b6e1930b86e38c86cb37fc7
Binary files /dev/null and b/game/modules/tome/data/gfx/talents/warp_mastery.png differ
diff --git a/game/modules/tome/data/gfx/talents/warp_mine.png b/game/modules/tome/data/gfx/talents/warp_mine.png
deleted file mode 100644
index 43b830b5ceee0fd929e580c2e8e8cc4de8cfd27f..0000000000000000000000000000000000000000
Binary files a/game/modules/tome/data/gfx/talents/warp_mine.png and /dev/null differ
diff --git a/game/modules/tome/data/gfx/talents/warp_mine_away.png b/game/modules/tome/data/gfx/talents/warp_mine_away.png
index 5268bb01dac6cecce5348f7432881c4c99029e5b..f08f5ff1961ba9331a4847ea400bd1abef670676 100644
Binary files a/game/modules/tome/data/gfx/talents/warp_mine_away.png and b/game/modules/tome/data/gfx/talents/warp_mine_away.png differ
diff --git a/game/modules/tome/data/gfx/talents/warp_mine_toward.png b/game/modules/tome/data/gfx/talents/warp_mine_toward.png
index e8ac5358c4b2ecd9e8ca9c5a7ef768d1f3d75c67..5c509a8f16c21423284337f167508b1b2eb16d1e 100644
Binary files a/game/modules/tome/data/gfx/talents/warp_mine_toward.png and b/game/modules/tome/data/gfx/talents/warp_mine_toward.png differ
diff --git a/game/modules/tome/data/gfx/talents/warp_mines.png b/game/modules/tome/data/gfx/talents/warp_mines.png
index 2c5d2d55c7c492f051132af2503cd1457ac848eb..31d9e2210e3a8237a70b26417676a82532f643a6 100644
Binary files a/game/modules/tome/data/gfx/talents/warp_mines.png and b/game/modules/tome/data/gfx/talents/warp_mines.png differ
diff --git a/game/modules/tome/data/gfx/talents/webs_of_fate.png b/game/modules/tome/data/gfx/talents/webs_of_fate.png
index aa8e3b51b14f93486ce54eda5d39bbd305b43045..71803764fa6797423c5407f345ebfc34a873f96f 100644
Binary files a/game/modules/tome/data/gfx/talents/webs_of_fate.png and b/game/modules/tome/data/gfx/talents/webs_of_fate.png differ
diff --git a/game/modules/tome/data/gfx/talents/wormhole.png b/game/modules/tome/data/gfx/talents/wormhole.png
index 1f85b3fcaee1b4ee5778908cc7fcece7f986d607..f34fe5e73422a3183267fb223ba0587eb145e655 100644
Binary files a/game/modules/tome/data/gfx/talents/wormhole.png and b/game/modules/tome/data/gfx/talents/wormhole.png differ
diff --git a/game/modules/tome/data/talents/chronomancy/age-manipulation.lua b/game/modules/tome/data/talents/chronomancy/age-manipulation.lua
index cb7fe1773cbc2b3a1d7124033baf9b3fa6934433..93bca691760f307855c65b78a5ff1cf48528ce53 100644
--- a/game/modules/tome/data/talents/chronomancy/age-manipulation.lua
+++ b/game/modules/tome/data/talents/chronomancy/age-manipulation.lua
@@ -29,12 +29,13 @@ newTalent{
 	reflectable = true,
 	requires_target = true,
 	proj_speed = 5,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 150, getParadoxSpellpower(self)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 150, getParadoxSpellpower(self, t)) end,
 	getDamageStat = function(self, t) return 2 + math.ceil(t.getDamage(self, t) / 15) end,
 	action = function(self, t)
 		local tg = {type="bolt", range=self:getTalentRange(t), talent=t, display={particle="temporal_bolt"}}
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
+		
 		self:projectile(tg, x, y, DamageType.CLOCK, self:spellCrit(t.getDamage(self, t)))
 		game:playSoundNear(self, "talents/spell_generic2")
 
@@ -64,7 +65,7 @@ newTalent{
 }
 
 newTalent{
-	name = "Temporal Fugue",
+	name = "Temporal Fugue Old",
 	type = {"chronomancy/age-manipulation", 2},
 	require = chrono_req2,
 	points = 5,
@@ -114,7 +115,7 @@ newTalent{
 	target = function(self, t)
 		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false}
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 8, 135, getParadoxSpellpower(self)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 8, 135, getParadoxSpellpower(self, t)) end,
 	getDuration = function(self, t) return 5 + math.ceil(self:getTalentLevel(t)) end,
 	direct_hit = true,
 	requires_target = true,
@@ -163,7 +164,7 @@ newTalent{
 		return nb
 	end },
 	is_heal = true,
-	getHeal = function(self, t) return self:combatTalentSpellDamage(t, 40, 440, getParadoxSpellpower(self)) end,
+	getHeal = function(self, t) return self:combatTalentSpellDamage(t, 40, 440, getParadoxSpellpower(self, t)) end,
 	getRemoveCount = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
 	action = function(self, t)
 		self:attr("allow_on_heal", 1)
diff --git a/game/modules/tome/data/talents/chronomancy/anomalies.lua b/game/modules/tome/data/talents/chronomancy/anomalies.lua
index 7636f689e2611ef651e379ad94c91a8f5ea7f1aa..270b3e9c16f89bf1d25c142c3a050ae630832dcc 100644
--- a/game/modules/tome/data/talents/chronomancy/anomalies.lua
+++ b/game/modules/tome/data/talents/chronomancy/anomalies.lua
@@ -25,37 +25,37 @@ local Object = require "engine.Object"
 local Trap = require "mod.class.Trap"
 
 -- All anomaly damage functions come from here with a bit of randomness thrown in to mix it up
-getAnomalyDamage = function(self)
-	local dam = self:combatScale(getParadoxSpellpower(self), 20, 10, 220, 100, 0.75)
+getAnomalyDamage = function(self, t)
+	local dam = self:combatScale(getParadoxSpellpower(self, t), 20, 10, 220, 100, 0.75)
 	return rng.avg(dam / 3, dam, 3)
 end
 
-getAnomalyDamageAoE = function(self)
-	local dam = self:combatScale(getParadoxSpellpower(self), 10, 10, 50, 100, 0.75)
+getAnomalyDamageAoE = function(self, t)
+	local dam = self:combatScale(getParadoxSpellpower(self, t), 10, 10, 50, 100, 0.75)
 	return rng.avg(dam / 3, dam, 3)
 end
 
 -- Here we have Effect Power and Anomaly duration, similar to damage but less random
-getAnomalyEffectPower = function(self)
-	local dam = self:combatScale(getParadoxSpellpower(self), 10, 10, 50, 100, 0.75)
+getAnomalyEffectPower = function(self, t)
+	local dam = self:combatScale(getParadoxSpellpower(self, t), 10, 10, 50, 100, 0.75)
 	return math.ceil(rng.avg(dam / 2, dam, 3))
 end
 
-getAnomalyDuration = function(self)
-	local dam = self:combatScale(getParadoxSpellpower(self), 4, 10, 12, 100, 0.75)
+getAnomalyDuration = function(self, t)
+	local dam = self:combatScale(getParadoxSpellpower(self, t), 4, 10, 12, 100, 0.75)
 	return math.ceil(rng.avg(dam / 2, dam, 3))
 end
 
 -- Determines the anomaly range based on current spellpower
 -- Generally we just use range = 10 for anomalies, this is for stuff with much longer range (such as teleports)
-getAnomalyRange = function(self)
-	local range = math.floor(self:combatLimit(getParadoxSpellpower(self), 80, 20, 20, 40, 100))
+getAnomalyRange = function(self, t)
+	local range = math.floor(self:combatLimit(getParadoxSpellpower(self, t), 80, 20, 20, 40, 100))
 	return range
 end
 
 -- Determines the anomaly radius based on current spellpower
-getAnomalyRadius = function(self)
-	local radius = math.floor(self:combatLimit(getParadoxSpellpower(self),8, 2, 20, 6, 100))
+getAnomalyRadius = function(self, t)
+	local radius = math.floor(self:combatLimit(getParadoxSpellpower(self, t), 6, 2, 20, 4, 100))
 	return radius
 end
 
@@ -102,7 +102,7 @@ end
 -- For this to work all anomalies have to have no_energy set to true in the talent table, otherwise it will use two turns
 checkAnomalyTargeting = function(self, t, tg)
 	local x, y = self.x, self.y
-	if self:knowTalent(self.T_BIAS_WEAVE) and rng.percent(self:callTalent(self.T_BIAS_WEAVE, "getTargetChance")) then
+	if self:knowTalent(self.T_BIAS_WEAVE) and rng.percent(self:callTalent(self.T_BIAS_WEAVE, "getTargetChance")) and not self:attr("anomaly_forced_target") then
 		if self == game.player then
 			game.bignews:saySimple(180, "#STEEL_BLUE#Targeting %s", t.name)
 		end
@@ -121,12 +121,6 @@ end
 -- Check for effects when hit by an anomaly
 -- This is called before immunity is checked
 checkAnomalyTriggers = function(self, target)
-	if target:knowTalent(target.T_WEBS_OF_FATE) then
-		target:forceUseTalent(target.T_WEBS_OF_FATE, {ignore_cd=true, ignore_energy=true, ignore_ressources=true})
-	end
-	if target:knowTalent(target.T_SEAL_FATE) then
-		target:forceUseTalent(target.T_SEAL_FATE, {ignore_cd=true, ignore_energy=true, ignore_ressources=true})
-	end
 	if target:hasEffect(target.EFF_TRIM_THREADS) then
 		local eff = target:hasEffect(target.EFF_TRIM_THREADS)
 		eff.src:callTalent(eff.src.T_TRIM_THREADS, "doAnomaly", target, eff)
@@ -142,9 +136,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -186,9 +180,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
-	range = function(self, t) return getAnomalyRange(self) end,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	cooldown =0,
+	range = function(self, t) return getAnomalyRange(self, t) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -231,9 +225,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -244,7 +238,7 @@ newTalent{
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
 		local x, y = checkAnomalyTargeting(self, t, tg)
-		local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRadius(t), true)
+		local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRange(t), true)
 
 		-- Randomly take targets
 		if #tgts <= 0 then return nil end
@@ -286,9 +280,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -309,7 +303,7 @@ newTalent{
 		checkAnomalyTriggers(self, self)
 		
 		if a:canBe("anomaly") then
-			self:setEffect(self.EFF_DISPLACEMENT_SHIELD, getAnomalyDuration(self)*2, {power=getAnomalyDamage(self)*4, target=a, chance=50})
+			self:setEffect(self.EFF_DISPLACEMENT_SHIELD, getAnomalyDuration(self, t)*2, {power=getAnomalyDamage(self, t)*4, target=a, chance=50})
 		end
 		
 		game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport")
@@ -319,7 +313,7 @@ newTalent{
 	end,
 	info = function(self, t)
 		return ([[50%% chance that damage the caster takes will be warped to a set target.
-		Once the maximum damage (%d) is absorbed, the time runs out, or the target dies, the shield will crumble.]]):format(getAnomalyDamage(self)*2)
+		Once the maximum damage (%d) is absorbed, the time runs out, or the target dies, the shield will crumble.]]):format(getAnomalyDamage(self, t)*2)
 	end,
 }
 
@@ -331,7 +325,7 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
 	direct_hit = true,
 	no_energy = true, 
@@ -356,7 +350,7 @@ newTalent{
 			image = "terrain/wormhole.png",
 			display = '&', color_r=255, color_g=255, color_b=255, back_color=colors.STEEL_BLUE,
 			message = "@Target@ moves onto the wormhole.",
-			temporary = getAnomalyDuration(self),
+			temporary = getAnomalyDuration(self, t),
 			x = start_x, y = start_y,
 			canAct = false,
 			energy = {value=0},
@@ -364,12 +358,12 @@ newTalent{
 			summoned_by = self, -- "summoner" is immune to it's own traps
 			triggered = function(self, x, y, who)
 				if who == self.summoned_by or who:canBe("teleport") then
-					-- since we're using a precise teleport we'll look for a free grid first
-					local tx, ty = util.findFreeGrid(self.dest.x, self.dest.y, 5, true, {[engine.Map.ACTOR]=true})
-					if tx and ty then
-						if not who:teleportRandom(tx, ty, 0) then
-							game.logSeen(who, "%s tries to enter the wormhole but a violent force pushes it back.", who.name:capitalize())
-						end
+					game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
+					if not who:teleportRandom(self.dest.x, self.dest.y, 3, 1) then
+						game.logSeen(who, "%s tries to enter the wormhole but a violent force pushes it back.", who.name:capitalize())
+					elseif who ~= self.summoned_by then
+						who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self.dest_power})
+						game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
 					end
 				else
 					game.logSeen(who, "%s ignores the wormhole.", who.name:capitalize())
@@ -423,9 +417,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -444,7 +438,7 @@ newTalent{
 			table.remove(tgts, id)
 			checkAnomalyTriggers(self, a)
 
-			a:setEffect(a.EFF_PROBABILITY_TRAVEL, getAnomalyDuration(self)*2, {power=getAnomalyDuration(self)})
+			a:setEffect(a.EFF_PROBABILITY_TRAVEL, getAnomalyDuration(self, t)*2, {power=getAnomalyDuration(self, t)})
 			game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport")
 
 			game:playSoundNear(self, "talents/spell_generic")
@@ -454,7 +448,7 @@ newTalent{
 	end,
 	info = function(self, t)
 		return ([[Allows up to five targets in a radius of %d to travel up to %d tiles through walls.]]):
-		format(getAnomalyDuration(self)*2, getAnomalyDuration(self))
+		format(getAnomalyDuration(self, t)*2, getAnomalyDuration(self, t))
 	end,
 }
 
@@ -466,9 +460,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -487,7 +481,7 @@ newTalent{
 			table.remove(tgts, id)
 			checkAnomalyTriggers(self, a)
 
-			a:setEffect(a.EFF_BLINK, getAnomalyDuration(self)*2, {power=getAnomalyDuration(self)})
+			a:setEffect(a.EFF_BLINK, getAnomalyDuration(self, t)*2, {power=getAnomalyDuration(self, t)})
 			game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport")
 
 			game:playSoundNear(self, "talents/spell_generic")
@@ -497,7 +491,7 @@ newTalent{
 	end,
 	info = function(self, t)
 		return ([[Up to five targets in a radius of %d are teleporting %d tiles every turn.]]):
-		format(self:getTalentRadius(t), getAnomalyDuration(self))
+		format(self:getTalentRadius(t), getAnomalyDuration(self, t))
 	end,
 }
 
@@ -509,16 +503,16 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	requires_target = true,
 	no_energy = true, 
 	target = function(self, t)
 		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)}
 	end,
-	getSummonTime = function(self, t) return math.ceil(getAnomalyDuration(self)*2) end,
+	getSummonTime = function(self, t) return math.ceil(getAnomalyDuration(self, t)*2) end,
 	message = "Some innocent bystanders have been teleported into the fight.",
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
@@ -618,16 +612,16 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
 	target = function(self, t)
 		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)}
 	end,
-	getSlow = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self)) / 100) end,
+	getSlow = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self, t)) / 100) end,
 	message = "@Source@ creates a bubble of slow time.",
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
@@ -641,7 +635,7 @@ newTalent{
 			checkAnomalyTriggers(self, a)
 			
 			if a:canBe("anomaly") then
-				a:setEffect(a.EFF_SLOW, getAnomalyDuration(self), {power=t.getSlow(self, t), no_ct_effect=true})
+				a:setEffect(a.EFF_SLOW, getAnomalyDuration(self, t), {power=t.getSlow(self, t), no_ct_effect=true})
 			end
 			game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y})
 			game:playSoundNear(self, "talents/spell_generic")
@@ -662,16 +656,16 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
 	target = function(self, t)
 		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), nowarning=true}
 	end,
-	getHaste = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self)) / 100) end,
+	getHaste = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self, t)) / 100) end,
 	message = "@Source@ creates a bubble of fast time.",
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
@@ -684,7 +678,7 @@ newTalent{
 			table.remove(tgts, id)
 			checkAnomalyTriggers(self, a)
 
-			a:setEffect(a.EFF_SPEED, getAnomalyDuration(self), {power=t.getHaste(self, t)})
+			a:setEffect(a.EFF_SPEED, getAnomalyDuration(self, t), {power=t.getHaste(self, t)})
 			game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y})
 			game:playSoundNear(self, "talents/spell_generic")
 		end
@@ -705,9 +699,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -727,7 +721,7 @@ newTalent{
 			checkAnomalyTriggers(self, a)
 
 			if a:canBe("anomaly") and a:canBe("stun") then
-				a:setEffect(a.EFF_STUNNED, getAnomalyDuration(self)/2, {no_ct_effect=true, apply_power=getParadoxSpellpower(self)})
+				a:setEffect(a.EFF_STUNNED, getAnomalyDuration(self, t)/2, {no_ct_effect=true, apply_power=getParadoxSpellpower(self, t)})
 			end
 			game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y})
 			game:playSoundNear(self, "talents/spell_generic")
@@ -749,9 +743,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -771,7 +765,7 @@ newTalent{
 			checkAnomalyTriggers(self, a)
 
 			if a:canBe("anomaly") then
-				a:setEffect(a.EFF_TIME_PRISON, getAnomalyDuration(self), {no_ct_effect=true})
+				a:setEffect(a.EFF_TIME_PRISON, getAnomalyDuration(self, t), {no_ct_effect=true})
 			end
 			game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y})
 			game:playSoundNear(self, "talents/spell_generic")
@@ -793,9 +787,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -814,7 +808,7 @@ newTalent{
 			table.remove(tgts, id)
 			checkAnomalyTriggers(self, a)
 
-			a:setEffect(a.EFF_TIME_SHIELD, getAnomalyDuration(self), {power=getAnomalyDamage(self)*4, dot_dur=5, time_reducer=40})
+			a:setEffect(a.EFF_TIME_SHIELD, getAnomalyDuration(self, t), {power=getAnomalyDamage(self, t)*4, dot_dur=5, time_reducer=40})
 			game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y})
 			game:playSoundNear(self, "talents/spell_generic")
 		end
@@ -835,9 +829,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -856,7 +850,7 @@ newTalent{
 			table.remove(tgts, id)
 			checkAnomalyTriggers(self, a)
 
-			a:setEffect(a.EFF_INVIGORATE, getAnomalyDuration(self), {power=getAnomalyEffectPower(self)/10})
+			a:setEffect(a.EFF_INVIGORATE, getAnomalyDuration(self, t), {power=getAnomalyEffectPower(self, t)/10})
 			game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y})
 			game:playSoundNear(self, "talents/spell_generic")
 		end
@@ -877,9 +871,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -893,20 +887,22 @@ newTalent{
 		local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRadius(t))
 
 		-- Randomly take targets
-		if #tgts <= 0 then return end
+		if #tgts <= 0 then return true end
 		local a, id = rng.table(tgts)
 		table.remove(tgts, id)
 		checkAnomalyTriggers(self, a)
 		
 		local x, y = util.findFreeGrid(a.x, a.y, 3, true, {[Map.ACTOR]=true})
 		if not x then
-			return
+			return true
 		end
 		if a:attr("summon_time") then return end
-		local m = makeParadoxClone(self, a, getAnomalyDuration(self)*2)
+		local m = makeParadoxClone(self, a, getAnomalyDuration(self, t)*2)
 		m.ai_state = { talent_in=2, ally_compassion=10}
 		game.zone:addEntity(game.level, m, "actor", x, y)
 		game.level.map:particleEmitter(x, y, 1, "generic_teleport", {rm=60, rM=130, gm=20, gM=110, bm=90, bM=130, am=70, aM=180})
+		
+		return true
 	end,
 	info = function(self, t)
 		return ([[Clones a random creature within range.]]):format()
@@ -921,9 +917,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown = 0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -937,8 +933,8 @@ newTalent{
 		
 		-- Add a lasting map effect
 		game.level.map:addEffect(self,
-			x, y, getAnomalyDuration(self),
-			DamageType.TEMPORAL, getAnomalyDamageAoE(self),
+			x, y, getAnomalyDuration(self, t)/2,
+			DamageType.TEMPORAL, getAnomalyDamageAoE(self, t),
 			tg.radius,
 			5, nil,
 			engine.MapEffect.new{alpha=85, color_br=200, color_bg=200, color_bb=0, effect_shader="shader_images/paradox_effect.png"},
@@ -947,8 +943,10 @@ newTalent{
 		return true
 	end,
 	info = function(self, t)
-		return ([[Creates a temporal storm for %d turns that deals %d temporal damage each turn.]])
-		:format(getAnomalyDuration(self), damDesc(self, DamageType.TEMPORAL, getAnomalyDamageAoE(self)))
+		local duration = self:combatScale(getParadoxSpellpower(self, t), 4, 10, 12, 100, 0.75)/2
+		local damage = self:combatScale(getParadoxSpellpower(self, t), 10, 10, 50, 100, 0.75)
+		return ([[Creates a temporal storm for %d to %d turns that deals between %0.2f and %0.2f temporal damage each turn.]])
+		:format(duration/2, duration, damDesc(self, DamageType.TEMPORAL, damage/3),  damDesc(self, DamageType.TEMPORAL, damage))
 	end,
 }
 
@@ -961,9 +959,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -1005,9 +1003,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -1037,7 +1035,7 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
 	radius = 1,
 	direct_hit = true,
@@ -1070,7 +1068,7 @@ newTalent{
 					show_tooltip = true,
 					block_move = true,
 					block_sight = true,
-					temporary = getAnomalyDuration(self),
+					temporary = getAnomalyDuration(self, t),
 					x = x + i, y = y + j,
 					canAct = false,
 					act = function(self)
@@ -1113,9 +1111,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1146,7 +1144,7 @@ newTalent{
 						if not target.talents_cd[tid] and t.mode == "activated" and not t.innate then tids[#tids+1] = t end
 					end
 					for i = 1, rng.avg(3, 6, 3) do
-						local power = getAnomalyDuration(self)
+						local power = getAnomalyDuration(self, t)
 						local t = rng.tableRemove(tids)
 						if not t then break end
 						target.talents_cd[t.id] = rng.range(2, power)
@@ -1160,7 +1158,7 @@ newTalent{
 	end,
 	info = function(self, t)
 		return ([[Places between three and six talents of up to 5 targets in a radius %d ball on cooldown for up to %d turns.]]):
-		format(getAnomalyRadius(self), getAnomalyDuration(self))
+		format(getAnomalyRadius(self, t), getAnomalyDuration(self, t))
 	end,
 }
 
@@ -1172,9 +1170,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -1195,7 +1193,7 @@ newTalent{
 			checkAnomalyTriggers(self, a)
 			
 			if a:canBe("anomaly") and a:canBe("pin") then
-				a:setEffect(self.EFF_PINNED, getAnomalyDuration(self), {apply_power=getParadoxSpellpower(self)})
+				a:setEffect(self.EFF_PINNED, getAnomalyDuration(self, t), {apply_power=getParadoxSpellpower(self, t)})
 			end
 		end
 		
@@ -1216,9 +1214,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1231,8 +1229,8 @@ newTalent{
 		local x, y = checkAnomalyTargeting(self, t, tg)
 		
 		-- Don't bury the player
-		if self ~= game.player and not game.player:knowTalent(game.player.T_DIG_OBJECT) then
-			return
+		if not game.player:knowTalent(game.player.T_DIG_OBJECT) then
+			return true
 		end
 
 		self:doQuake(tg, x, y)
@@ -1240,50 +1238,7 @@ newTalent{
 	end,
 	info = function(self, t)
 		return ([[Causes an earthquake in a radius of %d.]]):
-		format(getAnomalyRadius(self))
-	end,
-}
-
-newTalent{
-	name = "Anomaly Calcify",
-	type = {"chronomancy/anomalies", 1},
-	anomaly_type = "physical",
-	type_no_req = true,
-	no_unlearn_last = true,
-	points = 1,
-	paradox = 0,
-	cooldown = 1,
-	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
-	direct_hit = true,
-	no_energy = true,
-	requires_target = true,
-	target = function(self, t)
-		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)}
-	end,
-	message = "@Source@ calcifies several targets.",
-	action = function(self, t)
-		local tg = self:getTalentTarget(t)
-		local x, y = checkAnomalyTargeting(self, t, tg)
-		local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRadius(t))
-
-		-- Randomly take targets
-		for i = 1, rng.avg(1, 5, 3) do
-			if #tgts <= 0 then break end
-			local a, id = rng.table(tgts)
-			table.remove(tgts, id)
-			checkAnomalyTriggers(self, a)
-			
-			if a:canBe("anomaly") and a:canBe("stun") and a:canBe("stone") and a:canBe("instakill")then
-				a:setEffect(a.EFF_STONED, getAnomalyDuration(self), {apply_power=getParadoxSpellpower(self)})
-				game.level.map:particleEmitter(a.x, a.y, 1, "archery")
-			end
-		end
-		return true
-	end,
-	info = function(self, t)
-		return ([[Turns up to 5 targets in a radius %d ball to stone for %d turns.]]):
-		format(getAnomalyRadius(self), getAnomalyDuration(self))
+		format(getAnomalyRadius(self, t))
 	end,
 }
 
@@ -1295,9 +1250,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1317,14 +1272,14 @@ newTalent{
 			table.remove(tgts, id)
 			checkAnomalyTriggers(self, a)
 
-			a:setEffect(self.EFF_FLAWED_DESIGN, getAnomalyDuration(self), {power=getAnomalyEffectPower(self)})
+			a:setEffect(self.EFF_FLAWED_DESIGN, getAnomalyDuration(self, t), {power=getAnomalyEffectPower(self, t)})
 			game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport")
 			game:playSoundNear(self, "talents/spell_generic")
 		end
 		return true
 	end,
 	info = function(self, t)
-		return ([[Reduces the resistances of up to five targets in a ball of radius %d by %d%%.]]):format(self:getTalentRadius(t), getAnomalyEffectPower(self))
+		return ([[Reduces the resistances of up to five targets in a ball of radius %d by %d%%.]]):format(self:getTalentRadius(t), getAnomalyEffectPower(self, t))
 	end,
 }
 
@@ -1336,9 +1291,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1351,7 +1306,7 @@ newTalent{
 		local x, y = checkAnomalyTargeting(self, t, tg)
 		local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRange(t))
 		
-		local dam = getAnomalyRadius(self) -- not a typo, very low damage since this isn't a major anomaly
+		local dam = getAnomalyRadius(self, t) -- not a typo, very low damage since this isn't a major anomaly
 
 		-- Randomly take targets
 		for i = 1, rng.avg(3, 6, 3) do
@@ -1398,9 +1353,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1412,7 +1367,7 @@ newTalent{
 		local tg = self:getTalentTarget(t)
 		local tgts = getAnomalyTargets(self, t, self.x, self.y, "ACTOR", self:getTalentRange(t))
 		
-		local movedam = self:spellCrit(getAnomalyDamageAoE(self))
+		local movedam = self:spellCrit(getAnomalyDamage(self, t))
 
 		-- Randomly take targets
 		for i = 1, rng.avg(3, 6, 3) do
@@ -1448,6 +1403,49 @@ newTalent{
 	end,
 }
 
+newTalent{
+	name = "Anomaly Calcify",
+	type = {"chronomancy/anomalies", 1},
+	anomaly_type = "major",
+	type_no_req = true,
+	no_unlearn_last = true,
+	points = 1,
+	paradox = 0,
+	cooldown =0,
+	range = 10,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
+	direct_hit = true,
+	no_energy = true,
+	requires_target = true,
+	target = function(self, t)
+		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)}
+	end,
+	message = "@Source@ calcifies several targets.",
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local x, y = checkAnomalyTargeting(self, t, tg)
+		local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRadius(t))
+
+		-- Randomly take targets
+		for i = 1, rng.avg(1, 5, 3) do
+			if #tgts <= 0 then break end
+			local a, id = rng.table(tgts)
+			table.remove(tgts, id)
+			checkAnomalyTriggers(self, a)
+			
+			if a:canBe("anomaly") and a:canBe("stun") and a:canBe("stone") and a:canBe("instakill")then
+				a:setEffect(a.EFF_STONED, getAnomalyDuration(self, t), {apply_power=getParadoxSpellpower(self, t)})
+				game.level.map:particleEmitter(a.x, a.y, 1, "archery")
+			end
+		end
+		return true
+	end,
+	info = function(self, t)
+		return ([[Turns up to 5 targets in a radius %d ball to stone for %d turns.]]):
+		format(getAnomalyRadius(self, t), getAnomalyDuration(self, t))
+	end,
+}
+
 newTalent{
 	name = "Anomaly Call",
 	type = {"chronomancy/anomalies", 1},
@@ -1456,9 +1454,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 50,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1499,16 +1497,16 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
 	target = function(self, t)
 		return {type="hit", range=self:getTalentRange(t), talent=t}
 	end,
-	getHaste = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self)) / 100) end,
+	getHaste = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self, t)) / 100) end,
 	message = "The odds have tilted.",
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
@@ -1520,15 +1518,15 @@ newTalent{
 		table.remove(tgts, id)
 		checkAnomalyTriggers(self, a)
 
-		a:setEffect(self.EFF_SPEED, getAnomalyDuration(self), {power=t.getHaste(self, t)})
-		a:setEffect(self.EFF_REGENERATION,  getAnomalyDuration(self), {power=getAnomalyEffectPower(self)})
-		a:setEffect(self.EFF_PAIN_SUPPRESSION,  getAnomalyDuration(self), {power=getAnomalyEffectPower(self)})
+		a:setEffect(self.EFF_SPEED, getAnomalyDuration(self, t), {power=t.getHaste(self, t)})
+		a:setEffect(self.EFF_REGENERATION,  getAnomalyDuration(self, t), {power=getAnomalyEffectPower(self, t)})
+		a:setEffect(self.EFF_PAIN_SUPPRESSION,  getAnomalyDuration(self, t), {power=getAnomalyEffectPower(self, t)})
 		game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport")
 		game:playSoundNear(self, "talents/spell_generic")
 		return true
 	end,
 	info = function(self, t)
-		return ([[Substantially toughens and hastes one target for %d turns.]]):format(getAnomalyDuration(self))
+		return ([[Substantially toughens and hastes one target for %d turns.]]):format(getAnomalyDuration(self, t))
 	end,
 }
 
@@ -1540,9 +1538,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1558,7 +1556,7 @@ newTalent{
 			return
 		end
 		if self:attr("summon_time") then return end
-		local m = makeParadoxClone(self, self, getAnomalyDuration(self)*2)
+		local m = makeParadoxClone(self, self, getAnomalyDuration(self, t)*2)
 		game.zone:addEntity(game.level, m, "actor", x, y)
 		m.faction = "enemies"
 		m.summoner = nil
@@ -1569,7 +1567,7 @@ newTalent{
 		return true
 	end,
 	info = function(self, t)
-		return ([[Clones the caster.]]):format(getAnomalyDuration(self))
+		return ([[Clones the caster.]]):format(getAnomalyDuration(self, t))
 	end,
 }
 
@@ -1581,9 +1579,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1607,7 +1605,7 @@ newTalent{
 				return
 			end
 			if a:attr("summon_time") then return end
-			local m = makeParadoxClone(self, a, getAnomalyDuration(self)*2)
+			local m = makeParadoxClone(self, a, getAnomalyDuration(self, t)*2)
 			game.zone:addEntity(game.level, m, "actor", x, y)
 			m.ai_state = { talent_in=1, ally_compassion=10}
 			game.level.map:particleEmitter(x, y, 1, "generic_teleport", {rm=60, rM=130, gm=20, gM=110, bm=90, bM=130, am=70, aM=180})
@@ -1616,7 +1614,7 @@ newTalent{
 		return true
 	end,
 	info = function(self, t)
-		return ([[Clones all creatures in a radius of 10.]]):format(getAnomalyDuration(self))
+		return ([[Clones all creatures in a radius of 10.]]):format(getAnomalyDuration(self, t))
 	end,
 }
 
@@ -1628,9 +1626,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true, 
 	requires_target = true,
@@ -1662,9 +1660,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1676,7 +1674,7 @@ newTalent{
 		local tg = self:getTalentTarget(t)
 		local tgts = getAnomalyTargets(self, t, self.x, self.y, "ACTOR", self:getTalentRange(t))
 		
-		local movedam = self:spellCrit(getAnomalyDamage(self)) -- High damage
+		local movedam = self:spellCrit(getAnomalyDamage(self, t)) -- High damage
 
 		-- Randomly take targets
 		if #tgts <= 0 then return end
@@ -1720,9 +1718,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1734,8 +1732,9 @@ newTalent{
 		local tg = self:getTalentTarget(t)
 		local tgts = getAnomalyTargets(self, t, self.x, self.y, "ACTOR", self:getTalentRange(t))
 
-		local movedam = self:spellCrit(getAnomalyDamageAoE(self))
-		local dam = self:spellCrit(getAnomalyDamage(self)/2)
+		local movedam = self:spellCrit(getAnomalyDamage(self, t))
+		local dam = self:spellCrit(getAnomalyDamage(self, t)/2)
+		local apply_power = getParadoxSpellpower(self, t)
 
 		-- Randomly take targets
 		for i = 1, rng.avg(3, 6, 3) do
@@ -1748,7 +1747,7 @@ newTalent{
 			local proj = require("mod.class.Projectile"):makeHoming(
 				self,
 				{particle="bolt_lightning", trail="lightningtrail"},
-				{speed=2, name="Tornado", dam=dam, movedam=movedam, start_x=orig_x, start_y=orig_y},
+				{speed=2, name="Tornado", dam=dam, movedam=movedam, start_x=orig_x, start_y=orig_y, apply=apply_power},
 				target,
 				self:getTalentRange(t),
 				function(self, src)
@@ -1760,7 +1759,7 @@ newTalent{
 					src:project({type="ball", radius=1, x=self.x, y=self.y}, self.x, self.y, DT.LIGHTNING, self.def.dam)
 					src:project({type="ball", radius=1, x=self.x, y=self.y}, self.x, self.y, DT.MINDKNOCKBACK, self.def.dam)
 					if target:canBe("stun") then
-						target:setEffect(target.EFF_STUNNED, 4, {apply_power=getParadoxSpellpower(src)})
+						target:setEffect(target.EFF_STUNNED, 4, {apply_power=self.def.apply})
 					else
 						game.logSeen(target, "%s resists the tornado!", target.name:capitalize())
 					end
@@ -1796,9 +1795,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1860,7 +1859,7 @@ newTalent{
 					local target = game.level.map(px, py, engine.Map.ACTOR)
 					if target then
 						if target:canBe("stun") then
-							target:setEffect(target.EFF_STUNNED, 3, {apply_power=src:combatSpellpower()})
+							target:setEffect(target.EFF_STUNNED, 3, {apply_power=getParadoxSpellpower(src, t)})
 						else
 							game.logSeen(target, "%s resists the stun!", target.name:capitalize())
 						end
@@ -1871,7 +1870,7 @@ newTalent{
 			end
 		end
 
-		local dam = self:spellCrit(getAnomalyDamage(self))
+		local dam = self:spellCrit(getAnomalyDamage(self, t))
 		meteor(self, x, y, dam)
 
 		return true
@@ -1890,9 +1889,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
@@ -1923,7 +1922,7 @@ newTalent{
 				end
 				return true
 			end,
-			temporary = getAnomalyDuration(self)*10,
+			temporary = getAnomalyDuration(self, t)*10,
 			x = tx, y = ty,
 			canAct = false,
 			energy = {value=0},
@@ -1972,9 +1971,9 @@ newTalent{
 	no_unlearn_last = true,
 	points = 1,
 	paradox = 0,
-	cooldown = 1,
+	cooldown =0,
 	range = 10,
-	radius = function(self, t) return getAnomalyRadius(self) end,
+	radius = function(self, t) return getAnomalyRadius(self, t) end,
 	direct_hit = true,
 	no_energy = true,
 	requires_target = true,
diff --git a/game/modules/tome/data/talents/chronomancy/blade-threading.lua b/game/modules/tome/data/talents/chronomancy/blade-threading.lua
index ab1997ab060f68eee7b28875bc29eff107115744..75472cf5a8618d06b1ee183a1f0ce7e0f297ec9c 100644
--- a/game/modules/tome/data/talents/chronomancy/blade-threading.lua
+++ b/game/modules/tome/data/talents/chronomancy/blade-threading.lua
@@ -28,9 +28,10 @@ newTalent{
 	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
 	tactical = { ATTACK = {weapon = 2}, DISABLE = 3 },
 	requires_target = true,
-	no_energy = "fake",
+	speed = "weapon",
 	getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.5) end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end,
+	getWarp = function(self, t) return 7 + self:combatSpellpower(0.092) * self:combatTalentScale(t, 1, 7) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end,
 	on_pre_use = function(self, t, silent) if not doWardenPreUse(self, "dual") then if not silent then game.logPlayer(self, "You require two weapons to use this talent.") end return false end return true end,
 	action = function(self, t)
 		local dam, swap = doWardenWeaponSwap(self, t, t.getDamage(self, t))
@@ -39,49 +40,26 @@ newTalent{
 		local x, y, target = self:getTarget(tg)
 		if not x or not y or not target then if swap then doWardenWeaponSwap(self, t, nil, "bow") end return nil end
 		if core.fov.distance(self.x, self.y, x, y) > 1 then if swap then doWardenWeaponSwap(self, t, nil, "bow") end return nil end
-		local hitted = self:attackTarget(target, DamageType.MATTER, dam)
-
+		
+		-- Hit?
+		local hitted = self:attackTarget(target, nil, dam, true)
+		
+		-- Project our warp
 		if hitted then
-			local chance = rng.range(1, 4)
-			if chance == 1 then
-				if target:canBe("stun") then
-					target:setEffect(target.EFF_STUNNED, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self)})
-				else
-					game.logSeen(target, "%s resists the stun!", target.name:capitalize())
-				end
-			elseif chance == 2 then
-				if target:canBe("blind") then
-					target:setEffect(target.EFF_BLINDED, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self)})
-				else
-					game.logSeen(target, "%s resists the blindness!", target.name:capitalize())
-				end
-			elseif chance == 3 then
-				if target:canBe("pin") then
-					target:setEffect(target.EFF_PINNED, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self)})
-				else
-					game.logSeen(target, "%s resists the pin!", target.name:capitalize())
-				end
-			elseif chance == 4 then
-				if target:canBe("confusion") then
-					target:setEffect(target.EFF_CONFUSED, t.getDuration(self, t), {power=50, apply_power=getParadoxSpellpower(self)})
-				else
-					game.logSeen(target, "%s resists the confusion!", target.name:capitalize())
-				end
-			end
-			
+			self:project({type="hit"}, target.x, target.y, DamageType.WARP, {dam=self:spellCrit(t.getWarp(self, t)), chance=100, dur=t.getDuration(self, t), apply_power=getParadoxSpellpower(self, t)})
 			game.level.map:particleEmitter(target.x, target.y, 1, "generic_discharge", {rm=64, rM=64, gm=134, gM=134, bm=170, bM=170, am=35, aM=90})
-			--game:playSoundNear(self, "talents/distortion")
-		end				
-
+		end
+		
 		return true
 	end,
 	info = function(self, t)
 		local damage = t.getDamage(self, t) * 100
 		local duration = t.getDuration(self, t)
-		return ([[Attack the target with your melee weapons for %d%%.  Half of this damage will be temporal and half physical.
-		If the attack hits the target may be stunned, blinded, pinned, or confused for %d turns.
-		Chance of applying a random effect improves with your Spellpower.]])
-		:format(damage, duration)
+		local warp = t.getWarp(self, t)
+		return ([[Attack the target with your melee weapons for %d%%.  
+		If the attack hits you'll warp the target, dealing %0.2f temporal and %0.2f physical damage, and may stun, blind, pin, or confuse them for %d turns.
+		The bonus damage improves with your Spellpower.]])
+		:format(damage, damDesc(self, DamageType.TEMPORAL, warp/2), damDesc(self, DamageType.PHYSICAL, warp/2), duration)
 	end
 }
 
@@ -117,19 +95,17 @@ newTalent{
 	paradox = function (self, t) return getParadoxCost(self, t, 15) end,
 	tactical = { ATTACKAREA = {weapon = 2}, DISABLE = 3 },
 	requires_target = true,
-	no_energy = "fake",
+	speed = "weapon",
 	getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.5) end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end,
-	getPower = function(self, t) return self:combatTalentSpellDamage(t, 50, 150, getParadoxSpellpower(self)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end,
+	getPower = function(self, t) return self:combatTalentSpellDamage(t, 50, 150, getParadoxSpellpower(self, t)) end,
 	on_pre_use = function(self, t, silent) if not doWardenPreUse(self, "dual") then if not silent then game.logPlayer(self, "You require two weapons to use this talent.") end return false end return true end,
 	action = function(self, t)
 		local dam, swap = doWardenWeaponSwap(self, t, t.getDamage(self, t))
 		local tg = {type="hit", range=self:getTalentRange(t), talent=t}
 		local x, y, target = self:getTarget(tg)
 		if not x or not y or not target then if swap then doWardenWeaponSwap(self, t, nil, "bow") end return nil end
-		local hit1 = false
-		local hit2 = false
-		local hit3 = false
+		local braid_targets = {}
 
 		-- do the braid
 		if core.fov.distance(self.x, self.y, x, y) == 1 then
@@ -138,35 +114,30 @@ newTalent{
 			local lx, ly = util.coordAddDir(self.x, self.y, util.dirSides(dir, self.x, self.y).left)
 			local rx, ry = util.coordAddDir(self.x, self.y, util.dirSides(dir, self.x, self.y).right)
 			local lt, rt = game.level.map(lx, ly, Map.ACTOR), game.level.map(rx, ry, Map.ACTOR)
-
+			
 			-- target hit
-			hit1 = self:attackTarget(target, nil, damage)
+			local hit1 = self:attackTarget(target, nil, damage, true)
+			if hit1 then braid_targets[#braid_targets+1] = target end
 			
 			--left hit
-			if lt then
-				hit2 = self:attackTarget(lt, nil, damage, true)
+			if lt and self:reactionToward(lt) < 0 then
+				local hit2 = self:attackTarget(lt, nil, damage, true)
+				if hit2 then braid_targets[#braid_targets+1] = lt end
 			end
 			--right hit
-			if rt then
-				hit3 = self:attackTarget(rt, nil, damage, true)
+			if rt and self:reactionToward(rt) < 0 then
+				local hit3 = self:attackTarget(rt, nil, damage, true)
+				if hit3 then braid_targets[#braid_targets+1] = rt end
 			end
 			
-			-- Braid them; no save
-			if hit1 then
-				if hit2 or hit3 then
-					target:setEffect(target.EFF_BRAIDED, t.getDuration(self, t), {power=t.getPower(self, t), src=self, braid_one=lt or nil, braid_two=rt or nil})
-				end
-			end
-			if hit2 then
-				if hit1 or hit3 then
-					lt:setEffect(lt.EFF_BRAIDED, t.getDuration(self, t), {power=t.getPower(self, t), src=self, braid_one=target or nil, braid_two=rt or nil})
-				end
-			end
-			if hit3 then
-				if hit1 or hit2 then
-					rt:setEffect(rt.EFF_BRAIDED, t.getDuration(self, t), {power=t.getPower(self, t), src=self, braid_one=target or nil, braid_two=lt or nil})
+			-- if we hit more than one, braid them
+			if #braid_targets > 1 then
+				for i = 1, #braid_targets do
+					local target = braid_targets[i]
+					target:setEffect(target.EFF_BRAIDED, t.getDuration(self, t), {power=t.getPower(self, t), src=self, targets=braid_targets})
 				end
 			end
+
 		else
 			if swap then doWardenWeaponSwap(self, t, nil, "bow") end
 			return nil
@@ -195,8 +166,7 @@ newTalent{
 	tactical = { ATTACKAREA = {weapon = 2}, ATTACK = {weapon = 2},  },
 	requires_target = true,
 	is_teleport = true,
-	direct_hit = true,
-	no_energy = "fake",
+	speed = "weapon",
 	getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 0.6, 1.2) end,
 	getTeleports = function(self, t) return self:getTalentLevel(t) >= 5 and 2 or 1 end,
 	on_pre_use = function(self, t, silent) if not doWardenPreUse(self, "dual") then if not silent then game.logPlayer(self, "You require two weapons to use this talent.") end return false end return true end,
@@ -208,7 +178,7 @@ newTalent{
 			
 		-- Hit the target
 		if core.fov.distance(self.x, self.y, target.x, target.y) > 1 then if swap then doWardenWeaponSwap(self, t, nil, "bow") end return nil end
-		local hitted = self:attackTarget(target, nil, dam)
+		local hitted = self:attackTarget(target, nil, dam, true)
 
 		if hitted then
 			-- Get available targets
@@ -219,6 +189,7 @@ newTalent{
 				local a = game.level.map(x, y, Map.ACTOR)
 				if a and self:reactionToward(a) < 0 and self:hasLOS(a.x, a.y) then
 					tgts[#tgts+1] = a
+					print("Temporal Assault Target %s", a.name)
 				end
 			end end
 			
@@ -229,7 +200,7 @@ newTalent{
 				local a, id = rng.table(tgts)
 				-- since we're using a precise teleport we'll look for a free grid first
 				local tx2, ty2 = util.findFreeGrid(a.x, a.y, 5, true, {[Map.ACTOR]=true})
-				if tx2 and ty2 then
+				if tx2 and ty2 and not a.dead then
 					game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
 					if not self:teleportRandom(tx2, ty2, 0) then
 						attempts = attempts - 1
diff --git a/game/modules/tome/data/talents/chronomancy/bow-threading.lua b/game/modules/tome/data/talents/chronomancy/bow-threading.lua
index 042fbe96e221f9065820c0a23ea2513cdcbaa842..b2fe1e6afcffabbf5d36397daf6eb74f2dcf54b5 100644
--- a/game/modules/tome/data/talents/chronomancy/bow-threading.lua
+++ b/game/modules/tome/data/talents/chronomancy/bow-threading.lua
@@ -29,7 +29,7 @@ newTalent{
 	tactical = { BUFF = 2 },
 	points = 5,
 	getDamage = function(self, t) return 7 + self:combatSpellpower(0.092) * self:combatTalentScale(t, 1, 7) end,
-	getApplyPower = function(self, t) return getParadoxSpellpower(self) end,
+	getApplyPower = function(self, t) return getParadoxSpellpower(self, t) end,
 	activate = function(self, t)
 		return {}
 	end,
@@ -52,7 +52,7 @@ newTalent{
 	tactical = { ATTACK = {weapon = 2} },
 	requires_target = true,
 	range = archery_range,
-	no_energy = "fake",
+	speed = 'archery',
 	getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.1, 2.2) end,
 	getParadoxReduction = function(self, t) return math.floor(self:combatTalentScale(t, 10, 20)) end,
 	on_pre_use = function(self, t, silent) if not doWardenPreUse(self, "bow") then if not silent then game.logPlayer(self, "You require a bow to use this talent.") end return false end return true end,
@@ -62,9 +62,9 @@ newTalent{
 	action = function(self, t)
 		local dam, swap = doWardenWeaponSwap(self, t, t.getDamage(self, t))
 
-		local targets = self:archeryAcquireTargets(nil, {one_shot=true, infinite=true})
+		local targets = self:archeryAcquireTargets({type="bolt"}, {one_shot=true, infinite=true, no_energy = true})
 		if not targets then if swap then doWardenWeaponSwap(self, t, nil, "blade") end return end
-		self:archeryShoot(targets, t, nil, {mult=dam, damtype=DamageType.TEMPORAL})
+		self:archeryShoot(targets, t, {type="bolt"}, {mult=dam, damtype=DamageType.TEMPORAL})
 
 		return true
 	end,
@@ -88,9 +88,9 @@ newTalent{
 	requires_target = true,
 	range = archery_range,
 	radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.3, 3.7)) end,
-	no_energy = "fake",
+	speed = 'archery',
 	getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.5) end,
-	getDamageAoE = function(self, t) return self:combatTalentSpellDamage(t, 10, 170, getParadoxSpellpower(self)) end,
+	getDamageAoE = function(self, t) return self:combatTalentSpellDamage(t, 25, 290, getParadoxSpellpower(self, t)) end,
 	target = function(self, t)
 		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), talent=t}
 	end,
@@ -101,17 +101,45 @@ newTalent{
 			if not x or not y then return nil end
 			local _ _, _, _, x, y = self:canProject(tg, x, y)
 			
-			local dam = self:spellCrit(t.getDamageAoE(self, t))
-			local grids = self:project(tg, x, y, function(px, py)
+			local tgts = {}
+			self:project(tg, x, y, function(px, py)
 				local target = game.level.map(px, py, Map.ACTOR)
-				if not target then return end
-				local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true})
-				if tx and ty and target:canBe("knockback") then
-					target:move(tx, ty, true)
-					game.logSeen(target, "%s is drawn in by the singularity!", target.name:capitalize())
+				if target and not target:isTalentActive(target.T_GRAVITY_LOCUS) then
+					-- If we've already moved this target don't move it again
+					for _, v in pairs(tgts) do
+						if v == target then
+							return
+						end
+					end
+
+					-- Do our Knockback
+					local can = function(target)
+						if target:checkHit(getParadoxSpellpower(self, t), target:combatPhysicalResist(), 0, 95) and target:canBe("knockback") then -- Deprecated Checkhit call
+							return true
+						else
+							game.logSeen(target, "%s resists the knockback!", target.name:capitalize())
+						end
+					end
+					if can(target) then
+						target:pull(x, y, tg.radius, can)
+						tgts[#tgts+1] = target
+						game.logSeen(target, "%s is drawn in by the singularity!", target.name:capitalize())
+						target:crossTierEffect(target.EFF_OFFBALANCE, getParadoxSpellpower(self, t))
+					end
 				end
 			end)
-			self:project(tg, x, y, DamageType.GRAVITY, self:spellCrit(dam))
+			
+			-- 25% bonus damage per target beyond the first
+			local dam = self:spellCrit(t.getDamageAoE(self, t))
+			dam = dam + math.min(dam, dam*(#tgts-1)/4)
+			
+			-- Project our damage last based on number of targets hit
+			self:project(tg, x, y, function(px, py)
+				local dist_factor = 1 + (core.fov.distance(x, y, px, py)/5)
+				local damage = dam/dist_factor
+				DamageType:get(DamageType.GRAVITY).projector(self, px, py, DamageType.GRAVITY, damage)
+			end)
+
 			game.level.map:particleEmitter(x, y, tg.radius, "gravity_spike", {radius=tg.radius, allow=core.shader.allow("distort")})
 
 			game:playSoundNear(self, "talents/earth")
@@ -127,9 +155,9 @@ newTalent{
 		
 		tg.type = "bolt" -- switch our targeting back to a bolt
 
-		local targets = self:archeryAcquireTargets(self:getTalentTarget(t), {one_shot=true, x=x, y=y})
+		local targets = self:archeryAcquireTargets(tg, {one_shot=true, x=x, y=y, no_energy = true})
 		if not targets then return end
-		self:archeryShoot(targets, t, nil, {mult=dam})
+		self:archeryShoot(targets, t, {type="bolt"}, {mult=dam})
 
 		return true
 	end,
@@ -137,9 +165,11 @@ newTalent{
 		local damage = t.getDamage(self, t) * 100
 		local radius = self:getTalentRadius(t)
 		local aoe = t.getDamageAoE(self, t)
-		return ([[Fire a shot doing %d%% damage.  When the arrow reaches its destination it will draw in creatures in a radius of %d and inflict %0.2f additional physical damage.
-		The additional damage scales with your Spellpower and inflicts 50%% extra damage to pinned targets.]])
-		:format(damage, radius, damDesc(self, DamageType.PHYSICAL, aoe))
+		return ([[Fire a shot doing %d%% damage.  When the arrow reaches its destination it will draw in creatures in a radius of %d and inflict %0.2f physical damage.
+		Each target moved beyond the first deals an additional %0.2f physical damage (up to %0.2f bonus damage).
+		Targets take reduced damage the further they are from the epicenter (20%% less per tile).
+		The additional damage scales with your Spellpower.]])
+		:format(damage, radius, damDesc(self, DamageType.PHYSICAL, aoe), damDesc(self, DamageType.PHYSICAL, aoe/4), damDesc(self, DamageType.PHYSICAL, aoe))
 	end
 }
 
@@ -154,7 +184,7 @@ newTalent{
 	requires_target = true,
 	range = archery_range,
 	radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.3, 3.7)) end,
-	no_energy = "fake",
+	speed = 'archery',
 	getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.5) end,
 	getClones = function(self, t) return self:getTalentLevel(t) >= 5 and 3 or self:getTalentLevel(t) >= 3 and 2 or 1 end,
 	target = function(self, t)
@@ -176,7 +206,7 @@ newTalent{
 			return nil
 		end
 				
-		local targets = self:archeryAcquireTargets(self:getTalentTarget(t), {one_shot=true, x=x, y=y})
+		local targets = self:archeryAcquireTargets(self:getTalentTarget(t), {one_shot=true, x=x, y=y, no_energy = true})
 		if not targets then return end
 		self:archeryShoot(targets, t, {type="bolt", friendlyfire=false, friendlyblock=false}, {mult=dam})
 		
@@ -204,6 +234,7 @@ newTalent{
 				m.arrow_stitched_clone = true
 				m.generic_damage_penalty = 50
 				m.energy.value = 1000
+				m:attr("archery_pass_friendly", 1)
 				m.on_act = function(self)
 					if not self.shoot_target.dead then
 						self:forceUseTalent(self.T_ARROW_STITCHING, {force_level=t.leve, ignore_cd=true, ignore_energy=true, force_target=self.shoot_target, ignore_ressources=true, silent=true})
diff --git a/game/modules/tome/data/talents/chronomancy/chronomancer.lua b/game/modules/tome/data/talents/chronomancy/chronomancer.lua
index 9da046ab2d22c7b3dfeffebb9c2045464c6bf329..d99be34c9b5d4c018b87307e23c59e40ebe96be5 100644
--- a/game/modules/tome/data/talents/chronomancy/chronomancer.lua
+++ b/game/modules/tome/data/talents/chronomancy/chronomancer.lua
@@ -24,18 +24,18 @@ newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronoma
 newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/threaded-combat", name = "Threaded Combat", min_lev = 10, description = "A blend of ranged and dual-weapon combat." }
 newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/temporal-hounds", name = "Temporal Hounds", min_lev = 10, description = "Call temporal hounds to aid you in combat." }
 
-newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/energy", name = "energy", generic = true, description = "Manipulate raw energy by addition or subtraction." }
 newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/fate-threading", name = "Fate Threading", description = "Manipulate the threads of fate." }
 newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/gravity", name = "gravity", description = "Call upon the force of gravity to crush, push, and pull your foes." }
-newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/matter", name = "matter", description = "Change and shape matter itself." }
-newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/paradox", name = "paradox", min_lev = 10, description = "Create loopholes in the laws of spacetime." }
-newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/speed-control", name = "Speed Control", description = "Control how fast objects and creatures move through spacetime." }
-newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/timeline-threading", name = "Timeline Threading", min_lev = 10, description = "Examine and alter the timelines that make up the spacetime continuum." }
-newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/timetravel", name = "Time Travel", min_lev = 10, description = "Travel through time yourself, or send your foes into the future." }
 newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/spacetime-folding", name = "Spacetime Folding", description = "Mastery of folding points in space." }
+newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/speed-control", name = "Speed Control", description = "Control how fast objects and creatures move through spacetime." }
+newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/timeline-threading", name = "Timeline Threading", description = "Examine and alter the timelines that make up the spacetime continuum." }
+newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/timetravel", name = "timetravel", description = "Directly manipulate the flow of time" }
+newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/spatial-tears", name = "Spatial Tears", min_lev = 10, description = "Manipulate topological defects in the fabric of spacetime." }
+newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/spellbinding", name = "Spellbinding", min_lev = 10, description = "Manipulate chronomantic spells." }
 
 -- Generic Chronomancy
 newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/chronomancy", name = "Chronomancy", generic = true, description = "Allows you to glimpse the future, or become more aware of the present." }
+newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/energy", name = "energy", generic = true, min_lev = 10, description = "Manipulate raw energy by addition or subtraction." }
 newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/fate-weaving", name = "Fate Weaving", generic = true, description = "Weave the threads of fate." }
 newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/spacetime-weaving", name = "Spacetime Weaving", generic = true, description = "Weave the threads of spacetime." }
 
@@ -43,11 +43,11 @@ newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronoma
 newTalentType{ no_silence=true, is_spell=true, type="chronomancy/other", name = "Other", generic = true, description = "Miscellaneous Chronomancy effects." }
 
 newTalentType{ no_silence=true, is_spell=true, type="chronomancy/age-manipulation", name = "Age Manipulation", description = "Manipulate the age of creatures you encounter." }
+newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/matter", name = "matter", description = "Change and shape matter itself." }
+newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/paradox", name = "paradox", description = "Break the laws of spacetime." }
 newTalentType{ no_silence=true, is_spell=true, type="chronomancy/temporal-archery", name = "Temporal Archery", description = "A blend of chronomancy and ranged combat." }
 newTalentType{ no_silence=true, is_spell=true, type="chronomancy/temporal-combat", name = "Temporal Combat", description = "A blend of chronomancy and physical combat." }
 
-
-
 -- Anomalies are not learnable but can occur instead of an intended spell when paradox gets to high.
 newTalentType{ no_silence=true, is_spell=true, type="chronomancy/anomalies", name = "anomalies", description = "Spacetime anomalies that can randomly occur when paradox is to high." }
 
@@ -127,6 +127,7 @@ load("/data/talents/chronomancy/gravity.lua")
 load("/data/talents/chronomancy/matter.lua")
 load("/data/talents/chronomancy/paradox.lua")
 load("/data/talents/chronomancy/speed-control.lua")
+load("/data/talents/chronomancy/spellbinding.lua")
 load("/data/talents/chronomancy/temporal-archery.lua")
 load("/data/talents/chronomancy/temporal-combat.lua")
 load("/data/talents/chronomancy/guardian.lua")
@@ -136,6 +137,7 @@ load("/data/talents/chronomancy/timeline-threading.lua")
 load("/data/talents/chronomancy/timetravel.lua")
 load("/data/talents/chronomancy/spacetime-folding.lua")
 load("/data/talents/chronomancy/spacetime-weaving.lua")
+load("/data/talents/chronomancy/spatial-tears.lua")
 
 -- Loads many functions and misc. talents
 load("/data/talents/chronomancy/other.lua")
diff --git a/game/modules/tome/data/talents/chronomancy/chronomancy.lua b/game/modules/tome/data/talents/chronomancy/chronomancy.lua
index 6410b3fd853237e613a6887c415b51e4bb841e87..9526791660f0e475b3df6c7fff37dcca92dc1855 100644
--- a/game/modules/tome/data/talents/chronomancy/chronomancy.lua
+++ b/game/modules/tome/data/talents/chronomancy/chronomancy.lua
@@ -17,7 +17,7 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
--- EDGE TODO: Icons, Particles, Timed Effect Particles
+-- EDGE TODO: Particles, Timed Effect Particles
 
 newTalent{
 	name = "Precognition",
@@ -27,8 +27,8 @@ newTalent{
 	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
 	cooldown = 20,
 	no_npc_use = true,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 14)) end,
-	range = function(self, t) return 10 + math.min(self:combatTalentSpellDamage(t, 10, 20, getParadoxSpellpower(self))) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 6, 14))) end,
+	range = function(self, t) return 10 + math.min(self:combatTalentSpellDamage(t, 10, 20, getParadoxSpellpower(self, t))) end,
 	action = function(self, t)
 		-- Foresight bonuses
 		local defense = 0
@@ -86,13 +86,16 @@ newTalent{
 	sustain_paradox = 36,
 	mode = "sustained",
 	no_sustain_autoreset = true,
-	cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 15, 45, 25)) end, -- Limit >15
+	cooldown = 50,
+	getTrigger= function(self, t) return self:combatTalentScale(t, 0.25, 0.45, 0.6) end,
 	tactical = { DEFEND = 2 },
-	no_npc_use = true,
+	no_npc_use = true,  -- so rares don't learn useless talents
+	allow_temporal_clones = true,  -- let clones copy it anyway so they can benefit from the effects
+	on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end,  -- but don't let them cast it
 	callbackOnHit = function(self, t, cb)
 		local p = self:isTalentActive(t.id)
 		local life_after = self.life - cb.value
-		local cont_trigger = self.max_life * 0.3
+		local cont_trigger = self.max_life * t.getTrigger(self, t)
 		
 		-- Cast our contingent spell
 		if p and p.rest_count <= 0 and cont_trigger > life_after then
@@ -100,15 +103,18 @@ newTalent{
 			local cont_id = self:getTalentFromId(cont_t)
 			local t_level = math.min(self:getTalentLevel(t), self:getTalentLevel(cont_t))
 			
-			-- Make sure we still know the talent and that the preuse conditions apply
-			if t_level == 0 or not self:preUseTalent(cont_id, true, true) then
+			-- Make sure we still know the talent and that the pre-use conditions apply
+			if t_level == 0 or not self:knowTalent(cont_id) or not self:preUseTalent(cont_id, true, true) then
 				game.logPlayer(self, "#LIGHT_RED#Your Contingency has failed to cast %s!", self:getTalentFromId(cont_t).name)
 			else
-				self:forceUseTalent(cont_t, {ignore_ressources=true, ignore_cd=true, ignore_energy=true, force_level=t_level})
 				game.logPlayer(self, "#STEEL_BLUE#Your Contingency triggered %s!", self:getTalentFromId(cont_t).name)
+				p.rest_count = self:getTalentCooldown(t)
+				game:onTickEnd(function()
+					if not self.dead then
+						self:forceUseTalent(cont_t, {ignore_ressources=true, ignore_cd=true, ignore_energy=true, force_level=t_level})
+					end
+				end)
 			end
-			
-			p.rest_count = self:getTalentCooldown(t)
 		end
 		
 		return cb.value
@@ -135,14 +141,15 @@ newTalent{
 		return true
 	end,
 	info = function(self, t)
+		local trigger = t.getTrigger(self, t) * 100
 		local cooldown = self:getTalentCooldown(t)
 		local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None"
-		return ([[Choose an activatable spell that's not targeted.  When you take damage that reduces your life below 30%% the spell will automatically cast.
+		return ([[Choose an activatable spell that's not targeted.  When you take damage that reduces your life below %d%% the spell will automatically cast.
 		This spell will cast even if it is currently on cooldown, will not consume a turn or resources, and uses the talent level of Contingency or its own, whichever is lower.
 		This effect can only occur once every %d turns and takes place after the damage is resolved.
 		
 		Current Contingency Spell: %s]]):
-		format(cooldown, talent)
+		format(trigger, cooldown, talent)
 	end,
 }
 
@@ -153,8 +160,10 @@ newTalent{
 	points = 5,
 	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
 	cooldown = 50,
-	no_npc_use = true,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 10, 25)) end,
+	no_npc_use = true,  -- so rares don't learn useless talents
+	allow_temporal_clones = true,  -- let clones copy it anyway so they can benefit from the effects
+	on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end,  -- but don't let them cast it
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(self:getTalentLevel(t), 10, 25))) end,
 	on_pre_use = function(self, t, silent)
 		if checkTimeline(self) then
 			if not silent then
@@ -191,7 +200,7 @@ newTalent{
 		return ([[You peer into three possible futures, allowing you to explore each for %d turns.  When the effect expires, you'll choose which of the three futures becomes your present.
 		If you know Foresight you'll gain additional defense and chance to shrug off critical hits (equal to your Foresight values) while See the Threads is active.
 		This spell splits the timeline.  Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful.
-		Note that seeing visions of your own death can still be fatal.
+		If you die in any thread you'll revert the timeline to the point when you first cast the spell and the effect will end.
 		This spell may only be used once per zone level.]])
 		:format(duration)
 	end,
diff --git a/game/modules/tome/data/talents/chronomancy/energy.lua b/game/modules/tome/data/talents/chronomancy/energy.lua
index 89418eaefbcab9e4a82496b9f3f038d0bde41a44..d552f75a132168baa15059c4affdc2234f9df170 100644
--- a/game/modules/tome/data/talents/chronomancy/energy.lua
+++ b/game/modules/tome/data/talents/chronomancy/energy.lua
@@ -17,23 +17,27 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
+-- EDGE TODO: Particles, Timed Effect Particles
+
 newTalent{
 	name = "Energy Decomposition",
 	type = {"chronomancy/energy",1},
 	mode = "sustained",
-	require = chrono_req1,
+	require = chrono_req_high1,
 	points = 5,
-	sustain_paradox = 20,
+	sustain_paradox = 24,
 	cooldown = 10,
 	tactical = { BUFF = 2 },
-	getAbsorption = function(self, t) return  self:combatTalentSpellDamage(t, 5, 150, getParadoxSpellpower(self)) end, -- Increase shield strength
-	on_damage = function(self, t, damtype, dam)
-		if not DamageType:get(damtype).antimagic_resolve then return dam end
-		local absorb = t.getAbsorption(self, t)
-		-- works like armor with 30% hardiness for projected energy effects
-		dam = math.max(dam * 0.3 - absorb, 0) + (dam * 0.7)
+	getDecomposition = function(self, t) return  self:combatTalentSpellDamage(t, 5, 150, getParadoxSpellpower(self, t)) end, -- Increase shield strength
+	callbackOnTakeDamage = function(self, t, src, x, y, type, dam, tmp, no_martyr)
+		local decomp = t.getDecomposition(self, t)
+		local lastdam = dam
+		
+		-- works like armor with 30% hardiness
+		dam = math.max(dam * 0.3 - decomp, 0) + (dam * 0.7)
 		print("[PROJECTOR] after static reduction dam", dam)
-		return dam
+		game:delayedLogDamage(src or self, self, 0, ("%s(%d dissipated)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", lastdam - dam), false)
+		return {dam=dam}
 	end,
 	activate = function(self, t)
 		game:playSoundNear(self, "talents/heal")
@@ -46,67 +50,41 @@ newTalent{
 		return true
 	end,
 	info = function(self, t)
-		local absorption = t.getAbsorption(self, t)
-		return ([[Partially dissipates all incoming energy damage (all except mind and physical damage), reducing it by 30%%, up to a maximum of %d.
-		The maximum damage reduction will scale with your Spellpower.]]):format(absorption)
-	end,
-}
-
-newTalent{
-	name = "Entropic Field",
-	type = {"chronomancy/energy",2},
-	mode = "sustained",
-	require = chrono_req2,
-	points = 5,
-	sustain_paradox = 20,
-	cooldown = 10,
-	tactical = { BUFF = 2 },
-	getPower = function(self, t) return math.min(90, 10 +  self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self))) end,
-	activate = function(self, t)
-		game:playSoundNear(self, "talents/heal")
-		return {
-			particle = self:addParticles(Particles.new("time_shield", 1)),
-			phys = self:addTemporaryValue("resists", {[DamageType.PHYSICAL]=t.getPower(self, t)/2}),
-			proj = self:addTemporaryValue("slow_projectiles", t.getPower(self, t)),
-		}
-	end,
-	deactivate = function(self, t, p)
-		self:removeParticles(p.particle)
-		self:removeTemporaryValue("resists", p.phys)
-		self:removeTemporaryValue("slow_projectiles", p.proj)
-		return true
-	end,
-	info = function(self, t)
-		local power = t.getPower(self, t)
-		return ([[You encase yourself in a field that slows incoming projectiles by %d%%, and increases your physical resistance by %d%%.
-		The effect will scale with your Spellpower.]]):format(power, power / 2)
+		local decomp = t.getDecomposition(self, t)
+		return ([[Partially dissipates all incoming damage, reducing it by 30%%, up to a maximum of %d.
+		The maximum damage reduction will scale with your Spellpower.]]):format(decomp)
 	end,
 }
 
 newTalent{
 	name = "Energy Absorption",
-	type = {"chronomancy/energy", 3},
-	require = chrono_req3,
+	type = {"chronomancy/energy", 2},
+	require = chrono_req_high2,
 	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
-	cooldown = 10,
+	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
+	cooldown = 6,
+	fixed_cooldown = true,
 	tactical = { DISABLE = 2 },
 	direct_hit = true,
 	requires_target = true,
-	range = 6,
+	range = 10,
 	getTalentCount = function(self, t)
-		return 1 + math.floor(self:combatTalentScale(math.max(1, self:getTalentLevel(t)), 0.5, 2.5, "log"))
+		return 1 + math.floor(self:combatTalentLimit(t, 3, 0, 2))
 	end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self, t)) end,
 	getCooldown = function(self, t) return math.ceil(self:combatTalentScale(t, 1, 2.6)) end,
+	target = function (self, t)
+		return {type="hit", range=self:getTalentRange(t), talent=t}
+	end,
 	action = function(self, t)
-		local tg = {type="hit", range=self:getTalentRange(t)}
+		local tg = self:getTalentTarget(t)
 		local tx, ty = self:getTarget(tg)
 		if not tx or not ty then return nil end
 		local _ _, tx, ty = self:canProject(tg, tx, ty)
 		local target = game.level.map(tx, ty, Map.ACTOR)
 		if not target then return end
 
-		if not self:checkHit(self:combatSpellpower(), target:combatSpellResist()) then
+		if not self:checkHit(getParadoxSpellpower(self, t), target:combatSpellResist()) then
 			game.logSeen(target, "%s resists!", target.name:capitalize())
 			return true
 		end
@@ -124,7 +102,7 @@ newTalent{
 			local t = rng.tableRemove(tids)
 			if not t then break end
 			target.talents_cd[t.id] = cdr
-			game.logSeen(target, "%s's %s is disrupted by the Energy Absorption!", target.name:capitalize(), t.name)
+			game.logSeen(target, "%s's %s is disrupted by the Energy Drain!", target.name:capitalize(), t.name)
 			count = count + 1
 		end
 
@@ -132,17 +110,22 @@ newTalent{
 			local tids = {}
 			for tid, _ in pairs(self.talents_cd) do
 				local tt = self:getTalentFromId(tid)
-				if tt.type[1]:find("^chronomancy/") then
-					tids[#tids+1] = tid
+				if tt.type[1]:find("^chronomancy/") and not tt.fixed_cooldown then
+					tids[#tids+1] = tt
 				end
 			end
+	
 			for i = 1, count do
 				if #tids == 0 then break end
 				local tid = rng.tableRemove(tids)
-				self.talents_cd[tid] = self.talents_cd[tid] - cdr
+				self:alterTalentCoolingdown(tid, - cdr)
 			end
+			
+			-- Deal our damage in one lump sum
+			self:project(tg, target.x, target.y, DamageType.TEMPORAL, self:spellCrit(count * t.getDamage(self, t)))
 		end
-		target:crossTierEffect(target.EFF_SPELLSHOCKED, self:combatSpellpower())
+		
+		target:crossTierEffect(target.EFF_SPELLSHOCKED, getParadoxSpellpower(self, t))
 		game.level.map:particleEmitter(tx, ty, 1, "generic_charge", {rm=10, rM=110, gm=10, gM=50, bm=20, bM=125, am=25, aM=255})
 		game.level.map:particleEmitter(self.x, self.y, 1, "generic_charge", {rm=200, rM=255, gm=200, gM=255, bm=200, bM=255, am=125, aM=125})
 		game:playSoundNear(self, "talents/spell_generic")
@@ -151,33 +134,73 @@ newTalent{
 	info = function(self, t)
 		local talentcount = t.getTalentCount(self, t)
 		local cooldown = t.getCooldown(self, t)
+		local damage = t.getDamage(self, t)
 		return ([[You sap the target's energy and add it to your own, placing up to %d random talents on cooldown for %d turns.  
-		For each talent put on cooldown, you reduce the cooldown of one of your chronomancy talents currently on cooldown by %d turns.]]):
-		format(talentcount, cooldown, cooldown)
+		For each talent put on cooldown, you reduce the cooldown of one of your chronomancy talents currently on cooldown by %d turns and deal %0.2f temporal damage to the target.
+		The damage done will scale with your Spellpower.]]):
+		format(talentcount, cooldown, cooldown, damDesc(self, DamageType.TEMPORAL, damage))
 	end,
 }
 
 newTalent{
 	name = "Redux",
-	type = {"chronomancy/energy",4},
-	require = chrono_req4,
+	type = {"chronomancy/energy",3},
+	require = chrono_req_high3,
 	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 40) end,
-	cooldown = 12,
+	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
+	cooldown = 24,
 	tactical = { BUFF = 2 },
-	no_energy = true,
-	getMaxLevel = function(self, t) return self:getTalentLevel(t) end,
+	fixed_cooldown = true,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 2, 4))) end,
+	getMaxCooldown = function(self, t) return math.floor(self:combatTalentScale(t, 3, 8)) end,
 	action = function(self, t)
 		-- effect is handled in actor postUse
-		self:setEffect(self.EFF_REDUX, 5, {})
+		self:setEffect(self.EFF_REDUX, t.getDuration(self, t), {max_cd=t.getMaxCooldown(self, t)})
 		game:playSoundNear(self, "talents/heal")
 		return true
 	end,
 	info = function(self, t)
-		local maxlevel = t.getMaxLevel(self, t)
-		return ([[You may recast the next activated chronomancy spell (up to talent level %0.1f) that you cast within the next 5 turns on the turn following its initial casting.
-		The Paradox cost of the initial spell will be paid each time it is cast, and the second casting will still consume a turn.
-		This spell takes no time to cast.]]):
-		format(maxlevel)
+		local duration = t.getDuration(self, t)
+		local cooldown = t.getMaxCooldown(self, t)
+		return ([[For the next %d turns chronomancy spells with a cooldown of %d or less do not go on cooldown when cast.]]):
+		format(duration, cooldown)
 	end,
 }
+
+newTalent{
+	name = "Entropy",
+	type = {"chronomancy/energy",4},
+	require = chrono_req_high4,
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
+	cooldown = 12,
+	tactical = { ATTACK = { TEMPORAL = 2 }, DEBUFF=3 },
+	range = 10,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 100, getParadoxSpellpower(self, t)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end,
+	target = function(self, t)
+		return {type="hit", range=self:getTalentRange(t), talent=t}
+	end,
+	requires_target = true,
+	direct_hit = true,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local x, y, target = self:getTarget(tg)
+		if not x or not y or not target then return nil end
+		local _ _, x, y = self:canProject(tg, x, y)
+		
+		local damage = self:spellCrit(t.getDamage(self, t))
+		target:setEffect(target.EFF_ENTROPY, t.getDuration(self, t), {power=damage, src=self, apply_power=getParadoxSpellpower(self, t)})
+
+		game:playSoundNear(self, "talents/dispel")
+
+		return true
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[Over the next %d turns all beneficial timed effects on the target tick twice as fast.
+		Each timed effect affected by this talent deals %0.2f temporal damage to the target.
+		The damage will scale with your Spellpower.]]):format(duration, damDesc(self, DamageType.TEMPORAL, damage))
+	end,
+}
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/chronomancy/fate-threading.lua b/game/modules/tome/data/talents/chronomancy/fate-threading.lua
index 71cb732e3305c4f163641bd52045713e5e904031..9d42d04a953c474db9d8f5ec15c70121633510ac 100644
--- a/game/modules/tome/data/talents/chronomancy/fate-threading.lua
+++ b/game/modules/tome/data/talents/chronomancy/fate-threading.lua
@@ -20,19 +20,111 @@
 -- EDGE TODO: Particles, Timed Effect Particles
 
 newTalent{
-	name = "Trim Threads",
+	name = "Disentangle",
 	type = {"chronomancy/fate-threading", 1},
 	require = chrono_req1,
 	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
+	cooldown = 12,
+	tactical = { PARADOX = 2 },
+	getReduction = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self, t)) end,
+	getParadoxMulti = function(self, t) return self:combatTalentLimit(t, 2, 0.10, .75) end,
+	anomaly_type = "no-major",
+	no_energy = true,
+	passives = function(self, t, p)
+		self:talentTemporaryValue(p, "anomaly_paradox_recovery", t.getParadoxMulti(self, t))
+	end,
+	action = function(self, t)
+		local reduction = self:spellCrit(t.getReduction(self, t))
+		self:paradoxDoAnomaly(reduction, t.anomaly_type, "forced")
+		return true
+	end,
+	info = function(self, t)
+		local reduction = t.getReduction(self, t)
+		local paradox = 100 * t.getParadoxMulti(self, t)
+		return ([[Disentangle the timeline, reducing your Paradox by %d and creating an anomaly.  This spell will never produce a major anomaly.
+		Additionally you recover %d%% more Paradox from random anomalies (%d%% total).
+		The Paradox reduction will increase with your Spellpower.]]):format(reduction, paradox, paradox + 200)
+	end,
+}
+
+newTalent{
+	name = "Preserve Pattern",
+	type = {"chronomancy/fate-threading", 2},
+	require = chrono_req2,
+	mode = "sustained", 
+	sustain_paradox = 0,
+	points = 5,
+	cooldown = 10,
+	tactical = { DEFEND = 2 },
+	getPercent = function(self, t) return self:combatTalentLimit(t, 50, 10, 30)/100 end, -- Limit < 50%
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 6))) end,
+	getConversionRatio = function(self, t) return 200 / self:combatTalentSpellDamage(t, 60, 600) end,
+	damage_feedback = function(self, t, p, src)
+		if p.particle and p.particle._shader and p.particle._shader.shad and src and src.x and src.y then
+			local r = -rng.float(0.2, 0.4)
+			local a = math.atan2(src.y - self.y, src.x - self.x)
+			p.particle._shader:setUniform("impact", {math.cos(a) * r, math.sin(a) * r})
+			p.particle._shader:setUniform("impact_tick", core.game.getTime())
+		end
+	end,
+	iconOverlay = function(self, t, p)
+		local val = p.rest_count or 0
+		if val <= 0 then return "" end
+		local fnt = "buff_font"
+		return tostring(math.ceil(val)), fnt
+	end,
+	doPerservePattern = function(self, t, src, dam)
+		local absorb = dam * t.getPercent(self, t)
+		local paradox = absorb*t.getConversionRatio(self, t)
+		self:setEffect(self.EFF_PRESERVE_PATTERN, t.getDuration(self, t), {paradox=paradox/t.getDuration(self, t), no_ct_effect=true})
+		game:delayedLogMessage(self, nil,  "preserve pattern", "#LIGHT_BLUE##Source# converts damage to paradox!")
+		game:delayedLogDamage(src, self, 0, ("#LIGHT_BLUE#(%d converted)#LAST#"):format(absorb), false)
+		dam = dam - absorb
+		
+		return dam
+	end,
+	activate = function(self, t)
+		game:playSoundNear(self, "talents/arcane")
+
+		local particle
+		if core.shader.active(4) then
+			particle = self:addParticles(Particles.new("shader_shield", 1, {size_factor=1.4, img="runicshield"}, {type="runicshield", shieldIntensity=0.14, ellipsoidalFactor=1, scrollingSpeed=-1, time_factor=12000, bubbleColor={1, 0.5, 0.3, 0.2}, auraColor={1, 0.5, 0.3, 1}}))
+		else
+			particle = self:addParticles(Particles.new("time_shield", 1))
+		end
+
+		return {
+			particle = particle,
+		}
+	end,
+	deactivate = function(self, t, p)
+		self:removeParticles(p.particle)
+		return true
+	end,
+	info = function(self, t)
+		local ratio = t.getPercent(self, t) * 100
+		local absorb = t.getConversionRatio(self, t) * 100
+		local duration = t.getDuration(self, t)
+		return ([[While active, %d%% of all damage you take increases your Paradox by %d%% of the damage absorbed over %d turns.
+		The amount of Paradox damage you recieve will be reduced by your Spellpower.]]):
+		format(ratio, absorb, duration)
+	end,
+}
+
+newTalent{
+	name = "Trim Threads",
+	type = {"chronomancy/fate-threading", 3},
+	require = chrono_req3,
+	points = 5,
 	cooldown = 4,
 	tactical = { ATTACKAREA = { TEMPORAL = 2 } },
 	range = 10,
-	radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.5, 2.5)) end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 290, getParadoxSpellpower(self)) end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end,
+	radius = function(self, t) return math.floor(self:combatTalentScale(t, 1, 2)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 290, getParadoxSpellpower(self, t)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, 4) end,
+	getReduction = function(self, t) return self:getTalentLevel(t) * 2 end,
 	target = function(self, t)
-		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), talent=t}
+		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, nowarning=true, talent=t}
 	end,
 	requires_target = true,
 	direct_hit = true,
@@ -50,60 +142,37 @@ newTalent{
 		self:project(tg, x, y, function(px, py)
 			local target = game.level.map(px, py, Map.ACTOR)
 			if not target then return end
-			target:setEffect(target.EFF_TRIM_THREADS, 3, {power=damage/3, src=self, apply_power=getParadoxSpellpower(self)})
+			target:setEffect(target.EFF_TRIM_THREADS, t.getDuration(self, t), {power=damage/4, src=self, reduction=t.getReduction(self, t), apply_power=getParadoxSpellpower(self, t)})
 		end)
 
 		game.level.map:particleEmitter(x, y, tg.radius, "temporal_flash", {radius=tg.radius})
 
-		game:playSoundNear(self, "talents/dispel")
+		game:playSoundNear(self, "talents/tidalwave")
 
 		return true
 	end,
 	info = function(self, t)
 		local damage = t.getDamage(self, t)
+		local duration = t.getDuration(self, t)
 		local radius = self:getTalentRadius(t)
-		return ([[Deals %0.2f temporal damage over three turns to all targets in a radius of %d.  If the target is hit by an Anomaly the remaining damage will be done instantly.
-		The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), radius)
-	end,
-}
-
-newTalent{
-	name = "Disentangle",
-	type = {"chronomancy/fate-threading", 2},
-	require = chrono_req2,
-	points = 5,
-	cooldown = 12,
-	tactical = { PARADOX = 2 },
-	getReduction = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self)) end,
-	getParadoxMulti = function(self, t) return self:combatTalentLimit(t, 2, 0.10, .75) end,
-	anomaly_type = "no-major",
-	no_energy = true,
-	passives = function(self, t, p)
-		self:talentTemporaryValue(p, "anomaly_paradox_recovery", t.getParadoxMulti(self, t))
-	end,
-	action = function(self, t)
-		local reduction = self:spellCrit(t.getReduction(self, t))
-		self:paradoxDoAnomaly(reduction, t.anomaly_type, "forced")
-		return true
-	end,
-	info = function(self, t)
 		local reduction = t.getReduction(self, t)
-		local paradox = 100 * t.getParadoxMulti(self, t)
-		return ([[Disentangle the timeline, reducing your Paradox by %d and creating an anomaly.  This spell will never produce a major anomaly.
-		Additionally you recover %d%% more Paradox from random anomalies (%d%% total).
-		The Paradox reduction will increase with your Spellpower.]]):format(reduction, paradox, paradox + 200)
+		return ([[Deals %0.2f temporal damage over %d turns to all other targets in a radius of %d.  If the target is slain before the effect expires you'll recover %d Paradox.
+		If the target is hit by an Anomaly the remaining damage will be done instantly.
+		The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration, radius, reduction)
 	end,
 }
 
 newTalent{
 	name = "Bias Weave",
-	type = {"chronomancy/fate-threading", 3},
-	require = chrono_req3,
+	type = {"chronomancy/fate-threading", 4},
+	require = chrono_req4,
 	points = 5,
 	cooldown = 10,
 	-- Anomaly biases can be set manually for monsters
 	-- Use the following format anomaly_bias = { type = "teleport", chance=50}
-	on_pre_use = function(self, t, silent) if not self == game.player then return false end	return true end,
+	no_npc_use = true,  -- so rares don't learn useless talents
+	allow_temporal_clones = true,  -- let clones copy it anyway so they can benefit from the effects
+	on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end,  -- but don't let them cast it
 	getBiasChance = function(self, t) return self:combatTalentLimit(t, 100, 10, 75) end,
 	getTargetChance = function(self, t) return self:combatTalentLimit(t, 100, 10, 75) end,
 	getAnomalySpeed = function(self, t) return self:combatTalentLimit(t, 1, 0.10, .75) end,
@@ -141,72 +210,4 @@ newTalent{
 		Additionally random anomalies only cost you %d%% of a turn rather than a full turn when they occur.
 		Major anomalies, those occuring when your modified Paradox is over 600, are not affected by this talent.]]):format(target_chance, bias_chance, anomaly_recovery)
 	end,
-}
-
-newTalent{
-	name = "Preserve Pattern",
-	type = {"chronomancy/fate-threading", 4},
-	require = chrono_req4,
-	points = 5,
-	mode = "sustained",
-	sustain_paradox = 48,
-	cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 15, 45, 25)) end, -- Limit >15
-	tactical = { DEFEND = 2 },
-	getHeal = function(self, t) return self.max_life * self:combatTalentLimit(t, 1.5, 0.09, 0.4) end, -- Limit < 150% max life (to survive a large string of hits between turns)
-	callbackOnHit = function(self, t, cb)
-		local p = self:isTalentActive(t.id)
-		
-		if p and p.rest_count <= 0 and cb.value >= self.life then
-			-- Save them from the hit and heal up
-			cb.value = 0
-			self.life = 1
-			self:heal(t.getHeal(self, t))
-			
-			-- Make them invulnerable so they don't die to anomalies
-			game.logSeen(self, "#STEEL_BLUE#%s is rapidly shunted into another timeline!#LAST#", self.name:capitalize())
-			local invuln = self.invulnerable
-			self.invulnerable = 1
-			
-			-- Make some anomalies and remove invulnerability
-			for i = 1, rng.avg(3, 6, 3) do self:paradoxDoAnomaly(0, nil, "forced", self, true) end
-			self.invulnerable = invuln
-			
-			-- Set the counter on the sustain
-			p.rest_count = self:getTalentCooldown(t)
-			if self.player then world:gainAchievement("AVOID_DEATH", self) end
-		end
-		
-		return cb.value
-	end,
-	callbackOnActBase = function(self, t)
-		local p = self:isTalentActive(t.id)
-		if p.rest_count > 0 then p.rest_count = p.rest_count - 1 end
-	end,
-	iconOverlay = function(self, t, p)
-		local val = p.rest_count or 0
-		if val <= 0 then return "" end
-		local fnt = "buff_font"
-		return tostring(math.ceil(val)), fnt
-	end,
-	activate = function(self, t)
-		game:playSoundNear(self, "talents/heal")
-		local ret = { rest_count = 0 }
-	--[[if core.shader.active(4) then
-			ret.particle = self:addParticles(Particles.new("shader_ring_rotating", 1, {toback=true, a=0.6, rotation=0, radius=2, img="flamesgeneric"}, {type="sunaura", time_factor=6000}))
-		else
-			ret.particle = self:addParticles(Particles.new("golden_shield", 1))
-		end]]
-		return ret
-	end,
-	deactivate = function(self, t, p)
-	--	self:removeParticles(p.particle)
-		return true
-	end,
-	info = function(self, t)
-		local heal = t.getHeal(self, t)
-		local cooldown = self:getTalentCooldown(t)
-		return ([[Any attack that would drop you below 1 hit point instead triggers Preserve Pattern, setting your life to 1, then healing you for %d.
-		This effect causes three to six anomalies to occur as you move from timeline to timeline until you find one in which you're still alive.
-		These anomalies may not be targeted though they may be biased.  This effect can only occur once every %d turns.]]):format(heal, cooldown)
-	end,
 }
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/chronomancy/fate-weaving.lua b/game/modules/tome/data/talents/chronomancy/fate-weaving.lua
index 4ca142407891f225b00ed4426164c212930d6076..4b94a54eb4d3f3dc7dbc5836b17cde2235bc2e7e 100644
--- a/game/modules/tome/data/talents/chronomancy/fate-weaving.lua
+++ b/game/modules/tome/data/talents/chronomancy/fate-weaving.lua
@@ -26,13 +26,31 @@ newTalent{
 	mode = "passive",
 	points = 5,
 	getSaveBonus = function(self, t) return math.ceil(self:combatTalentScale(t, 2, 8, 0.75)) end,
-	doSpinFate = function(self, t)
-		local save_bonus = t.getSaveBonus(self, t)
-		local resists = self:knowTalent(self.T_FATEWEAVER) and self:callTalent(self.T_FATEWEAVER, "getResist") or 0
-		
-		self:setEffect(self.EFF_SPIN_FATE, 3, {save_bonus=save_bonus, resists=resists, spin=1, max_spin=3})
+	callbackOnTakeDamage = function(self, t, src, x, y, type, dam, tmp, no_martyr)
+		if dam > 0 and src ~= self then
+			if self.turn_procs and not self.turn_procs.spin_fate then
+				
+				self:setEffect(self.EFF_SPIN_FATE, 3, {save_bonus=t.getSaveBonus(self, t), spin=1, max_spin=3})
+				
+				-- Set our turn procs, we do spin_fate last since it's the only one checked above
+				if self.hasEffect and self:hasEffect(self.EFF_WEBS_OF_FATE) and not self.turn_procs.spin_webs then
+					self.turn_procs.spin_webs = true
+				elseif self.hasEffect and self:hasEffect(self.EFF_SEAL_FATE) and not self.turn_procs.spin_seal then
+					self.turn_procs.spin_seal = true
+				else
+					self.turn_procs.spin_fate = true
+				end
+				
+				-- Reduce damage if we know Fateweaver
+				if self:knowTalent(self.T_FATEWEAVER) then
+					local reduction = dam * self:callTalent(self.T_FATEWEAVER, "getReduction")
+					dam = dam - reduction
+					game:delayedLogDamage(src, self, 0, ("%s(%d fatewever)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", reduction), false)
+				end				
+			end
+		end
 		
-		return true
+		return {dam=dam}
 	end,
 	info = function(self, t)
 		local save = t.getSaveBonus(self, t)
@@ -41,7 +59,6 @@ newTalent{
 		format(save, save * 3)
 	end,
 }
-
 newTalent{
 	name = "Webs of Fate",
 	type = {"chronomancy/fate-weaving", 2},
@@ -50,15 +67,16 @@ newTalent{
 	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
 	cooldown = 12,
 	tactical = { BUFF = 2, CLOSEIN = 2, ESCAPE = 2 },
-	getPower = function(self, t) return self:combatTalentSpellDamage(t, 20, 100, getParadoxSpellpower(self))/100 end,
-	getDuration = function(self, t) return 5 end,
+	getPower = function(self, t) return paradoxTalentScale(self, t, 15, 30, 50)/100 end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, 5) end,
+	no_energy = true,
 	action = function(self, t)
 		local effs = {}
 			
 		-- Find all pins
 		for eff_id, p in pairs(self.tmp) do
 			local e = self.tempeffect_def[eff_id]
-			if e.subtype.pin then
+			if e.subtype.pin or e.subtype.stun then
 				effs[#effs+1] = {"effect", eff_id}
 			end
 		end
@@ -73,27 +91,23 @@ newTalent{
 		end
 		
 		-- Set our power based on current spin
-		local move = t.getPower(self, t)
-		local pin = t.getPower(self, t)/2
+		local imm = t.getPower(self, t)
 		local eff = self:hasEffect(self.EFF_SPIN_FATE)
 		if eff then 
-			move = move * (1 + eff.spin/3)
-			pin = pin * (1 + eff.spin/3)
+			imm = imm * (1 + eff.spin/3)
 		end
-		pin = math.min(1, pin) -- Limit 100%
 		
-		self:setEffect(self.EFF_WEBS_OF_FATE, t.getDuration(self, t), {move=move, pin=pin})
+		self:setEffect(self.EFF_WEBS_OF_FATE, t.getDuration(self, t), {imm=imm})
 		
 		return true
 	end,
 	info = function(self, t)
 		local power = t.getPower(self, t) * 100
 		local duration = t.getDuration(self, t)
-		return ([[Activate to remove pins.  You also gain %d%% movement speed and %d%% pin immunity for %d turns.
-		If you have Spin Fate active these bonuses will be increased by 33%% per spin (up to a maximum of %d%% and %d%% respectively).
-		This spell will automatically cast when you're hit by most anomalies.  This will not consume a turn or put the spell on cooldown.
-		While Webs of Fate is active you may gain one additional spin per turn.
-		These bonuses will scale with your Spellpower.]]):format(power, math.min(100, power/2), duration, power * 2, math.min(100, power/2 * 2))
+		return ([[Activate to remove pins and stuns.  You also gain %d%% pin and stun immunity for %d turns.
+		If you have Spin Fate active these bonuses will be increased by 33%% per spin (up to a maximum of %d%%).
+		While Webs of Fate is active you may gain one additional spin per turn.  These bonuses will scale with your Spellpower.]])
+		:format(power, duration, power * 2)
 	end,
 }
 
@@ -103,11 +117,12 @@ newTalent{
 	require = chrono_req3,
 	mode = "passive",
 	points = 5,
-	getResist = function(self, t) return self:combatTalentScale(t, 2, 8, 0.75) end,
+	getReduction = function(self, t) return paradoxTalentScale(self, t, 10, 30, 40)/100 end,
 	info = function(self, t)
-		local resist = t.getResist(self, t)
-		return ([[You now gain %0.1f%% resist all when you gain spin with Spin Fate (up to a maximum of %0.1f%% resist all at three spin).]]):
-		format(resist, resist*3)
+		local reduction = t.getReduction(self, t)*100
+		return ([[When Spin Fate is triggered you reduce the triggering damage by %d%%.
+		This effect scales with your Spellpower.]]):
+		format(reduction)
 	end,
 }
 
@@ -119,27 +134,18 @@ newTalent{
 	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
 	cooldown = 24,
 	tactical = { BUFF = 2 },
-	getPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 25, getParadoxSpellpower(self)) end,
-	getDuration = function(self, t) return 5 end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, 5) end,
+	getProcs = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5)) end,
+	no_energy = true,
 	action = function(self, t)
-		-- Set our power based on current spin
-		local crits = t.getPower(self, t)
-		local eff = self:hasEffect(self.EFF_SPIN_FATE)
-		if eff then 
-			crits = crits * (1 + eff.spin/3)
-		end
-			
-		self:setEffect(self.EFF_SEAL_FATE, t.getDuration(self, t), {crit=crits})
-		
+		self:setEffect(self.EFF_SEAL_FATE, t.getDuration(self, t), {procs=t.getProcs(self, t)})
 		return true
 	end,
 	info = function(self, t)
-		local power = t.getPower(self, t)
+		local procs = t.getProcs(self, t)
 		local duration = t.getDuration(self, t)
-		return ([[Activate to increase critical hit chance and critical damage by %d%% for five turns.
-		If you have Spin Fate active these bonuses will be increased by 33%% per spin (up to a maximum of %d%%).
-		This spell will automatically cast when you're hit by most anomalies.  This will not consume a turn or put the spell on cooldown.
-		While Seal Fate is active you may gain one additional spin per turn.
-		These bonuses will scale with your Spellpower.]]):format(power, power * 2)
+		return ([[Activate to Seal Fate for %d turns.  When you damage a target while Seal Fate is active you have a 50%% chance to increase the duration of one detrimental status effect on it by one turn.
+		If you have Spin Fate active the chance twill be increased by 33%% per Spin (to a maximum of 100%% at three Spin.)
+		This can occur at most %d times per turn.  While Seal Fate is active you may gain one additional spin per turn.]]):format(duration, procs)
 	end,
 }
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/chronomancy/gravity.lua b/game/modules/tome/data/talents/chronomancy/gravity.lua
index f76e1f7f77240ff1d318a43f1b3e45a5fe55b53e..37c7e8861290cd7c33477e59e0745efed6a76a30 100644
--- a/game/modules/tome/data/talents/chronomancy/gravity.lua
+++ b/game/modules/tome/data/talents/chronomancy/gravity.lua
@@ -17,27 +17,71 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
+-- EDGE TODO: Particles, Timed Effect Particles
+
 newTalent{
 	name = "Repulsion Blast",
 	type = {"chronomancy/gravity",1},
 	require = chrono_req1,
 	points = 5,
 	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
-	cooldown = 4,
+	cooldown = 3,
 	tactical = { ATTACKAREA = {PHYSICAL = 2}, ESCAPE = 2 },
 	range = 0,
 	radius = function(self, t) return math.floor(self:combatTalentScale(t, 4.5, 6.5)) end,
 	requires_target = true,
 	direct_hit = true,
 	target = function(self, t)
-		return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), friendlyfire=false, talent=t}
+		return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t}
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 170, getParadoxSpellpower(self)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.REPULSION, self:spellCrit(t.getDamage(self, t)))
+		
+		-- Project our base damage		
+		local dam = self:spellCrit(t.getDamage(self, t))
+		local bonus_dam = dam/2
+		self:project(tg, x, y, DamageType.GRAVITY, dam)
+		
+		-- Do our knockback
+		local tgts = {}
+		local grids = self:project(tg, x, y, function(px, py)
+			local target = game.level.map(px, py, Map.ACTOR)
+			if target and not target:isTalentActive(target.T_GRAVITY_LOCUS) then
+				-- If we've already moved this target don't move it again
+				for _, v in pairs(tgts) do
+					if v == target then
+						return
+					end
+				end
+				
+				local hit = target:checkHit(getParadoxSpellpower(self, t), target:combatPhysicalResist(), 0, 95) and target:canBe("knockback")
+					
+				if hit then
+					local slam = false
+					local dist = self:getTalentRadius(t) + 1 - core.fov.distance(self.x, self.y, px, py)
+					target:knockback(self.x, self.y, dist, false, function(g, x, y)
+						-- Deal our bonus damage
+						if game.level.map:checkAllEntities(x, y, "block_move", target) then
+							slam = true
+							self:project({type="hit"}, target.x, target.y, DamageType.GRAVITY, bonus_dam)
+							self:project({type="hit"}, x, y, DamageType.GRAVITY, bonus_dam)
+							game.logSeen(target, "%s slams into something solid!", target.name:capitalize())
+						end
+					end)
+					
+					tgts[#tgts+1] = target
+					if not slam then game.logSeen(target, "%s is knocked back!", target.name:capitalize()) end
+					target:crossTierEffect(target.EFF_OFFBALANCE, getParadoxSpellpower(self, t))
+				else
+					game.logSeen(target, "%s resists the knockback!", target.name:capitalize())
+				end
+				
+			end
+		end)
+		
 		game.level.map:particleEmitter(self.x, self.y, tg.radius, "gravity_breath", {radius=tg.radius, tx=x-self.x, ty=y-self.y, allow=core.shader.allow("distort")})
 		game:playSoundNear(self, "talents/earth")
 		return true
@@ -45,9 +89,9 @@ newTalent{
 	info = function(self, t)
 		local damage = t.getDamage(self, t)
 		local radius = self:getTalentRadius(t)
-		return ([[Sends out a blast wave of gravity in a %d radius cone, dealing %0.2f physical damage and knocking back creatures caught in the area.  This deals 50%% extra damage to pinned targets, in addition to the knockback.
-		The blast wave may hit targets more then once, depending on the radius and the knockback effect.
-		The damage will scale with your Spellpower.]]):
+		return ([[Sends out a blast wave of gravity in a radius %d cone, dealing %0.2f base physical damage and knocking back targets caught in the area.
+		Targets knocked into walls or other targets take 50%% additional damage and deal 50%% damage to targets they're knocked into.
+		Closer targets will be knocked back further and the damage will scale with your Spellpower.]]):
 		format(radius, damDesc(self, DamageType.PHYSICAL, t.getDamage(self, t)))
 	end,
 }
@@ -58,31 +102,61 @@ newTalent{
 	require = chrono_req2,
 	points = 5,
 	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
-	cooldown = 6,
+	cooldown = 4,
 	tactical = { ATTACKAREA = {PHYSICAL = 2}, DISABLE = 2 },
 	range = 10,
 	radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.3, 3.7)) end,
 	direct_hit = true,
 	requires_target = true,
 	target = function(self, t)
-		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)}
+		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), talent=t}
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 170, getParadoxSpellpower(self)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 290, getParadoxSpellpower(self, t)) end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
 		local _ _, _, _, x, y = self:canProject(tg, x, y)
-		local grids = self:project(tg, x, y, function(px, py)
+		
+		local tgts = {}
+		self:project(tg, x, y, function(px, py)
 			local target = game.level.map(px, py, Map.ACTOR)
-			if not target then return end
-			local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true})
-			if tx and ty and target:canBe("knockback") then
-				target:move(tx, ty, true)
-				game.logSeen(target, "%s is drawn in by the gravity spike!", target.name:capitalize())
+			if target and not target:isTalentActive(target.T_GRAVITY_LOCUS) then
+				-- If we've already moved this target don't move it again
+				for _, v in pairs(tgts) do
+					if v == target then
+						return
+					end
+				end
+
+				-- Do our Knockback
+				local can = function(target)
+					if target:checkHit(getParadoxSpellpower(self, t), target:combatPhysicalResist(), 0, 95) and target:canBe("knockback") then -- Deprecated Checkhit call
+						return true
+					else
+						game.logSeen(target, "%s resists the knockback!", target.name:capitalize())
+					end
+				end
+				if can(target) then
+					target:pull(x, y, tg.radius, can)
+					tgts[#tgts+1] = target
+					game.logSeen(target, "%s is drawn in by the singularity!", target.name:capitalize())
+					target:crossTierEffect(target.EFF_OFFBALANCE, getParadoxSpellpower(self, t))
+				end
 			end
 		end)
-		self:project(tg, x, y, DamageType.GRAVITY, self:spellCrit(t.getDamage(self, t)))
+		
+		-- 25% bonus damage per target beyond the first
+		local dam = self:spellCrit(t.getDamage(self, t))
+		dam = dam + math.min(dam, dam*(#tgts-1)/4)
+		
+		-- Project our damage last based on number of targets hit
+		self:project(tg, x, y, function(px, py)
+			local dist_factor = 1 + (core.fov.distance(x, y, px, py)/5)
+			local damage = dam/dist_factor
+			DamageType:get(DamageType.GRAVITY).projector(self, px, py, DamageType.GRAVITY, damage)
+		end)
+		
 		game.level.map:particleEmitter(x, y, tg.radius, "gravity_spike", {radius=tg.radius, allow=core.shader.allow("distort")})
 
 		game:playSoundNear(self, "talents/earth")
@@ -91,53 +165,49 @@ newTalent{
 	info = function(self, t)
 		local damage = t.getDamage(self, t)
 		local radius = self:getTalentRadius(t)
-		return ([[Creates a gravity spike in a radius of %d that moves all targets towards the spells center and inflicts %0.2f physical damage.  This deals 50%% extra damage to pinned targets, in addition to the knockback.
-		The damage dealt will scale with your Spellpower.]]):format(radius, damDesc(self, DamageType.PHYSICAL, t.getDamage(self, t)))
+		return ([[Creates a gravity spike in a radius of %d that moves all targets towards the spell's center and inflicts %0.2f physical damage.
+		Each target moved beyond the first deals an additional %0.2f physical damage (up to %0.2f bonus damage).
+		Targets take reduced damage the further they are from the epicenter (20%% less per tile).
+		The damage dealt will scale with your Spellpower.]])
+		:format(radius, damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.PHYSICAL, damage/4), damDesc(self, DamageType.PHYSICAL, damage))
 	end,
 }
 
 newTalent{
-	name = "Repulsion Field",
+	name = "Gravity Locus",
 	type = {"chronomancy/gravity",3},
 	require = chrono_req3,
+	mode = "sustained",
+	sustain_paradox = 24,
+	cooldown = 10,
+	tactical = { BUFF = 2 },
 	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 30) end,
-	cooldown = 14,
-	tactical = { ATTACKAREA = {PHYSICAL = 2}, ESCAPE = 2 },
-	range = 0,
-	radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.5, 3.5)) end,
-	target = function(self, t)
-		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false}
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t)) end,
+	getSlow = function(self, t) return paradoxTalentScale(self, t, 20, 50, 80) end,
+	callbackOnMeleeHit = function(self, t, target)
+		if not self.dead and self:isTalentActive(self.T_GRAVITY_LOCUS) then
+			self:project({type="hit", talent=t}, target.x, target.y, DamageType.GRAVITY, {dam=t.getDamage(self, t), anti=true, dur=2, apply=getParadoxSpellpower(self, t)})
+		end
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 8, 80, getParadoxSpellpower(self)) end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end,
-	direct_hit = true,
-	requires_target = true,
-	action = function(self, t)
-		local tg = self:getTalentTarget(t)
-		game.level.map:addEffect(self,
-			self.x, self.y, t.getDuration(self, t),
-			DamageType.REPULSION, t.getDamage(self, t),
-			tg.radius,
-			5, nil,
-			engine.MapEffect.new{color_br=200, color_bg=120, color_bb=0, effect_shader="shader_images/paradox_effect.png"},
-			function(e)
-				e.x = e.src.x
-				e.y = e.src.y
-				return true
-			end,
-			tg.selffire
-		)
-		game:playSoundNear(self, "talents/cloud")
+	activate = function(self, t)
+		game:playSoundNear(self, "talents/heal")
+		local particle = Particles.new("ultrashield", 1, {rm=204, rM=220, gm=102, gM=120, bm=0, bM=0, am=35, aM=90, radius=0.5, density=10, life=28, instop=100})
+		return {
+			proj = self:addTemporaryValue("slow_projectiles", t.getSlow(self, t)),
+			particle = self:addParticles(particle)
+		}
+	end,
+	deactivate = function(self, t, p)
+		self:removeTemporaryValue("slow_projectiles", p.proj)
+		self:removeParticles(p.particle)
 		return true
 	end,
 	info = function(self, t)
 		local damage = t.getDamage(self, t)
-		local duration = t.getDuration(self, t)
-		local radius = self:getTalentRadius(t)
-		return ([[You surround yourself with a radius %d distortion of gravity, knocking back and dealing %0.2f physical damage to all creatures inside it.  The effect lasts %d turns.  Deals 50%% extra damage to pinned targets, in addition to the knockback.
-		The blast wave may hit targets more then once, depending on the radius and the knockback effect.
-		The damage will scale with your Spellpower.]]):format(radius, damDesc(self, DamageType.PHYSICAL, damage), duration)
+		local proj = t.getSlow(self, t)
+		return ([[Create a gravity field around you that slows incoming projectiles by %d%% and protects you from all gravity effects.
+		While this spell is active creatures that hit you in melee combat will take %0.2f physical damage and have their knockback resistance reduced by half for two turns.
+		The projectile slowing and damage will scale with your spellpower.]]):format(proj, damDesc(self, DamageType.PHYSICAL, damage))
 	end,
 }
 
@@ -146,31 +216,31 @@ newTalent{
 	type = {"chronomancy/gravity", 4},
 	require = chrono_req4,
 	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 40) end,
-	cooldown = 24,
+	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
+	cooldown = 12,
 	tactical = { ATTACKAREA = {PHYSICAL = 2}, DISABLE = 2 },
 	range = 10,
 	radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4.5)) end,
 	direct_hit = true,
 	requires_target = true,
 	target = function(self, t)
-		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)}
+		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), talent=t}
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self)) end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self, t)) end,
+	getSlow = function(self, t) return paradoxTalentScale(self, t, 20, 50, 80)/100 end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 4, 8))) end,
 	action = function(self, t)
-		local duration = t.getDuration(self,t)
-		local radius = self:getTalentRadius(t)
-		local dam = t.getDamage(self, t)
 		local tg = self:getTalentTarget(t)
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
 		local _ _, _, _, x, y = self:canProject(tg, x, y)
+
 		-- Add a lasting map effect
+		local dam = self:spellCrit(t.getDamage(self, t))
 		game.level.map:addEffect(self,
-			x, y, duration,
-			DamageType.GRAVITYPIN, dam,
-			radius,
+			x, y, t.getDuration(self,t),
+			DamageType.GRAVITY, {dam=dam, dur=1, slow=t.getSlow(self, t), apply=getParadoxSpellpower(self, t)},
+			self:getTalentRadius(t),
 			5, nil,
 			{type="gravity_well"},
 			nil, self:spellFriendlyFire()
@@ -182,7 +252,8 @@ newTalent{
 		local damage = t.getDamage(self, t)
 		local duration = t.getDuration(self, t)
 		local radius = self:getTalentRadius(t)
-		return ([[Increases local gravity of an area, doing %0.2f physical damage with a chance to pin in a radius of %d each turn for %d turns.
-		The damage done will scale with your Spellpower.]]):format(damDesc(self, DamageType.PHYSICAL, damage), radius, duration)
+		local slow = t.getSlow(self, t)
+		return ([[Increases local gravity in a radius of %d for %d turns, dealing %0.2f physical damage and slowing the movement speed of all affected targets by %d%%.
+		The damage done will scale with your Spellpower.]]):format(radius, duration, damDesc(self, DamageType.PHYSICAL, damage), slow*100)
 	end,
 }
diff --git a/game/modules/tome/data/talents/chronomancy/guardian.lua b/game/modules/tome/data/talents/chronomancy/guardian.lua
index c6ce9167dbd812a8efc6fbcfbec665d24ffe0144..26f1b68d3d3e2c1ce23f726533a812366b4e85c0 100644
--- a/game/modules/tome/data/talents/chronomancy/guardian.lua
+++ b/game/modules/tome/data/talents/chronomancy/guardian.lua
@@ -17,7 +17,7 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
--- EDGE TODO: Icons, Particles, Timed Effect Particles
+-- EDGE TODO: Particles, Timed Effect Particles
 
 newTalent{
 	name = "Strength of Purpose",
@@ -46,9 +46,8 @@ newTalent{
 	cooldown = 24,
 	fixed_cooldown = true,
 	tactical = { HEAL = 1 },
-	getDuration = function(self, t) return math.floor(self:combatTalentLimit(self:getTalentLevel(t), 14, 4, 8)) end, -- Limit < 14
-	getPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)) end,
-	getNumber = function(self, t) return math.ceil(self:getTalentLevel(t)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentLimit(self:getTalentLevel(t), 14, 4, 8))) end, -- Limit < 14
+	getPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t)) end,
 	action = function(self, t)
 		self:setEffect(self.EFF_INVIGORATE, t.getDuration(self,t), {power=t.getPower(self, t)})
 		return true
@@ -67,24 +66,24 @@ newTalent{
 	require = chrono_req3,
 	points = 5,
 	mode = "passive",
-	getSplit = function(self, t) return math.min(100, self:combatTalentSpellDamage(t, 20, 50, getParadoxSpellpower(self)))/100 end,
-	getDuration = function(self, t) return 2 end,
+	getSplit = function(self, t) return paradoxTalentScale(self, t, 20, 50, 80)/100 end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, 2) end,
 	getLifeTrigger = function(self, t) return self:combatTalentLimit(t, 10, 40, 24)	end,
 	remove_on_clone = true,
 	callbackOnHit = function(self, t, cb, src)
 		local split = cb.value * t.getSplit(self, t)
 	
 		-- If we already split this turn pass damage to our clone
-		if self.turn_procs.double_edge and self.turn_procs.double_edge ~= self and not self.turn_procs.double_edge.dead then
+		if self.turn_procs.double_edge and self.turn_procs.double_edge ~= self and game.level:hasEntity(self.turn_procs.double_edge) then
 			split = split/2
 			-- split the damage
-			game:delayedLogDamage(src, self.turn_procs.double_edge, split, ("#PINK#%d displaced#LAST#"):format(split), false)
+			game:delayedLogDamage(src, self.turn_procs.double_edge, split, ("#STEEL_BLUE#(%d shared)#LAST#"):format(split), nil)
 			cb.value = cb.value - split
 			self.turn_procs.double_edge:takeHit(split, src)
 		end
 			
 		-- Do our split
-		if self.max_life and cb.value >= self.max_life * t.getLifeTrigger(self, t) * 0.01 and not self.turn_procs.double_edge then
+		if self.max_life and cb.value >= self.max_life * (t.getLifeTrigger(self, t)/100) and not self.turn_procs.double_edge then
 			-- Look for space first
 			local tx, ty = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true})
 			if tx and ty then
@@ -93,17 +92,6 @@ newTalent{
 				-- clone our caster
 				local m = makeParadoxClone(self, self, t.getDuration(self, t))
 				
-				-- remove some talents; note most of this is handled by makeParadoxClone already but we don't want to keep splitting
-				local tids = {}
-				for tid, _ in pairs(m.talents) do
-					local t = m:getTalentFromId(tid)
-					if t.remove_on_clone then tids[#tids+1] = t end
-				end
-				for i, t in ipairs(tids) do
-					if t.mode == "sustained" and m:isTalentActive(t.id) then m:forceUseTalent(t.id, {ignore_energy=true, silent=true}) end
-					m.talents[t.id] = nil
-				end
-				
 				-- add our clone
 				game.zone:addEntity(game.level, m, "actor", tx, ty)
 				m.ai_state = { talent_in=2, ally_compassion=10 }
@@ -125,7 +113,8 @@ newTalent{
 				self.turn_procs.double_edge = m
 				m:takeHit(split, src)
 				m:setTarget(src or nil)
-				game:delayedLogDamage(src, m, split, ("#PINK#%d displaced#LAST#"):format(split), false)
+				game:delayedLogMessage(self, nil, "guardian_damage", "#STEEL_BLUE##Source# shares damage with %s guardian!", string.his_her(self))
+				game:delayedLogDamage(src or self, self, 0, ("#STEEL_BLUE#(%d shared)#LAST#"):format(split), nil)
 												
 			else
 				game.logPlayer(self, "Not enough space to summon warden!")
@@ -154,9 +143,9 @@ newTalent{
 	tactical = { ATTACK = {weapon = 2}, DISABLE = 3 },
 	requires_target = true,
 	range = archery_range,
-	no_energy = "fake",
+	speed = function(self, t) return self:hasArcheryWeapon("bow") and "archery" or "weapon" end,
 	getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.5) end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end,
 	on_pre_use = function(self, t, silent) if self:attr("disarmed") then if not silent then game.logPlayer(self, "You require a weapon to use this talent.") end return false end return true end,
 	archery_onhit = function(self, t, target, x, y)
 		target:setEffect(target.EFF_BREACH, t.getDuration(self, t), {})
@@ -166,19 +155,19 @@ newTalent{
 		
 		if self:hasArcheryWeapon("bow") then
 			-- Ranged attack
-			local targets = self:archeryAcquireTargets(nil, {one_shot=true})
+			local targets = self:archeryAcquireTargets({type="bolt"}, {one_shot=true, no_energy = true})
 			if not targets then return end
-			self:archeryShoot(targets, t, nil, {mult=t.getDamage(self, t)})
+			self:archeryShoot(targets, t, {type="bolt"}, {mult=t.getDamage(self, t)})
 		elseif mainhand then
 			-- Melee attack
 			local tg = {type="hit", range=self:getTalentRange(t), talent=t}
 			local x, y, target = self:getTarget(tg)
 			if not x or not y or not target then return nil end
 			if core.fov.distance(self.x, self.y, x, y) > 1 then return nil end
-			local hitted = self:attackTarget(target, nil, t.getDamage(self, t))
+			local hitted = self:attackTarget(target, nil, t.getDamage(self, t), true)
 
 			if hitted then
-				target:setEffect(target.EFF_BREACH, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self)})
+				target:setEffect(target.EFF_BREACH, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self, t)})
 			end
 		else
 			game.logPlayer(self, "You cannot use Breach without an appropriate weapon!")
diff --git a/game/modules/tome/data/talents/chronomancy/induced-phenomena.lua b/game/modules/tome/data/talents/chronomancy/induced-phenomena.lua
new file mode 100644
index 0000000000000000000000000000000000000000..0c094ad7b3de06add0ac76148c735a621286d749
--- /dev/null
+++ b/game/modules/tome/data/talents/chronomancy/induced-phenomena.lua
@@ -0,0 +1,339 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 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
+
+-- EDGE TODO: Particles, Timed Effect Particles
+
+newTalent{
+	name = "Cosmic Cycle",
+	type = {"chronomancy/induced-phenomena", 1},
+	require = chrono_req_high1,
+	points = 5,
+	sustain_paradox = 36,
+	mode = "sustained",
+	no_sustain_autoreset = true,
+	cooldown = 12,
+	tactical = { BUFF = 2},
+	range = 0,
+	radius = function(self, t) local p=self:isTalentActive(self.T_COSMIC_CYCLE) return p and p.radius or 0 end,
+	target = function(self, t)
+		return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false, talent=t}
+	end,
+	iconOverlay = function(self, t, p)
+		local val = p.radius or 0
+		if val <= 0 then return "" end
+		local fnt = "buff_font"
+		return tostring(math.ceil(val)), fnt
+	end,
+	getWillMultiplier = function(self, t) return self:combatTalentLimit(t, 100, 17, 50)/100 end, --Limit < 100%
+	getResistPen = function(self, t) return self:combatTalentLimit(t, 100, 17, 50) end, --Limit < 100%
+	callbackOnActBase = function(self, t)
+		local p = self:isTalentActive(self.T_COSMIC_CYCLE)
+		if not p then return end
+		p.old_tgts = p.new_tgts
+		p.new_tgts = {}
+		
+		-- Update our target table
+		self:project(self:getTalentTarget(t), self.x, self.y, function(px, py)
+			local target = game.level.map(px, py, Map.ACTOR)
+			if not target then return end
+			p.new_tgts[#p.new_tgts+1] = target
+		end)
+
+		if self:knowTalent(self.T_REVERSE_CAUSALITY) then
+			self:callTalent(self.T_REVERSE_CAUSALITY, "doReverseCausality")
+		end
+		
+		if self:knowTalent(self.T_EPOCH) then
+			if p.mode == "expansion" then
+				self:callTalent(self.T_EPOCH, "doAging")
+			else
+				self:callTalent(self.T_EPOCH, "doRegression")
+			end
+		end
+	end,
+	doExpansion = function(self, t, radius)
+		local p = self:isTalentActive(self.T_COSMIC_CYCLE)
+		
+		-- Update Radius
+		p.radius = radius or p.radius
+		self:removeParticles(p.particle)
+		p.particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, a=55, appear=0, speed=0.2, img="cycle_expansion", radius=p.radius}))
+		
+		if p.mode ~= "expansion" then
+			-- change mode
+			p.mode = "expansion"
+			
+			-- Update temporary values
+			self:removeTemporaryValue("paradox_will_multi", p.will)
+			p.resist = self:addTemporaryValue("resists_pen", {[DamageType.TEMPORAL] = t.getResistPen(self, t)})
+		end
+		
+		game.logPlayer(self, "#LIGHT_BLUE#Your cosmic cycle expands.")
+		
+	end,
+	doContraction = function(self, t, radius)
+		local p = self:isTalentActive(self.T_COSMIC_CYCLE)
+		
+		-- Change radius and particles
+		p.radius = radius or p.radius
+		self:removeParticles(p.particle)
+		p.particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, a=55, appear=0, speed=-0.2, img="cycle_contraction", radius=p.radius}))
+		
+		if p.mode ~= "contraction" then
+			p.mode = "contraction"
+
+			-- Update temporary values
+			self:removeTemporaryValue("resists_pen", p.resist)
+			p.will = self:addTemporaryValue("paradox_will_multi", t.getWillMultiplier(self, t))
+		end
+		
+		game.logPlayer(self, "#LIGHT_RED#Your cosmic cycle contracts.")
+		
+	end,
+	doCosmicCycle = function(self, t)
+		game:onTickEnd(function()
+			local p = self:isTalentActive(self.T_COSMIC_CYCLE)
+			if not p then return end
+			local radius = math.floor(self:getParadox()/100)
+			
+			-- Cycle between expansion and contraction
+			if radius > p.radius then
+				t.doExpansion(self, t, radius)
+			elseif radius < p.radius then
+				t.doContraction(self, t, radius)
+			end
+		end)
+	end,
+	activate = function(self, t)
+		local radius = math.floor(self:getParadox()/100)
+		local will = self:addTemporaryValue("paradox_will_multi", 0)
+		local resist =  self:addTemporaryValue("resists_pen", {[DamageType.TEMPORAL] = t.getResistPen(self, t)})
+		
+		local ret ={
+			mode = "expansion", radius = radius, new_tgts ={}, old_tgts ={}, resist = resist, will = will,
+			particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, a=55, appear=0, speed=0.2, img="cycle_expansion", radius=radius}))
+		}
+		game:playSoundNear(self, "talents/arcane")
+		return ret
+	end,
+	deactivate = function(self, t, p)
+		self:removeParticles(p.particle)
+		self:removeTemporaryValue("resists_pen", p.resist)
+		self:removeTemporaryValue("paradox_will_multi", p.will)
+		return true	
+	end,
+	info = function(self, t)
+		local paradox = t.getResistPen(self, t)
+		local will = t.getWillMultiplier(self, t) * 100
+		return ([[Tune yourself into the ebb and flow of spacetime.  When your Paradox crosses a 100 point threshold, your Cosmic Cycle gains or loses one radius.
+		While Cosmic Cycle is expanding, your temporal resistance penetration will be increased by %d%%.  While it's contracting, your Willpower for Paradox calculations will be increased by %d%%.]]):
+		format(paradox, will)
+	end,
+}
+
+newTalent{
+	name = "Polarity Shift",
+	type = {"chronomancy/induced-phenomena",2},
+	require = chrono_req_high2,
+	points = 5,
+	cooldown = 6,
+	tactical = { BUFF = 2, ATTACKAREA = {TEMPORAL = 2} },
+	direct_hit = true,
+	requires_target = true,
+	on_pre_use = function(self, t, silent) if not self:isTalentActive(self.T_COSMIC_CYCLE) then if not silent then game.logPlayer(self, "You must have Cosmic Cycle active to use this talent.") end return false end return true end,
+	radius = function(self, t) local p=self:isTalentActive(self.T_COSMIC_CYCLE) return p and p.radius or 0 end,
+	target = function(self, t)
+		return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false, friendlyfire=false, talent=t}
+	end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 100) end,
+	getBraid = function(self, t) return self:combatTalentSpellDamage(t, 25, 50, getParadoxSpellpower(self, t)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end,
+	action = function(self, t)
+		local p = self:isTalentActive(self.T_COSMIC_CYCLE)
+		local tg = self:getTalentTarget(t)
+		
+		if p.mode == "contraction" then
+			self:callTalent(self.T_COSMIC_CYCLE, "doExpansion")
+			
+			local dam = self:spellCrit(t.getDamage(self, t))
+			self:project(tg, self.x, self.y, function(tx, ty)
+				local target = game.level.map(tx, ty, engine.Map.ACTOR)
+				if not target then return end
+
+				local proj = require("mod.class.Projectile"):makeHoming(
+						self,
+						{particle="arrow", particle_args={tile=("particles_images/alt_temporal_bolt_0%d"):format(rng.range(1, 7)), proj_x=tx, proj_y=ty, src_x=self.x, src_y=self.y},  trail="trail_paradox"},
+						{speed=1, name="Polarity Bolt", dam=dam, movedam=dam},
+						target,
+						self:getTalentRadius(t),
+						function(self, src)
+							local DT = require("engine.DamageType")
+							DT:get(DT.TEMPORAL).projector(src, self.x, self.y, DT.TEMPORAL, self.def.movedam)
+						end,
+						function(self, src, target)
+							local DT = require("engine.DamageType")
+							DT:get(DT.TEMPORAL).projector(src, self.x, self.y, DT.TEMPORAL, self.def.movedam)
+						end
+					)
+				game.zone:addEntity(game.level, proj, "projectile", self.x, self.y)
+			end)
+
+		else
+			self:callTalent(self.T_COSMIC_CYCLE, "doContraction")
+
+			-- Get our targets
+			local braid_targets = {}
+			self:project(tg, self.x, self.y, function(tx, ty)
+				local target = game.level.map(tx, ty, engine.Map.ACTOR)
+				if target then
+					braid_targets[#braid_targets+1] = target
+				end
+			end)
+			
+			-- if we hit more than one, braid them
+			if #braid_targets > 1 then
+				for i = 1, #braid_targets do
+					local target = braid_targets[i]
+					target:setEffect(target.EFF_BRAIDED, t.getDuration(self, t), {power=t.getBraid(self, t), src=self, targets=braid_targets})
+				end
+			end
+		end
+		
+		game:playSoundNear(self, "talents/spell_generic")
+		return true
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)
+		local braid = t.getBraid(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[Reverses the polarity of your Cosmic Cycle.  If it's currently contracting, it will begin to expand, firing a homing missile at each target within the radius that deals %0.2f temporal damage.
+		If it's currently expanding, it will begin to contract, braiding the lifelines of all targets within the radius for %d turns.  Braided targets take %d%% of all damage dealt to other braided targets.
+		The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration, braid)
+	end,
+}
+
+newTalent{
+	name = "Reverse Causality",
+	type = {"chronomancy/induced-phenomena", 3},
+	require = chrono_req_high3,
+	points = 5,
+	mode = "passive",
+	getHeal = function(self, t) return self:combatTalentSpellDamage(t, 15, 70, getParadoxSpellpower(self, t)) end,
+	getReduction= function(self, t) return math.floor(self:combatTalentScale(t, 1, 5)) end,
+	doReverseCausality = function(self, t)
+		local p = self:isTalentActive(self.T_COSMIC_CYCLE)
+		if not p then return end
+		
+		local keys = table.compareKeys(p.new_tgts, p.old_tgts)
+		if p.mode == "expansion" then
+			local heal = self:spellCrit(t.getHeal(self, t))
+			heal = heal * #keys.left
+			if heal > 0 then
+				self:heal(heal, self)
+			end
+		else
+			local effs = {}
+			for eff_id, p in pairs(self.tmp) do
+				local e = self.tempeffect_def[eff_id]
+				if e.type ~= "other" and e.status == "detrimental" then
+					effs[#effs+1] = p
+				end
+			end
+			
+			for i=1, #keys.right do
+				local eff = effs[i]
+				if not eff then break end
+				eff.dur = eff.dur - t.getReduction(self, t)
+				if eff.dur <= 0 then
+					self:removeEffect(eff.effect_id)
+					effs[i] = nil
+				end
+			end
+		end
+			
+	end,
+	info = function(self, t)
+		local heal = t.getHeal(self, t)
+		local reduction = t.getReduction(self, t)
+		return ([[When a creature enters your expanding Cosmic Cycle, you heal %d life at the start of your next turn.
+		When a creature leaves your contracting Cosmic Cycle, you reduce the duration of one detrimental effect on you by %d at the start of your next turn.
+		The healing will scale with your Spellpower.]]):format(heal, reduction)
+	end,
+}
+
+newTalent{
+	name = "Epoch",
+	type = {"chronomancy/induced-phenomena", 4},
+	require = chrono_req_high4,
+	points = 5,
+	mode = "passive",
+	radius = function(self, t) local p=self:isTalentActive(self.T_COSMIC_CYCLE) return p and p.radius or 0 end,
+	target = function(self, t)
+		return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false, friendlyfire=false, talent=t}
+	end,
+	getRegression = function(self, t) return self:combatTalentSpellDamage(t, 5, 25, getParadoxSpellpower(self, t)) end,
+	getAgingChance = function(self, t) return paradoxTalentScale(self, t, 15, 50, 100) end,
+	doAging = function(self, t)
+		local tg = self:getTalentTarget(t)
+		
+		self:project(self:getTalentTarget(t), self.x, self.y, function(px, py)
+			local target = game.level.map(px, py, Map.ACTOR)
+			if not target then return end
+			
+			if rng.percent(t.getAgingChance(self, t)) then
+				local effect = rng.range(1, 3)
+				if effect == 1 then
+					if target:canBe("blind") then
+						target:setEffect(target.EFF_BLINDED, 3, {apply_power=getParadoxSpellpower(self, t), no_ct_effect=true})
+					else
+						game.logSeen(target, "%s resists the blindness!", target.name:capitalize())
+					end
+				elseif effect == 2 then
+					if target:canBe("pin") then
+						target:setEffect(target.EFF_PINNED, 3, {apply_power=getParadoxSpellpower(self, t), no_ct_effect=true})
+					else
+						game.logSeen(target, "%s resists the pin!", target.name:capitalize())
+					end
+				elseif effect == 3 then
+					if target:canBe("confusion") then
+						target:setEffect(target.EFF_CONFUSED, 3, {power=50, apply_power=getParadoxSpellpower(self, t), no_ct_effect=true})
+					else
+						game.logSeen(target, "%s resists the confusion!", target.name:capitalize())
+					end
+				end
+			end
+		end)
+	end,
+	doRegression = function(self, t)
+		local tg = self:getTalentTarget(t)
+		
+		self:project(self:getTalentTarget(t), self.x, self.y, function(px, py)
+			local target = game.level.map(px, py, Map.ACTOR)
+			if not target then return end
+			target:setEffect(target.EFF_REGRESSION, 1, {power=t.getRegression(self, t), apply_power=getParadoxSpellpower(self, t), no_ct_effect=true})		
+		end)
+	end,
+	info = function(self, t)
+		local regress = t.getRegression(self, t)
+		local aging = t.getAgingChance(self, t)
+		return ([[While your cosmic cycle is expanding, creatures in its radius have a %d%% chance to suffer the effects of aging; pinning, blinding, or confusing them for 3 turns.
+		While your cosmic cycle is contracting, creatures in its radius suffer from age regression; reducing their three highest stats by %d.
+		The chance and stat reduction will scale with your Spellpower.]]):format(aging, regress)
+	end,
+}
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/chronomancy/matter.lua b/game/modules/tome/data/talents/chronomancy/matter.lua
index 7347030e75770961c6b7bfee0593b5fa1b8e6428..7d50b7ea94494077b3a90c59f002ac1f9fdfd346 100644
--- a/game/modules/tome/data/talents/chronomancy/matter.lua
+++ b/game/modules/tome/data/talents/chronomancy/matter.lua
@@ -32,7 +32,7 @@ newTalent{
 	target = function(self, t)
 		return {type="beam", range=self:getTalentRange(t), talent=t}
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
 		local x, y = self:getTarget(tg)
@@ -60,8 +60,8 @@ newTalent{
 	sustain_paradox = 20,
 	cooldown = 12,
 	tactical = { BUFF =2, DEFEND = 2 },
-	getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 1, 150, getParadoxSpellpower(self)) end,
-	getArmor = function(self, t) return math.ceil(self:combatTalentSpellDamage(t, 20, 50, getParadoxSpellpower(self))) end,
+	getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 1, 150, getParadoxSpellpower(self, t)) end,
+	getArmor = function(self, t) return math.ceil(self:combatTalentSpellDamage(t, 20, 50, getParadoxSpellpower(self, t))) end,
 	do_carbonRegrowth = function(self, t)
 		local maxspikes = t.getArmor(self, t)
 		if self.carbon_armor < maxspikes then
@@ -112,8 +112,8 @@ newTalent{
 	tactical = { ATTACK = 2 },
 	requires_target = true,
 	direct_hit = true,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 60, getParadoxSpellpower(self)) end,
-	getExplosion = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 60, getParadoxSpellpower(self, t)) end,
+	getExplosion = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end,
 	action = function(self, t)
 		local tg = {type="hit", range=self:getTalentRange(t), talent=t}
 		local x, y = self:getTarget(tg)
@@ -152,7 +152,7 @@ newTalent{
 	target = function(self, t)
 		return {type="hit", range=self:getTalentRange(t), talent=t}
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 30, 300, getParadoxSpellpower(self)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 30, 300, getParadoxSpellpower(self, t)) end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
 		local x, y, target = self:getTarget(tg)
@@ -196,5 +196,4 @@ newTalent{
 		Quantum Spike deals 50%% additional damage to targets affected by temporal destabilization and/or continuum destabilization.
 		The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage/2), damDesc(self, DamageType.PHYSICAL, damage/2))
 	end,
-}
-
+}
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/chronomancy/other.lua b/game/modules/tome/data/talents/chronomancy/other.lua
index 2196d55db789506603e09f2e56c65c051e20f10a..cce458a64dfe9dfda013534619ba8742af710a92 100644
--- a/game/modules/tome/data/talents/chronomancy/other.lua
+++ b/game/modules/tome/data/talents/chronomancy/other.lua
@@ -23,10 +23,8 @@
 -- Note that 300 is the optimal balance
 -- Caps at -50% and +50%
 getParadoxModifier = function (self)
-	local pm = util.bound(math.sqrt(self:getParadox() / 300), 0.5, 1.5)
---	local pm = math.sqrt(self:getParadox()/300)
---	pm = math.min(1.5, pm)
---	pm = math.max(0.5, pm)
+	local paradox = self:getParadox()
+	local pm = util.bound(math.sqrt(paradox / 300), 0.5, 1.5)
 	return pm
 end
 
@@ -37,13 +35,42 @@ getParadoxCost = function (self, t, value)
 end
 
 -- Paradox Spellpower (regulates spellpower for chronomancy)
-getParadoxSpellpower = function(self, mod, add)
+getParadoxSpellpower = function(self, t, mod, add)
 	local pm = getParadoxModifier(self)
-	local mod = mod or 0
-	local spellpower = self:combatSpellpower(pm + mod, add)
+	local mod = mod or 1
+	
+	-- Empower?
+	local p = self:isTalentActive(self.T_EMPOWER)
+	if p and p.talent == t.id then
+		pm = pm + self:callTalent(self.T_EMPOWER, "getPower")
+	end
+	
+	local spellpower = self:combatSpellpower(mod * pm, add)
 	return spellpower
 end
 
+-- Paradox Talent scaling based on Spellpower (thanks grayswandir)
+paradoxTalentScale = function(self, t, low, high, limit)
+        local low_power = 50
+        local high_power = 150
+        return self:combatLimit(
+                self:combatTalentSpellDamage(t, low_power, high_power, getParadoxSpellpower(self, t)),
+                limit,
+                low, low_power,
+                high, high_power)
+end
+
+-- Extension Spellbinding
+getExtensionModifier = function(self, t, value)
+	local mod = 1
+	local p = self:isTalentActive(self.T_EXTENSION)
+	if p and p.talent == t.id then
+		mod = mod + self:callTalent(self.T_EXTENSION, "getPower")
+	end
+	value = math.ceil(value * mod)
+	return value
+end
+
 --- Warden weapon functions
 -- Checks for weapons in main and quickslot
 doWardenPreUse = function(self, weapon, silent)
@@ -130,11 +157,15 @@ makeParadoxClone = function(self, target, duration)
 	m.puuid = nil
 	m.on_acquire_target = nil
 	m.no_inventory_access = true
+	m.no_levelup_access = true
 	m.on_takehit = nil
 	m.seen_by = nil
 	m.can_talk = nil
 	m.clone_on_hit = nil
 	m.self_resurrect = nil
+	m.escort_quest = nil
+	m.unused_talents = 0
+	m.unused_generics = 0
 	if m.talents.T_SUMMON then m.talents.T_SUMMON = nil end
 	if m.talents.T_MULTIPLY then m.talents.T_MULTIPLY = nil end
 	
@@ -146,12 +177,11 @@ makeParadoxClone = function(self, target, duration)
 	local tids = {}
 	for tid, _ in pairs(m.talents) do
 		local t = m:getTalentFromId(tid)
-		if t.no_npc_use then tids[#tids+1] = t end
-		if t.remove_on_clone then tids[#tids+1] = t end
+		if (t.no_npc_use and not t.allow_temporal_clones) or t.remove_on_clone then tids[#tids+1] = t end
 	end
 	for i, t in ipairs(tids) do
 		if t.mode == "sustained" and m:isTalentActive(t.id) then m:forceUseTalent(t.id, {ignore_energy=true, silent=true}) end
-		m.talents[t.id] = nil
+		m:unlearnTalentFull(t.id)
 	end
 	
 	-- remove timed effects
@@ -233,10 +263,10 @@ newTalent{
 	info = function(self, t)
 		local duration = t.getDuration(self, t)
 		local preference = self.preferred_paradox
-		local spellpower = getParadoxSpellpower(self)
+		local spellpower = getParadoxSpellpower(self, t)
 		local after_will, will_modifier, sustain_modifier = self:getModifiedParadox()
 		local anomaly = self:paradoxFailChance()
-		return ([[Use to set your preferred Paradox.  While resting you'll adjust your Paradox towards this number over %d turns.
+		return ([[Use to set your preferred Paradox.  While resting or waiting you'll adjust your Paradox towards this number over %d turns.
 		The time it takes you to adjust your Paradox scales down with your Spellpower to a minimum of 10 turns.
 		
 		Preferred Paradox          :  %d
@@ -265,7 +295,7 @@ newTalent{
 		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=self:spellFriendlyFire(), talent=t}
 	end,
 	getDuration = function(self, t) return math.ceil(self:combatTalentScale(self:getTalentLevel(t), 2.3, 4.3)) end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 170, getParadoxSpellpower(self)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 170, getParadoxSpellpower(self, t)) end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
 		local x, y = self:getTarget(tg)
@@ -303,8 +333,8 @@ newTalent{
 	target = function(self, t)
 		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)}
 	end,
-	getSlow = function(self, t) return math.min(10 + self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self))/ 100 , 0.6) end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 60, getParadoxSpellpower(self)) end,
+	getSlow = function(self, t) return math.min(10 + self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t))/ 100 , 0.6) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 60, getParadoxSpellpower(self, t)) end,
 	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
@@ -334,72 +364,6 @@ newTalent{
 	end,
 }
 
-newTalent{
-	name = "Swap",
-	type = {"chronomancy/other", 1},
-	require = chrono_req1,
-	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
-	cooldown = 10,
-	tactical = { DISABLE = 2, },
-	requires_target = true,
-	direct_hit = true,
-	range = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end,
-	getConfuseDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 3, 7)) end,
-	getConfuseEfficency = function(self, t) return math.min(50, self:getTalentLevelRaw(t) * 10) end,
-	action = function(self, t)
-		local tg = {type="hit", range=self:getTalentRange(t)}
-		local tx, ty, target = self:getTarget(tg)
-		if not tx or not ty then return nil end
-		if tx then
-			local _ _, tx, ty = self:canProject(tg, tx, ty)
-			if tx then
-				target = game.level.map(tx, ty, Map.ACTOR)
-				if not target then return nil end
-			end
-		end
-		
-		-- checks for spacetime mastery hit bonus
-		local power = getParadoxSpellpower(self)
-		if self:knowTalent(self.T_SPACETIME_MASTERY) then
-			power =  getParadoxSpellpower(self) * (1 + self:callTalent(self.T_SPACETIME_MASTERY, "getPower"))
-		end
-		
-		if target:canBe("teleport") and self:checkHit(power, target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) then
-			-- first remove the target so the destination tile is empty
-			game.level.map:remove(target.x, target.y, Map.ACTOR)
-			local px, py 
-			px, py = self.x, self.y
-			if self:teleportRandom(tx, ty, 0) then
-				-- return the target at the casters old location
-				game.level.map(px, py, Map.ACTOR, target)
-				self.x, self.y, target.x, target.y = target.x, target.y, px, py
-				game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport")
-				game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
-				target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self:combatSpellpower(0.3)})
-				-- confuse them
-				self:project(tg, target.x, target.y, DamageType.CONFUSION, { dur = t.getConfuseDuration(self, t), dam = t.getConfuseEfficency(self, t),	})
-			else
-				-- return the target without effect
-				game.level.map(target.x, target.y, Map.ACTOR, target)
-				game.logSeen(self, "The spell fizzles!")
-			end
-		else
-			game.logSeen(target, "%s resists the swap!", target.name:capitalize())
-		end
-
-		game:playSoundNear(self, "talents/teleport")
-		return true
-	end,
-	info = function(self, t)
-		local range = self:getTalentRange(t)
-		local duration = t.getConfuseDuration(self, t)
-		local power = t.getConfuseEfficency(self, t)
-		return ([[You manipulate the spacetime continuum in such a way that you switch places with another creature with in a range of %d.  The targeted creature will be confused (power %d%%) for %d turns.
-		The spell's hit chance will increase with your Spellpower.]]):format (range, power, duration)
-	end,
-}
-
 newTalent{
 	name = "Spacetime Mastery",
 	type = {"chronomancy/other", 1},
@@ -518,4 +482,597 @@ newTalent{
 		This spell takes no time to cast.]]):
 		format(power, duration)
 	end,
+}
+
+newTalent{
+	name = "Gather the Threads",
+	type = {"chronomancy/other", 1},
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
+	cooldown = 12,
+	tactical = { BUFF = 2 },
+	getThread = function(self, t) return self:combatTalentScale(t, 7, 30, 0.75) end,
+	getReduction = function(self, t) return self:combatTalentScale(t, 3.6, 15, 0.75) end,
+	action = function(self, t)
+		self:setEffect(self.EFF_GATHER_THE_THREADS, 5, {power=t.getThread(self, t), reduction=t.getReduction(self, t)})
+		game:playSoundNear(self, "talents/spell_generic2")
+		return true
+	end,
+	info = function(self, t)
+		local primary = t.getThread(self, t)
+		local reduction = t.getReduction(self, t)
+		return ([[You begin to gather energy from other timelines. Your Spellpower will increase by %0.2f on the first turn and %0.2f more each additional turn.
+		The effect ends either when you cast a spell, or after five turns.
+		Eacn turn the effect is active, your Paradox will be reduced by %d.
+		This spell will not break Spacetime Tuning, nor will it be broken by activating Spacetime Tuning.]]):format(primary + (primary/5), primary/5, reduction)
+	end,
+}
+
+newTalent{
+	name = "Entropic Field",
+	type = {"chronomancy/other",1},
+	mode = "sustained",
+	points = 5,
+	sustain_paradox = 20,
+	cooldown = 10,
+	tactical = { BUFF = 2 },
+	getPower = function(self, t) return math.min(90, 10 +  self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t))) end,
+	activate = function(self, t)
+		game:playSoundNear(self, "talents/heal")
+		return {
+			particle = self:addParticles(Particles.new("time_shield", 1)),
+			phys = self:addTemporaryValue("resists", {[DamageType.PHYSICAL]=t.getPower(self, t)/2}),
+			proj = self:addTemporaryValue("slow_projectiles", t.getPower(self, t)),
+		}
+	end,
+	deactivate = function(self, t, p)
+		self:removeParticles(p.particle)
+		self:removeTemporaryValue("resists", p.phys)
+		self:removeTemporaryValue("slow_projectiles", p.proj)
+		return true
+	end,
+	info = function(self, t)
+		local power = t.getPower(self, t)
+		return ([[You encase yourself in a field that slows incoming projectiles by %d%%, and increases your physical resistance by %d%%.
+		The effect will scale with your Spellpower.]]):format(power, power / 2)
+	end,
+}
+
+newTalent{
+	name = "Fade From Time",
+	type = {"chronomancy/other", 1},
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
+	cooldown = 24,
+	tactical = { DEFEND = 2, CURE = 2 },
+	getResist = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t)) end,
+	getdurred = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t)), 100, 0, 0, 32.9, 32.9) end, -- Limit < 100%
+	action = function(self, t)
+		-- fading managed by FADE_FROM_TIME effect in mod.data.timed_effects.other.lua
+		self:setEffect(self.EFF_FADE_FROM_TIME, 10, {power=t.getResist(self, t), durred=t.getdurred(self,t)})
+		game:playSoundNear(self, "talents/heal")
+		return true
+	end,
+	info = function(self, t)
+		local resist = t.getResist(self, t)
+		local dur = t.getdurred(self,t)
+		return ([[You partially remove yourself from the timeline for 10 turns.
+		This increases your resistance to all damage by %d%%, reduces the duration of all detrimental effects on you by %d%%, and reduces all damage you deal by 20%%.
+		The resistance bonus, effect reduction, and damage penalty will gradually lose power over the duration of the spell.
+		The effects scale with your Spellpower.]]):
+		format(resist, dur)
+	end,
+}
+
+newTalent{
+	name = "Paradox Clone",
+	type = {"chronomancy/other", 1},
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 50) end,
+	cooldown = 50,
+	tactical = { ATTACK = 1, DISABLE = 2 },
+	range = 2,
+	requires_target = true,
+	no_npc_use = true,
+	getDuration = function(self, t)	return math.floor(self:combatTalentLimit(self:getTalentLevel(t), 50, 4, 8)) end, -- Limit <50
+	getModifier = function(self, t) return rng.range(t.getDuration(self,t)*2, t.getDuration(self, t)*4) end,
+	action = function (self, t)
+		if checkTimeline(self) == true then
+			return
+		end
+
+		local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t}
+		local tx, ty = self:getTarget(tg)
+		if not tx or not ty then return nil end
+		local _ _, tx, ty = self:canProject(tg, tx, ty)
+		if not tx or not ty then return nil end
+		
+		local x, y = util.findFreeGrid(tx, ty, 2, true, {[Map.ACTOR]=true})
+		if not x then
+			game.logPlayer(self, "Not enough space to summon!")
+			return
+		end
+
+		local sex = game.player.female and "she" or "he"
+		local m = require("mod.class.NPC").new(self:cloneFull{
+			no_drops = true,
+			faction = self.faction,
+			summoner = self, summoner_gain_exp=true,
+			exp_worth = 0,
+			summon_time = t.getDuration(self, t),
+			ai_target = {actor=nil},
+			ai = "summoned", ai_real = "tactical",
+			ai_tactic = resolvers.tactic("ranged"), ai_state = { talent_in=1, ally_compassion=10},
+			desc = [[The real you... or so ]]..sex..[[ says.]]
+		})
+		m:removeAllMOs()
+		m.make_escort = nil
+		m.on_added_to_level = nil
+		
+		m.energy.value = 0
+		m.player = nil
+		m.puuid = nil
+		m.max_life = m.max_life
+		m.life = util.bound(m.life, 0, m.max_life)
+		m.forceLevelup = function() end
+		m.die = nil
+		m.on_die = nil
+		m.on_acquire_target = nil
+		m.seen_by = nil
+		m.can_talk = nil
+		m.on_takehit = nil
+		m.no_inventory_access = true
+		m.clone_on_hit = nil
+		m.remove_from_party_on_death = true
+		
+		-- Remove some talents
+		local tids = {}
+		for tid, _ in pairs(m.talents) do
+			local t = m:getTalentFromId(tid)
+			if t.no_npc_use then tids[#tids+1] = t end
+		end
+		for i, t in ipairs(tids) do
+			m.talents[t.id] = nil
+		end
+		
+		game.zone:addEntity(game.level, m, "actor", x, y)
+		game.level.map:particleEmitter(x, y, 1, "temporal_teleport")
+		game:playSoundNear(self, "talents/teleport")
+
+		if game.party:hasMember(self) then
+			game.party:addMember(m, {
+				control="no",
+				type="minion",
+				title="Paradox Clone",
+				orders = {target=true},
+			})
+		end
+
+		self:setEffect(self.EFF_IMMINENT_PARADOX_CLONE, t.getDuration(self, t) + t.getModifier(self, t), {})
+		return true
+	end,
+	info = function(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[You summon your future self to fight alongside you for %d turns.  At some point in the future, you'll be pulled into the past to fight alongside your past self after the initial effect ends.
+		This spell splits the timeline.  Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful.]]):format(duration)
+	end,
+}
+
+newTalent{
+	name = "Displace Damage",
+	type = {"chronomancy/other", 1},
+	mode = "sustained",
+	require = chrono_req1,
+	sustain_paradox = 48,
+	cooldown = 10,
+	tactical = { BUFF = 2 },
+	points = 5,
+	-- called by _M:onTakeHit function in mod\class\Actor.lua to perform the damage displacment
+	getDisplaceDamage = function(self, t) return self:combatTalentLimit(t, 25, 5, 15)/100 end, -- Limit < 25%
+	range = 10,
+	callbackOnTakeDamage = function(self, t, src, x, y, type, dam, tmp, no_martyr)
+		if dam > 0 and src ~= self then
+			-- find available targets
+			local tgts = {}
+			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
+					tgts[#tgts+1] = a
+				end
+			end end
+
+			-- Displace the damage
+			local a = rng.table(tgts)
+			if a then
+				local displace = dam * t.getDisplaceDamage(self, t)
+				game:delayedLogMessage(self, a, "displace_damage"..(a.uid or ""), "#PINK##Source# displaces some damage onto #Target#!")
+				DamageType.defaultProjector(self, a.x, a.y, type, displace, tmp, true)
+				dam = dam - displace
+			end
+		end
+		
+		return {dam=dam}
+	end,
+	activate = function(self, t)
+		return {}
+	end,
+	deactivate = function(self, t, p)
+		return true
+	end,
+	info = function(self, t)
+		local displace = t.getDisplaceDamage(self, t) * 100
+		return ([[You bend space around you, displacing %d%% of any damage you receive onto a random enemy within range.
+		]]):format(displace)
+	end,
+}
+
+newTalent{
+	name = "Repulsion Field",
+	type = {"chronomancy/other",1},
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 30) end,
+	cooldown = 14,
+	tactical = { ATTACKAREA = {PHYSICAL = 2}, ESCAPE = 2 },
+	range = 0,
+	radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.5, 3.5)) end,
+	target = function(self, t)
+		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false}
+	end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 8, 80, getParadoxSpellpower(self, t)) end,
+	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end,
+	direct_hit = true,
+	requires_target = true,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		game.level.map:addEffect(self,
+			self.x, self.y, t.getDuration(self, t),
+			DamageType.REPULSION, t.getDamage(self, t),
+			tg.radius,
+			5, nil,
+			engine.MapEffect.new{color_br=200, color_bg=120, color_bb=0, effect_shader="shader_images/paradox_effect.png"},
+			function(e)
+				e.x = e.src.x
+				e.y = e.src.y
+				return true
+			end,
+			tg.selffire
+		)
+		game:playSoundNear(self, "talents/cloud")
+		return true
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)
+		local duration = t.getDuration(self, t)
+		local radius = self:getTalentRadius(t)
+		return ([[You surround yourself with a radius %d distortion of gravity, knocking back and dealing %0.2f physical damage to all creatures inside it.  The effect lasts %d turns.  Deals 50%% extra damage to pinned targets, in addition to the knockback.
+		The blast wave may hit targets more then once, depending on the radius and the knockback effect.
+		The damage will scale with your Spellpower.]]):format(radius, damDesc(self, DamageType.PHYSICAL, damage), duration)
+	end,
+}
+
+newTalent{
+	name = "Temporal Clone",
+	type = {"chronomancy/other", 1},
+	points = 5,
+	cooldown = 12,
+	paradox = function (self, t) return getParadoxCost(self, t, 15) end,
+	tactical = { ATTACK = 2, DISABLE = 2 },
+	requires_target = true,
+	range = 10,
+	remove_on_clone = true,
+	target = function (self, t)
+		return {type="hit", range=self:getTalentRange(t), talent=t, nowarning=true}
+	end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 6, 12))) end,
+	getDamagePenalty = function(self, t) return 60 - math.min(self:combatTalentSpellDamage(t, 0, 20, getParadoxSpellpower(self, t)), 30) end,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local x, y, target = self:getTarget(tg)
+		if not x or not y then return nil end
+		local _ _, _, _, x, y = self:canProject(tg, x, y)
+		if not x or not y then return nil end
+		local target = game.level.map(x, y, Map.ACTOR)
+		if not target then return end
+
+		-- Find space
+		local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true})
+		if not tx then
+			game.logPlayer(self, "Not enough space to summon!")
+			return
+		end
+
+		-- Rank Penalty
+		local duration = t.getDuration(self, t)
+		if target.rank > 1 then duration = math.ceil(t.getDuration(self, t)/(target.rank/2)) end
+		
+		 -- Clone the target
+		local m = makeParadoxClone(self, target, duration)
+		-- Add and change some values
+		m.faction = self.faction
+		m.summoner = self
+		m.generic_damage_penalty = t.getDamagePenalty(self, t)
+		m.max_life = m.max_life * (100 - t.getDamagePenalty(self, t))/100
+		m.life = m.max_life
+		m.remove_from_party_on_death = true
+		
+		-- Handle some AI stuff
+		m.ai_state = { talent_in=2, ally_compassion=10 }
+
+		game.zone:addEntity(game.level, m, "actor", tx, ty)
+		
+		-- Set our target
+		if self:reactionToward(target) < 0 then
+			m:setTarget(target)
+		end
+		
+		if game.party:hasMember(self) then
+			game.party:addMember(m, {
+				control="no",
+				type="temporal-clone",
+				title="Temporal Clone",
+				orders = {target=true},
+			})
+		end
+			
+		game.level.map:particleEmitter(tx, ty, 1, "temporal_teleport")
+		game:playSoundNear(self, "talents/spell_generic")
+		
+		return true
+	end,
+	info = function(self, t)
+		local duration = t.getDuration(self, t)
+		local damage_penalty = t.getDamagePenalty(self, t)
+		return ([[Clones the target creature for up to %d turns.  The duration of the effect will be divided by half the target's rank, and the target will have have %d%% of its normal life and deal %d%% less damage.
+		If you clone a hostile creature the clone will target the creature it was cloned from.
+		The life and damage penalties will be lessened by your Spellpower.]]):
+		format(duration, 100 - damage_penalty, damage_penalty)
+	end,
+}
+
+newTalent{
+	name = "Paradox Mastery",
+	type = {"chronomancy/other", 1},
+	mode = "passive",
+	points = 5,
+	-- Static history bonus handled in timetravel.lua, backfire calcs performed by _M:getModifiedParadox function in mod\class\Actor.lua	
+	WilMult = function(self, t) return self:combatTalentScale(t, 0.15, 0.5) end,
+	stabilityDuration = function(self, t) return math.floor(self:combatTalentScale(t, 0.4, 2.7, "log")) end,  --This is still used by an older talent, leave it here for backwards compatability
+	passives = function(self, t, p)
+		self:talentTemporaryValue(p, "paradox_will_mutli", t.WilMult(self, t))
+	end,
+	info = function(self, t)
+		return ([[You've learned to focus your control over the spacetime continuum, and quell anomalous effects.  Increases your effective Willpower for anomaly calculations by %d%%.]]):
+		format(t.WilMult(self, t) * 100)
+	end,
+}
+
+newTalent{
+	name = "Damage Smearing",
+	type = {"chronomancy/other", 1},
+	mode = "sustained",
+	sustain_paradox = 48,
+	cooldown = 24,
+	tactical = { DEFEND = 2 },
+	points = 5,
+	getPercent = function(self, t) return self:combatTalentLimit(t, 50, 10, 30)/100 end, -- Limit < 50%
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 6))) end,
+	callbackOnTakeDamage = function(self, t, src, x, y, type, dam, tmp, no_martyr)
+		if dam > 0 and type ~= DamageType.TEMPORAL then
+			local smear = dam * t.getPercent(self, t)
+			self:setEffect(self.EFF_DAMAGE_SMEARING, t.getDuration(self, t), {dam=smear/t.getDuration(self, t), no_ct_effect=true})
+			game:delayedLogDamage(src, self, 0, ("%s(%d smeared)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", smear), false)
+			dam = dam - smear
+		end
+			
+		return {dam=dam}
+	end,
+	activate = function(self, t)
+		game:playSoundNear(self, "talents/spell_generic")
+		return {}
+	end,
+	deactivate = function(self, t, p)
+		return true
+	end,
+	info = function(self, t)
+		local percent = t.getPercent(self, t) * 100
+		local duration = t.getDuration(self, t)
+		return ([[You convert %d%% of all non-temporal damage you receive into temporal damage spread out over %d turns.
+		This damage will bypass resistance and affinity.]]):format(percent, duration)
+	end,
+}
+
+newTalent{
+	name = "Banish",
+	type = {"chronomancy/other", 1},
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
+	cooldown = 10,
+	tactical = { ESCAPE = 2 },
+	range = 0,
+	radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 5.5)) end,
+	getTeleport = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 8, 16)) end,
+	target = function(self, t)
+		return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false, talent=t}
+	end,
+	requires_target = true,
+	direct_hit = true,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local hit = false
+
+		self:project(tg, self.x, self.y, function(px, py)
+			local target = game.level.map(px, py, Map.ACTOR)
+			if not target or target == self then return end
+			game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport")
+			if self:checkHit(getParadoxSpellpower(self, t), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) and target:canBe("teleport") then
+				if not target:teleportRandom(target.x, target.y, self:getTalentRadius(t) * 4, self:getTalentRadius(t) * 2) then
+					game.logSeen(target, "The spell fizzles on %s!", target.name:capitalize())
+				else
+					target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=getParadoxSpellpower(self, t, 0.3)})
+					game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport")
+					hit = true
+				end
+			else
+				game.logSeen(target, "%s resists the banishment!", target.name:capitalize())
+			end
+		end)
+		
+		if not hit then
+			game:onTickEnd(function()
+				if not self:attr("no_talents_cooldown") then
+					self.talents_cd[self.T_BANISH] = self.talents_cd[self.T_BANISH] /2
+				end
+			end)
+		end
+
+		game:playSoundNear(self, "talents/teleport")
+
+		return true
+	end,
+	info = function(self, t)
+		local radius = self:getTalentRadius(t)
+		local range = t.getTeleport(self, t)
+		return ([[Randomly teleports all targets within a radius of %d around you.  Targets will be teleported between %d and %d tiles from their current location.
+		If no targets are teleported the cooldown will be halved.
+		The chance of teleportion will scale with your Spellpower.]]):format(radius, range / 2, range)
+	end,
+}
+
+newTalent{
+	name = "Swap",
+	type = {"chronomancy/other", 1},
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
+	cooldown = 10,
+	tactical = { DISABLE = 1 },
+	requires_target = true,
+	direct_hit = true,
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end,
+	getConfuseDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 3, 7)) end,
+	getConfuseEfficency = function(self, t) return math.min(50, self:getTalentLevelRaw(t) * 10) end,
+	action = function(self, t)
+		local tg = {type="hit", range=self:getTalentRange(t)}
+		local tx, ty, target = self:getTarget(tg)
+		if not tx or not ty then return nil end
+		if tx then
+			local _ _, tx, ty = self:canProject(tg, tx, ty)
+			if tx then
+				target = game.level.map(tx, ty, Map.ACTOR)
+				if not target then return nil end
+			end
+		end
+		
+		-- Check hit
+		if target:canBe("teleport") and self:checkHit(getParadoxSpellpower(self, t), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) then
+			-- Grab the caster's location
+			local px, py = self.x, self.y
+		
+			-- Remove the target so the destination tile is empty
+			game.level.map:remove(target.x, target.y, Map.ACTOR)
+			
+			-- Try to teleport to the target's old location
+			if self:teleportRandom(tx, ty, 0) then
+				-- Put the target back in the caster's old location
+				game.level.map(px, py, Map.ACTOR, target)
+				target.x, target.y = px, py
+				
+				-- confuse them
+				self:project(tg, target.x, target.y, DamageType.CONFUSION, { dur = t.getConfuseDuration(self, t), dam = t.getConfuseEfficency(self, t), apply_power=getParadoxSpellpower(self, t)})
+				target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=getParadoxSpellpower(self, t, 0.3)})
+				
+				game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport")
+				game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
+			else
+				-- If we can't teleport, return the target
+				game.level.map(target.x, target.y, Map.ACTOR, target)
+				game.logSeen(self, "The spell fizzles!")
+			end
+		else
+			game.logSeen(target, "%s resists the swap!", target.name:capitalize())
+		end
+
+		game:playSoundNear(self, "talents/teleport")
+		return true
+	end,
+	info = function(self, t)
+		local range = self:getTalentRange(t)
+		local duration = t.getConfuseDuration(self, t)
+		local power = t.getConfuseEfficency(self, t)
+		return ([[You manipulate the spacetime continuum in such a way that you switch places with another creature with in a range of %d.  The targeted creature will be confused (power %d%%) for %d turns.
+		The spell's hit chance will increase with your Spellpower.]]):format (range, power, duration)
+	end,
+}
+
+newTalent{
+	name = "Temporal Wake",
+	type = {"chronomancy/other", 1},
+	points = 5,
+	random_ego = "attack",
+	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
+	cooldown = 10,
+	tactical = { ATTACK = {TEMPORAL = 1, PHYSICAL = 1}, CLOSEIN = 2, DISABLE = { stun = 2 } },
+	direct_hit = true,
+	requires_target = true,
+	is_teleport = true,
+	target = function(self, t)
+		return {type="beam", start_x=x, start_y=y, range=self:getTalentRange(t), selffire=false, talent=t}
+	end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end,
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local x, y, target = self:getTarget(tg)
+		if not x or not y then return nil end
+		if not self:hasLOS(x, y) or game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then
+			game.logSeen(self, "You do not have line of sight.")
+			return nil
+		end
+		local _ _, x, y = self:canProject(tg, x, y)
+		local ox, oy = self.x, self.y
+		
+		-- If we target an actor directly project onto the other side of it (quality of life)
+		if target then
+			local dir = util.getDir(x, y, self.x, self.y)
+			x, y = util.coordAddDir(x, y, dir)
+		end
+		
+		-- since we're using a precise teleport we'll look for a free grid first
+		local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true})
+		if tx and ty then
+			if not self:teleportRandom(tx, ty, 0) then
+				game.logSeen(self, "The teleport fizzles!")
+			else
+				local dam = self:spellCrit(t.getDamage(self, t))
+				local x, y = ox, oy
+				self:project(tg, x, y, function(px, py)
+					local target = game.level.map(px, py, Map.ACTOR)
+					if target then
+						-- Deal warp damage first so we don't overwrite a big stun with a little one
+						DamageType:get(DamageType.WARP).projector(self, px, py, DamageType.WARP, dam)
+						
+						-- Try to stun
+						if target:canBe("stun") then
+							target:setEffect(target.EFF_STUNNED, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self, t)})
+						else
+							game.logSeen(target, "%s resists the stun!", target.name:capitalize())
+						end
+					end
+				end)
+				game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "temporal_lightning", {tx=x-self.x, ty=y-self.y})
+				game:playSoundNear(self, "talents/lightning")
+			end
+		end
+		
+		return true
+	end,
+	info = function(self, t)
+		local stun = t.getDuration(self, t)
+		local damage = t.getDamage(self, t)
+		return ([[Violently fold the space between yourself and another point within range.
+		You teleport to the target location, and leave a temporal wake behind that stuns for %d turns and deals %0.2f temporal and %0.2f physical warp damage to targets in the path.
+		The damage will scale with your Spellpower.]]):
+		format(stun, damDesc(self, DamageType.TEMPORAL, damage/2), damDesc(self, DamageType.PHYSICAL, damage/2))
+	end,
 }
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/chronomancy/paradox.lua b/game/modules/tome/data/talents/chronomancy/paradox.lua
index 936e39dc76c55e1bab95a1339f9e4f383f31f899..1cba0e3267adfaf7fa6766ce94641b73bbc758b1 100644
--- a/game/modules/tome/data/talents/chronomancy/paradox.lua
+++ b/game/modules/tome/data/talents/chronomancy/paradox.lua
@@ -17,234 +17,7 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
-newTalent{
-	name = "Paradox Mastery",
-	type = {"chronomancy/paradox", 1},
-	mode = "passive",
-	require = chrono_req_high1,
-	points = 5,
-	-- Static history bonus handled in timetravel.lua, backfire calcs performed by _M:getModifiedParadox function in mod\class\Actor.lua	
-	WilMult = function(self, t) return self:combatTalentScale(t, 0.15, 0.5) end,
-	stabilityDuration = function(self, t) return math.floor(self:combatTalentScale(t, 0.4, 2.7, "log")) end,
-	getResist = function(self, t) return self:combatTalentScale(t, 10, 35) end,
-	passives = function(self, t, p)
-		self:talentTemporaryValue(p, "resists", {[DamageType.TEMPORAL] = t.getResist(self, t)})
-	end,
-	info = function(self, t)
-		return ([[You've learned to focus your control over the spacetime continuum, and quell anomalous effects.  Increases your Temporal resistance by %d%%, extends the duration of the Static History stability effect by %d turns, and increases your effective willpower for failure, anomaly, and backfire calculations by %d%%.]]):
-		format(t.getResist(self, t), t.stabilityDuration(self, t), t.WilMult(self, t) * 100)
-	end,
-}
+-- EDGE TODO: Particles, Timed Effect Particles
 
-newTalent{
-	name = "Cease to Exist",
-	type = {"chronomancy/paradox", 2},
-	require = chrono_req_high2,
-	points = 5,
-	cooldown = 24,
-	paradox = function (self, t) return getParadoxCost(self, t, 40) end,
-	range = 10,
-	tactical = { ATTACK = 2 },
-	requires_target = true,
-	direct_hit = true,
-	no_npc_use = true,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 5, 9)) end,
-	getPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)) end,
-	-- Resistance reduction handled under CEASE_TO_EXIST in data\timed_effects\magical.lua
-	getPower = function(self, t)
-		return self:combatLimit(self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)), 100, 0, 0, 32.9, 32.9) -- Limit < 100%
-	end,
-	on_pre_use = function(self, t, silent)
-		if checkTimeline(self) then
-			if not silent then
-				game.logPlayer(self, "The timeline is too fractured to do this now.")
-			end
-			return false
-		end
-		return true
-	end,
-	do_instakill = function(self, t)
-		-- search for target because it's ID will change when the chrono restore takes place
-		local tg = false
-		local grids = core.fov.circle_grids(self.x, self.y, 10, 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 a:hasEffect(a.EFF_CEASE_TO_EXIST) then
-				tg = a
-			end
-		end end
-		
-		if tg then
-			game:onTickEnd(function()
-				tg:removeEffect(tg.EFF_CEASE_TO_EXIST)
-				game.logSeen(tg, "#LIGHT_BLUE#%s never existed, this never happened!", tg.name:capitalize())
-				tg:die(self)
-			end)
-		end
-	end,
-	action = function(self, t)
-		-- get our target
-		local tg = {type="hit", range=self:getTalentRange(t)}
-		local tx, ty = self:getTarget(tg)
-		if not tx or not ty then return nil end
-		local _ _, tx, ty = self:canProject(tg, tx, ty)
-		
-		local target = game.level.map(tx, ty, Map.ACTOR)
-		if not target then return end
 
-		if target == self then
-			game.logSeen(self, "#LIGHT_STEEL_BLUE#%s tries to remove %sself from existance!", self.name, string.his_her(self))
-			self:incParadox(400)
-			game.level.map:particleEmitter(self.x, self.y, 1, "ball_temporal", {radius=1, tx=self.x, ty=self.y})
-			return true
-		end
-		
-		-- does the spell hit?  if not nothing happens
-		if not self:checkHit(self:combatSpellpower(), target:combatSpellResist()) then
-			game.logSeen(target, "%s resists!", target.name:capitalize())
-			return true
-		end
-	
-		-- Manualy start cooldown before the chronoworld is made
-		game.player:startTalentCooldown(t)
-		
-		-- set up chronoworld next, we'll load it when the target dies in class\actor
-		game:onTickEnd(function()
-			game:chronoClone("cease_to_exist")
-		end)
-			
-		target:setEffect(target.EFF_CEASE_TO_EXIST, t.getDuration(self,t), {power=t.getPower(self, t)})
-				
-		return true
-	end,
-	info = function(self, t)
-		local duration = t.getDuration(self, t)
-		local power = t.getPower(self, t)
-		return ([[Over the next %d turns, you attempt to remove the target from the timeline.  Its resistances will be reduced by %d%%, and if you manage to kill it while the spell is in effect, you'll be returned to the point in time you cast this spell and the target will be slain.
-		This spell splits the timeline.  Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful.
-		The duration will scale with your Paradox and the resistance penalty will scale with your Spellpower.]])
-		:format(duration, power)
-	end,
-}
 
-newTalent{
-	name = "Fade From Time",
-	type = {"chronomancy/paradox", 3},
-	require = chrono_req_high3,
-	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
-	cooldown = 24,
-	tactical = { DEFEND = 2, CURE = 2 },
-	getResist = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)) end,
-	getdurred = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)), 100, 0, 0, 32.9, 32.9) end, -- Limit < 100%
-	action = function(self, t)
-		-- fading managed by FADE_FROM_TIME effect in mod.data.timed_effects.other.lua
-		self:setEffect(self.EFF_FADE_FROM_TIME, 10, {power=t.getResist(self, t), durred=t.getdurred(self,t)})
-		game:playSoundNear(self, "talents/heal")
-		return true
-	end,
-	info = function(self, t)
-		local resist = t.getResist(self, t)
-		local dur = t.getdurred(self,t)
-		return ([[You partially remove yourself from the timeline for 10 turns.
-		This increases your resistance to all damage by %d%%, reduces the duration of all detrimental effects on you by %d%%, and reduces all damage you deal by 20%%.
-		The resistance bonus, effect reduction, and damage penalty will gradually lose power over the duration of the spell.
-		The effects scale with your Spellpower.]]):
-		format(resist, dur)
-	end,
-}
-
-newTalent{
-	name = "Paradox Clone",
-	type = {"chronomancy/paradox", 4},
-	require = chrono_req_high4,
-	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 50) end,
-	cooldown = 50,
-	tactical = { ATTACK = 1, DISABLE = 2 },
-	range = 2,
-	requires_target = true,
-	no_npc_use = true,
-	getDuration = function(self, t)	return math.floor(self:combatTalentLimit(self:getTalentLevel(t), 50, 4, 8)) end, -- Limit <50
-	getModifier = function(self, t) return rng.range(t.getDuration(self,t)*2, t.getDuration(self, t)*4) end,
-	action = function (self, t)
-		if checkTimeline(self) == true then
-			return
-		end
-
-		local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t}
-		local tx, ty = self:getTarget(tg)
-		if not tx or not ty then return nil end
-		local _ _, tx, ty = self:canProject(tg, tx, ty)
-		if not tx or not ty then return nil end
-		
-		local x, y = util.findFreeGrid(tx, ty, 2, true, {[Map.ACTOR]=true})
-		if not x then
-			game.logPlayer(self, "Not enough space to summon!")
-			return
-		end
-
-		local sex = game.player.female and "she" or "he"
-		local m = require("mod.class.NPC").new(self:cloneFull{
-			no_drops = true,
-			faction = self.faction,
-			summoner = self, summoner_gain_exp=true,
-			exp_worth = 0,
-			summon_time = t.getDuration(self, t),
-			ai_target = {actor=nil},
-			ai = "summoned", ai_real = "tactical",
-			ai_tactic = resolvers.tactic("ranged"), ai_state = { talent_in=1, ally_compassion=10},
-			desc = [[The real you... or so ]]..sex..[[ says.]]
-		})
-		m:removeAllMOs()
-		m.make_escort = nil
-		m.on_added_to_level = nil
-		
-		m.energy.value = 0
-		m.player = nil
-		m.puuid = nil
-		m.max_life = m.max_life
-		m.life = util.bound(m.life, 0, m.max_life)
-		m.forceLevelup = function() end
-		m.die = nil
-		m.on_die = nil
-		m.on_acquire_target = nil
-		m.seen_by = nil
-		m.can_talk = nil
-		m.on_takehit = nil
-		m.no_inventory_access = true
-		m.clone_on_hit = nil
-		m.remove_from_party_on_death = true
-		
-		-- Remove some talents
-		local tids = {}
-		for tid, _ in pairs(m.talents) do
-			local t = m:getTalentFromId(tid)
-			if t.no_npc_use then tids[#tids+1] = t end
-		end
-		for i, t in ipairs(tids) do
-			m.talents[t.id] = nil
-		end
-		
-		game.zone:addEntity(game.level, m, "actor", x, y)
-		game.level.map:particleEmitter(x, y, 1, "temporal_teleport")
-		game:playSoundNear(self, "talents/teleport")
-
-		if game.party:hasMember(self) then
-			game.party:addMember(m, {
-				control="no",
-				type="minion",
-				title="Paradox Clone",
-				orders = {target=true},
-			})
-		end
-
-		self:setEffect(self.EFF_IMMINENT_PARADOX_CLONE, t.getDuration(self, t) + t.getModifier(self, t), {})
-		return true
-	end,
-	info = function(self, t)
-		local duration = t.getDuration(self, t)
-		return ([[You summon your future self to fight alongside you for %d turns.  At some point in the future, you'll be pulled into the past to fight alongside your past self after the initial effect ends.
-		This spell splits the timeline.  Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful.]]):format(duration)
-	end,
-}
diff --git a/game/modules/tome/data/talents/chronomancy/spacetime-folding.lua b/game/modules/tome/data/talents/chronomancy/spacetime-folding.lua
index b32c5ab6f6d8093de24029cc64f78d7b2619c853..e8c0b67babef10e84a843f088cbea9c7b36165cb 100644
--- a/game/modules/tome/data/talents/chronomancy/spacetime-folding.lua
+++ b/game/modules/tome/data/talents/chronomancy/spacetime-folding.lua
@@ -21,15 +21,99 @@
 
 local Trap = require "mod.class.Trap"
 
+makeWarpMine = function(self, t, x, y, type)
+	-- Mine values
+	local dam = self:spellCrit(self:callTalent(self.T_WARP_MINES, "getDamage"))
+	local duration = self:callTalent(self.T_WARP_MINES, "getDuration")
+	local detect = math.floor(self:callTalent(self.T_WARP_MINES, "trapPower") * 0.8)
+	local disarm = math.floor(self:callTalent(self.T_WARP_MINES, "trapPower"))
+	local power = getParadoxSpellpower(self, t)
+	local dest_power = getParadoxSpellpower(self, t, 0.3)
+	
+	-- Our Mines
+	local mine = Trap.new{
+		name = ("warp mine: %s"):format(type),
+		type = "temporal", id_by_type=true, unided_name = "trap",
+		display = '^', color=colors.BLUE, image = ("trap/chronomine_%s_0%d.png"):format(type == "toward" and "blue" or "red", rng.avg(1, 4, 3)),
+		shader = "shadow_simulacrum", shader_args = { color = {0.2, 0.2, 0.2}, base = 0.8, time_factor = 1500 },
+		dam = dam, t=t, power = power, dest_power = dest_power,
+		temporary = duration,
+		x = x, y = y, type = type,
+		summoner = self, summoner_gain_exp = true,
+		disarm_power = disarm,	detect_power = detect,
+		canTrigger = function(self, x, y, who)
+			if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
+			return false
+		end,
+		triggered = function(self, x, y, who)
+			-- Project our damage
+			self.summoner:project({type="hit",x=x,y=y, talent=t}, x, y, engine.DamageType.WARP, self.dam)
+			
+			-- Teleport?
+			if not who.dead then
+				-- Does our teleport hit?
+				local hit = self.summoner:checkHit(self.power, who:combatSpellResist() + (who:attr("continuum_destabilization") or 0)) and who:canBe("teleport")
+				if hit then
+					game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
+					local teleport_done = false
+					
+					if self.type == "toward" then
+						-- since we're using a precise teleport we'll look for a free grid first
+						local tx, ty = util.findFreeGrid(self.summoner.x, self.summoner.y, 5, true, {[engine.Map.ACTOR]=true})
+						if tx and ty then
+							game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
+							if not who:teleportRandom(self.summoner.x, self.summoner.y, 1, 0) then
+								game.logSeen(self, "The teleport fizzles!")
+							else
+								teleport_done = true
+							end
+						end
+					elseif self.type == "away" then
+						game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
+						if not who:teleportRandom(self.summoner.x, self.summoner.y, 10, 5) then
+							game.logSeen(self, "The teleport fizzles!")
+						else
+							teleport_done = true
+						end
+					end
+					
+					-- Destabailize?
+					if teleport_done then
+						who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self.dest_power})
+						game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
+						game:playSoundNear(self, "talents/teleport")
+					end
+				else
+					game.logSeen(who, "%s resists the teleport!", who.name:capitalize())
+				end					
+			end
+	
+			return true, true
+		end,
+		canAct = false,
+		energy = {value=0},
+		act = function(self)
+			self:useEnergy()
+			self.temporary = self.temporary - 1
+			if self.temporary <= 0 then
+				if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
+				game.level:removeEntity(self)
+			end
+		end,
+	}
+	
+	return mine
+end
+
 newTalent{
 	name = "Warp Mines",
 	type = {"chronomancy/spacetime-folding", 1},
 	points = 5,
 	mode = "passive",
 	require = chrono_req1,
-	getRange = function(self, t) return math.floor(self:combatTalentScale(t, 5, 10, 0.5, 0, 1)) end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 200, getParadoxSpellpower(self)) end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph
+	getRange = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 200, getParadoxSpellpower(self, t)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 6, 10))) end, -- Duration of mines
 	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end, -- Used to determine detection and disarm power, about 75 at level 50
 	on_learn = function(self, t)
 		local lev = self:getTalentLevelRaw(t)
@@ -52,10 +136,10 @@ newTalent{
 		local disarm = t.trapPower(self,t)
 		local duration = t.getDuration(self, t)
 		return ([[Learn to lay Warp Mines in a radius of 1 out to a range of %d.
-		Warp Mines teleport targets that trigger them either toward you or away from you depending on the type of mine used and inflict %0.2f temporal and %0.2f physical damage.
+		Warp Mines teleport targets that trigger them either toward you or away from you depending on the type of mine used and inflict %0.2f physical and %0.2f temporal (warp) damage.
 		The mines are hidden traps (%d detection and %d disarm power based on your Magic) and last for %d turns.
 		The damage caused by your Warp Mines will improve with your Spellpower.]]):
-		format(range, damDesc(self, DamageType.TEMPORAL, damage), damDesc(self, DamageType.PHYSICAL, damage), detect, disarm, duration) --I5
+		format(range, damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage), detect, disarm, duration) --I5
 	end,
 }
 
@@ -65,7 +149,7 @@ newTalent{
 	points = 1,
 	cooldown = 10,
 	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
-	tactical = { ATTACKAREA = { TEMPORAL = 2 }, CLOSEIN = 2  },
+	tactical = { ATTACKAREA = { TEMPORAL = 1, PHYSICAL = 1 }, CLOSEIN = 2  },
 	requires_target = true,
 	range = function(self, t) return self:callTalent(self.T_WARP_MINES, "getRange")end,
 	no_unlearn_last = true,
@@ -75,76 +159,17 @@ newTalent{
 		local tx, ty = self:getTarget(tg)
 		if not tx or not ty then return nil end
 		local __, tx, ty = self:canProject(tg, tx, ty)
-		
-
-		local dam = self:spellCrit(self:callTalent(self.T_WARP_MINES, "getDamage"))
-		local duration = self:callTalent(self.T_WARP_MINES, "getDuration")
-		local detect = self:callTalent(self.T_WARP_MINES, "trapPower") * 0.8
-		local disarm = self:callTalent(self.T_WARP_MINES, "trapPower")
-		
+	
 		-- Lay the mines in a ball
 		self:project(tg, tx, ty, function(px, py)
 			local target_trap = game.level.map(px, py, Map.TRAP)
 			if target_trap then return end
 			if game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") then return end
 			
-			-- Our Mines
-			local power = getParadoxSpellpower(self)
-			local trap = Trap.new{
-				name = "warp mine: toward",
-				type = "temporal", id_by_type=true, unided_name = "trap",
-				display = '^', color=colors.BLUE, image = "trap/trap_warpmine_01.png",
-				shader = "shadow_simulacrum",
-				shader_args = { color = {0.2, 0.2, 0.6}, base = 0.8, time_factor = 1500 },
-				dam = dam, power = power,
-				canTrigger = function(self, x, y, who)
-					if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
-					return false
-				end,
-				triggered = function(self, x, y, who)
-					self:project({type="hit",x=x,y=y}, x, y, engine.DamageType.MATTER, self.dam)
-					-- Teleport Toward
-					if not who.dead then
-						local hit = self.summoner:checkHit(self.power, who:combatSpellResist() + (who:attr("continuum_destabilization") or 0)) and who:canBe("teleport")
-						if not hit then	
-							game.logSeen(who, "%s resists the teleport!", who.name:capitalize())
-						else
-							game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
-							-- since we're using a precise teleport we'll look for a free grid first
-							local tx, ty = util.findFreeGrid(self.summoner.x, self.summoner.y, 5, true, {[Map.ACTOR]=true})
-							if tx and ty then
-								game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
-								if not who:teleportRandom(self.summoner.x, self.summoner.y, 1, 0) then
-									game.logSeen(self, "The warp fizzles!")
-								else
-									who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=power*0.3})
-									game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
-								end
-							end
-						end
-					end
-			
-					return true, true
-				end,
-				temporary = duration,
-				x = px, y = py,
-				disarm_power = math.floor(disarm),
-				detect_power = math.floor(detect),
-				canAct = false,
-				energy = {value=0},
-				act = function(self)
-					self:useEnergy()
-					self.temporary = self.temporary - 1
-					if self.temporary <= 0 then
-						if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
-						game.level:removeEntity(self)
-					end
-				end,
-				summoner = self,
-				summoner_gain_exp = true,
-			}
+			-- Make our mine
+			local trap = makeWarpMine(self, t, px, py, "toward")
 			
-			-- Add mines
+			-- Add the mine
 			game.level:addEntity(trap)
 			trap:identify(true)
 			trap:setKnown(self, true)
@@ -161,11 +186,11 @@ newTalent{
 		local duration = self:callTalent(self.T_WARP_MINES, "getDuration")
 		local detect = self:callTalent(self.T_WARP_MINES, "trapPower") * 0.8
 		local disarm = self:callTalent(self.T_WARP_MINES, "trapPower")
-		return ([[Lay Warp Mines in a radius of 1 that teleport enemies to you and inflict %0.2f temporal and %0.2f physical damage.
+		return ([[Lay Warp Mines in a radius of 1 that teleport enemies to you and inflict %0.2f physical and %0.2f temporal (warp) damage.
 		The mines are hidden traps (%d detection and %d disarm power based on your Magic) and last for %d turns.
 		The damage caused by your Warp Mines will improve with your Spellpower.
 		Using this talent will trigger the cooldown on Warp Mine Away.]]):
-		format(damDesc(self, DamageType.TEMPORAL, damage), damDesc(self, DamageType.PHYSICAL, damage), detect, disarm, duration) 
+		format(damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage), detect, disarm, duration)
 	end,
 }
 
@@ -175,7 +200,7 @@ newTalent{
 	points = 1,
 	cooldown = 10,
 	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
-	tactical = { ATTACKAREA = { TEMPORAL = 2 }, ESCAPE = 2  },
+	tactical = { ATTACKAREA = { TEMPORAL = 1, PHYSICAL = 1 }, ESCAPE = 2  },
 	requires_target = true,
 	range = function(self, t) return self:callTalent(self.T_WARP_MINES, "getRange") end,
 	no_unlearn_last = true,
@@ -185,12 +210,6 @@ newTalent{
 		local tx, ty = self:getTarget(tg)
 		if not tx or not ty then return nil end
 		local _ _, tx, ty = self:canProject(tg, tx, ty)
-				
-		local dam = self:spellCrit(self:callTalent(self.T_WARP_MINES, "getDamage"))
-		local duration = self:callTalent(self.T_WARP_MINES, "getDuration")
-		local detect = self:callTalent(self.T_WARP_MINES, "trapPower") * 0.8
-		local disarm = self:callTalent(self.T_WARP_MINES, "trapPower")
-		local power = getParadoxSpellpower(self)
 		
 		-- Lay the mines in a ball
 		self:project(tg, tx, ty, function(px, py)
@@ -198,57 +217,10 @@ newTalent{
 			if target_trap then return end
 			if game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") then return end
 			
-			-- Our Mines
-			local trap = Trap.new{
-				name = "warp mine away",
-				type = "temporal", id_by_type=true, unided_name = "trap",
-				display = '^', color=colors.BLUE, image = "trap/trap_warpmine_02.png",
-				shader = "shadow_simulacrum",
-				shader_args = { color = {0.6, 0.2, 0.2}, base = 0.8, time_factor = 1500 },
-				dam = dam, power = power,
-				canTrigger = function(self, x, y, who)
-					if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
-					return false
-				end,
-				triggered = function(self, x, y, who)
-					self:project({type="hit",x=x,y=y}, x, y, engine.DamageType.MATTER, self.dam)
-					-- Teleport Away
-					if not who.dead then
-						local hit = self.summoner:checkHit(self.power, who:combatSpellResist() + (who:attr("continuum_destabilization") or 0)) and who:canBe("teleport")
-						if not hit then	
-							game.logSeen(who, "%s resists the teleport!", who.name:capitalize())
-						else
-							game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
-							if not who:teleportRandom(self.summoner.x, self.summoner.y, 10, 5) then
-								game.logSeen(self, "The warp fizzles!")
-							else
-								who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=power*0.3})
-								game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
-							end
-						end
-					end
+			-- Make our mine
+			local trap = makeWarpMine(self, t, px, py, "away")
 			
-					return true, true
-				end,
-				temporary = duration,
-				x = px, y = py,
-				disarm_power = math.floor(disarm),
-				detect_power = math.floor(detect),
-				canAct = false,
-				energy = {value=0},
-				act = function(self)
-					self:useEnergy()
-					self.temporary = self.temporary - 1
-					if self.temporary <= 0 then
-						if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
-						game.level:removeEntity(self)
-					end
-				end,
-				summoner = self,
-				summoner_gain_exp = true,
-			}
-			
-			-- Add mines
+			-- Add the mine
 			game.level:addEntity(trap)
 			trap:identify(true)
 			trap:setKnown(self, true)
@@ -265,139 +237,225 @@ newTalent{
 		local duration = self:callTalent(self.T_WARP_MINES, "getDuration")
 		local detect = self:callTalent(self.T_WARP_MINES, "trapPower") * 0.8
 		local disarm = self:callTalent(self.T_WARP_MINES, "trapPower")
-		return ([[Lay Warp Mines in a radius of 1 that teleport enemies away from you and inflict %0.2f temporal and %0.2f physical damage.
+		return ([[Lay Warp Mines in a radius of 1 that teleport enemies away from you and inflict %0.2f physical and %0.2f temporal (warp) damage.
 		The mines are hidden traps (%d detection and %d disarm power based on your Magic) and last for %d turns.
 		The damage caused by your Warp Mines will improve with your Spellpower.
 		Using this talent will trigger the cooldown on Warp Mine Away.]]):
-		format(damDesc(self, DamageType.TEMPORAL, damage), damDesc(self, DamageType.PHYSICAL, damage), detect, disarm, duration) 
+		format(damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage), detect, disarm, duration) 
 	end,
 }
 
 newTalent{
-	name = "Banish",
+	name = "Wormhole",
 	type = {"chronomancy/spacetime-folding", 2},
 	require = chrono_req2,
 	points = 5,
 	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
 	cooldown = 10,
 	tactical = { ESCAPE = 2 },
-	range = 0,
-	radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 5.5)) end,
-	getTeleport = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 8, 16)) end,
-	target = function(self, t)
-		return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false, talent=t}
-	end,
-	direct_hit = true,
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end,
+	radius = function(self, t) return math.floor(self:combatTalentLimit(t, 1, 7, 3)) end, -- Limit to radius 1
+	requires_target = true,
+	getDuration = function (self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(self:getTalentLevel(t), 6, 10))) end,
+	no_npc_use = true,
 	action = function(self, t)
-		local tg = self:getTalentTarget(t)
-		local hit = false
+		-- Target the entrance location
+		local tg = {type="bolt", nowarning=true, range=1, nolock=true, simple_dir_request=true, talent=t}
+		local entrance_x, entrance_y = self:getTarget(tg)
+		if not entrance_x or not entrance_y then return nil end
+		local _ _, entrance_x, entrance_y = self:canProject(tg, entrance_x, entrance_y)
+		local trap = game.level.map(entrance_x, entrance_y, engine.Map.TRAP)
+		if trap or game.level.map:checkEntity(entrance_x, entrance_y, Map.TERRAIN, "block_move") then game.logPlayer(self, "You can't place a wormhole entrance here.") return end
 
-		--checks for spacetime mastery hit bonus
-		local power = getParadoxSpellpower(self)
-		if self:knowTalent(self.T_SPACETIME_MASTERY) then
-			power = getParadoxSpellpower(self) * (1 + self:callTalent(self.T_SPACETIME_MASTERY, "getPower"))
-		end
+		-- Target the exit location
+		local tg = {type="hit", nolock=true, pass_terrain=true, nowarning=true, range=self:getTalentRange(t)}
+		local exit_x, exit_y = self:getTarget(tg)
+		if not exit_x or not exit_y then return nil end
+		local _ _, exit_x, exit_y = self:canProject(tg, exit_x, exit_y)
+		local trap = game.level.map(exit_x, exit_y, engine.Map.TRAP)
+		if trap or game.level.map:checkEntity(exit_x, exit_y, Map.TERRAIN, "block_move") or core.fov.distance(entrance_x, entrance_y, exit_x, exit_y) < 2 then game.logPlayer(self, "You can't place a wormhole exit here.") return end
 
-		self:project(tg, self.x, self.y, function(px, py)
-			local target = game.level.map(px, py, Map.ACTOR)
-			if not target or target == self then return end
-			game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport")
-			if self:checkHit(power, target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) and target:canBe("teleport") then
-				if not target:teleportRandom(target.x, target.y, self:getTalentRadius(t) * 4, self:getTalentRadius(t) * 2) then
-					game.logSeen(target, "The spell fizzles on %s!", target.name:capitalize())
-				else
-					target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=power*0.3})
-					game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport")
-					hit = true
-				end
-			else
-				game.logSeen(target, "%s resists the banishment!", target.name:capitalize())
-			end
-		end)
+		-- Wormhole values
+		local power = getParadoxSpellpower(self, t)
+		local dest_power = getParadoxSpellpower(self, t, 0.3)
 		
-		if not hit then
-			game:onTickEnd(function()
-				if not self:attr("no_talents_cooldown") then
-					self.talents_cd[self.T_BANISH] = self.talents_cd[self.T_BANISH] /2
-				end
-			end)
+		-- Our base wormhole
+		local function makeWormhole(x, y, dest_x, dest_y)
+			local wormhole = mod.class.Trap.new{
+				name = "wormhole",
+				type = "annoy", subtype="teleport", id_by_type=true, unided_name = "trap",
+				image = "terrain/wormhole.png",
+				display = '&', color_r=255, color_g=255, color_b=255, back_color=colors.STEEL_BLUE,
+				message = "@Target@ moves onto the wormhole.",
+				temporary = t.getDuration(self, t),
+				x = x, y = y, dest_x = dest_x, dest_y = dest_y,
+				radius = self:getTalentRadius(t),
+				canAct = false,
+				energy = {value=0},
+				disarm = function(self, x, y, who) return false end,
+				power = power, dest_power = dest_power,
+				summoned_by = self, -- "summoner" is immune to it's own traps
+				triggered = function(self, x, y, who)
+					local hit = who == self.summoned_by or who:checkHit(self.power, who:combatSpellResist()+(who:attr("continuum_destabilization") or 0), 0, 95) and who:canBe("teleport") -- Bug fix, Deprecrated checkhit call
+					if hit then
+						game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
+						if not who:teleportRandom(self.dest_x, self.dest_y, self.radius, 1) then
+							game.logSeen(who, "%s tries to enter the wormhole but a violent force pushes it back.", who.name:capitalize())
+						else
+							if who ~= self.summoned_by then who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self.dest_power}) end
+							game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
+							game:playSoundNear(self, "talents/teleport")
+						end
+					else
+						game.logSeen(who, "%s ignores the wormhole.", who.name:capitalize())
+					end
+					return true
+				end,
+				act = function(self)
+					self:useEnergy()
+					self.temporary = self.temporary - 1
+					if self.temporary <= 0 then
+						game.logSeen(self, "Reality asserts itself and forces the wormhole shut.")
+						if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
+						game.level:removeEntity(self)
+					end
+				end,
+			}
+			
+			return wormhole
 		end
+		
+		-- Adding the entrance wormhole
+		local entrance = makeWormhole(entrance_x, entrance_y, exit_x, exit_y)
+		game.level:addEntity(entrance)
+		entrance:identify(true)
+		entrance:setKnown(self, true)
+		game.zone:addEntity(game.level, entrance, "trap", entrance_x, entrance_y)
+		entrance.faction = nil
+		game:playSoundNear(self, "talents/heal")
 
-		game:playSoundNear(self, "talents/teleport")
+		-- Adding the exit wormhole
+		local exit = makeWormhole(exit_x, exit_y, entrance_x, entrance_y)
+		exit.x = exit_x
+		exit.y = exit_y
+		game.level:addEntity(exit)
+		exit:identify(true)
+		exit:setKnown(self, true)
+		game.zone:addEntity(game.level, exit, "trap", exit_x, exit_y)
+		exit.faction = nil
 
+		-- Linking the wormholes
+		entrance.dest = exit
+		exit.dest = entrance
+
+		game.logSeen(self, "%s folds the space between two points.", self.name)
 		return true
 	end,
 	info = function(self, t)
+		local duration = t.getDuration(self, t)
 		local radius = self:getTalentRadius(t)
-		local range = t.getTeleport(self, t)
-		return ([[Randomly teleports all targets within a radius of %d around you.  Targets will be teleported between %d and %d tiles from their current location.
-		If no targets are teleported the cooldown will be halved.
-		The chance of teleportion will scale with your Spellpower.]]):format(radius, range / 2, range)
+		local range = self:getTalentRange(t)
+		return ([[You fold the space between yourself and a second point within a range of %d, creating a pair of wormholes.  Any creature stepping on either wormhole will be teleported near the other (radius %d accuracy).  
+		The wormholes will last %d turns and must be placed at least two tiles apart.
+		The chance of teleporting enemies will scale with your Spellpower.]])
+		:format(range, radius, duration)
 	end,
 }
 
 newTalent{
-	name = "Temporal Wake",
+	name = "Spatial Tether",
 	type = {"chronomancy/spacetime-folding", 3},
 	require = chrono_req3,
 	points = 5,
-	random_ego = "attack",
-	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
+	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
 	cooldown = 10,
-	tactical = { ATTACK = {TEMPORAL = 1}, CLOSEIN = 2, DISABLE = { stun = 2 } },
-	direct_hit = true,
+	tactical = { DISABLE = 2 },
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end,
 	requires_target = true,
-	is_teleport = true,
+	getDuration = function (self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(self:getTalentLevel(t), 6, 10))) end,
+	getChance = function(self, t) return paradoxTalentScale(self, t, 10, 20, 30) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 200, getParadoxSpellpower(self, t)) end,
 	target = function(self, t)
-		return {type="beam", range=self:getTalentRange(t), friendlyfire=false, talent=t}
+		return {type="hit", range=self:getTalentRange(t), nowarning=true, talent=t}
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self)) end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end,
-	range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 10, 0.5, 0, 1)) end,
+	no_energy=true,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		if not self:hasLOS(x, y) or game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then
-			game.logSeen(self, "You do not have line of sight.")
-			return nil
-		end
 		local _ _, x, y = self:canProject(tg, x, y)
-		local ox, oy = self.x, self.y
+		local target = game.level.map(x, y, Map.ACTOR)
+		if not target then return end
+		if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then return end
+		
+		-- Tether values
+		local power = getParadoxSpellpower(self, t)
+		local dest_power = getParadoxSpellpower(self, t, 0.3)
 		
-		-- since we're using a precise teleport we'll look for a free grid first
-		local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true})
-		if tx and ty then
-			if not self:teleportRandom(tx, ty, 0) then
-				game.logSeen(self, "The teleport fizzles!")
-			else
-				local dam = self:spellCrit(t.getDamage(self, t))
-				local x, y = ox, oy
-				self:project(tg, x, y, function(px, py)
-					DamageType:get(DamageType.MATTER).projector(self, px, py, DamageType.MATTER, dam)
-					local target = game.level.map(px, py, Map.ACTOR)
-					if target then
-						if target:canBe("stun") then
-							target:setEffect(target.EFF_STUNNED, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self)})
+		-- Store the old terrain
+		local oe = game.level.map(target.x, target.y, engine.Map.TERRAIN)
+		if not oe or oe:attr("temporary") then return true end
+	
+		-- Make our tether
+		local tether = mod.class.Object.new{
+			old_feat = oe, type = oe.type, subtype = oe.subtype,
+			name = "temporal instability", image = oe.image, add_mos = {{image="object/temporal_instability.png"}},
+			display = '&', color=colors.LIGHT_BLUE,
+			temporary = t.getDuration(self, t), 
+			power = power, dest_power = dest_power, chance = t.getChance(self, t),
+			x = x, y = y, target = target,
+			summoner = self, summoner_gain_exp = true,
+			canAct = false,
+			energy = {value=0},
+			act = function(self)
+				self:useEnergy()
+				self.temporary = self.temporary - 1
+				
+				-- Teleport
+				if not self.target.dead and (game.level and game.level:hasEntity(self.target)) then
+					local hit = self.summoner == self.target or (self.summoner:checkHit(self.power, self.target:combatSpellResist() + (self.target:attr("continuum_destabilization") or 0), 0, 95) and self.target:canBe("teleport"))
+					if hit and rng.percent(self.chance * core.fov.distance(self.x, self.y, self.target.x, self.target.y)) then	
+						game.level.map:particleEmitter(self.target.x, self.target.y, 1, "temporal_teleport")
+						-- Since we're using a precise teleport, find a free grit first
+						local tx, ty = util.findFreeGrid(self.x, self.y, 5, true, {[engine.Map.ACTOR]=true})
+						if not self.target:teleportRandom(tx, ty, 1, 0) then
+							game.logSeen(self, "The teleport fizzles!")
 						else
-							game.logSeen(target, "%s resists the stun!", target.name:capitalize())
+							if target ~= self.summoner then 
+								self.target:setEffect(self.target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self.dest_power})
+							end
+							game.level.map:particleEmitter(self.target.x, self.target.y, 1, "temporal_teleport")
+							game:playSoundNear(self, "talents/teleport")
 						end
+					else
+						game.logSeen(self.target, "%s resists the teleport!", self.target.name:capitalize())
 					end
-				end)
-				game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "temporal_lightning", {tx=x-self.x, ty=y-self.y})
-				game:playSoundNear(self, "talents/lightning")
-			end
-		end
+				end
+				
+				-- End the effect?
+				if self.temporary <= 0 then
+					game.level.map(self.x, self.y, engine.Map.TERRAIN, self.old_feat)
+					game.nicer_tiles:updateAround(game.level, self.target.x, self.target.y)
+					game.level:removeEntity(self)
+				end
+			end,
+		}
+		
+		-- add our tether to the map
+		game.level:addEntity(tether)
+		game.level.map(x, y, Map.TERRAIN, tether)
+		game.nicer_tiles:updateAround(game.level, x, y)
+		game.level.map:updateMap(x, y)
+		game:playSoundNear(self, "talents/heal")
 		
 		return true
 	end,
 	info = function(self, t)
-		local stun = t.getDuration(self, t)
-		local damage = t.getDamage(self, t)/2
-		return ([[Violently fold the space between yourself and another point within range.
-		You move to the target location, and leave a temporal wake behind that stuns for %d turns and inflicts %0.2f temporal and %0.2f physical damage to everything in the path.
-		The damage will scale with your Spellpower.]]):
-		format(stun, damDesc(self, DamageType.TEMPORAL, damage), damDesc(self, DamageType.PHYSICAL, damage))
+		local duration = t.getDuration(self, t)
+		local chance = t.getChance(self, t)
+		return ([[Tethers the target to the location for %d turns.  For each tile the target moves away from the target location it has a %d%% chance each turn of being teleported back to the tether.
+		The teleportation chance scales with your Spellpower.]])
+		:format(duration, chance)
 	end,
 }
 
@@ -407,12 +465,12 @@ newTalent{
 	require = chrono_req4,
 	points = 5,
 	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
-	cooldown = 24,
+	cooldown = 12,
 	tactical = { DISABLE = 2 },
-	range = 10,
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end,
 	radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4.5)) end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self)) end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 6, 10))) end,
 	target = function(self, t)
 		return {type="ball", range=self:getTalentRange(t), friendlyfire=false, radius=self:getTalentRadius(t), talent=t}
 	end,
@@ -422,16 +480,19 @@ newTalent{
 		local tg = self:getTalentTarget(t)
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		local _ _, x, y = self:canProject(tg, x, y)
-		
-		local damage = self:spellCrit(t.getDamage(self, t))
-		self:project(tg, x, y, function(px, py)
-			local target = game.level.map(px, py, Map.ACTOR)
-			if not target then return end
-			target:setEffect(target.EFF_DIMENSIONAL_ANCHOR, t.getDuration(self, t), {damage=damage, src=self, apply_power=getParadoxSpellpower(self)})
-		end)
+		local _ _, _, _, x, y = self:canProject(tg, x, y)
+
+		-- Add a lasting map effect
+		local dam = self:spellCrit(t.getDamage(self, t))
+		game.level.map:addEffect(self,
+			x, y, t.getDuration(self,t),
+			DamageType.DIMENSIONAL_ANCHOR, {dam=dam, dur=1, src=self, apply=getParadoxSpellpower(self, t)},
+			self:getTalentRadius(t),
+			5, nil,
+			{type="temporal_cloud"},
+			nil, false, false
+		)
 
-		game.level.map:particleEmitter(x, y, tg.radius, "ball_teleport", {radius=tg.radius})
 		game:playSoundNear(self, "talents/teleport")
 
 		return true
@@ -440,7 +501,7 @@ newTalent{
 		local damage = t.getDamage(self, t)/2
 		local radius = self:getTalentRadius(t)
 		local duration = t.getDuration(self, t)
-		return ([[Anchors enemies in a radius of %d.  Anchored targets will be prevented from teleporting for %d turns and take %0.2f temporal and %0.2f physical damage on teleport attempts.
-		The damage will scale with your Spellpower.]]):format(radius, duration, damDesc(self, DamageType.TEMPORAL, damage), damDesc(self, DamageType.PHYSICAL, damage))
+		return ([[Create a radius %d anti-telport field for %d turns.  Enemies in the field will be anchored, preventing teleportation and taking %0.2f physical and %0.2f temporal (warp) damage on teleport attempts.
+		The damage will scale with your Spellpower.]]):format(radius, duration, damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage))
 	end,
 }
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua b/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua
index 7662ceef24c444b4054e6006d8b11e8793875ec3..07e3effa385fa4238946ba315b77108684292f72 100644
--- a/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua
+++ b/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua
@@ -17,7 +17,7 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
--- EDGE TODO: Icons, Particles, Timed Effect Particles
+-- EDGE TODO: Particles, Timed Effect Particles
 
 newTalent{
 	name = "Dimensional Step",
@@ -27,261 +27,180 @@ newTalent{
 	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
 	cooldown = 10,
 	tactical = { CLOSEIN = 2, ESCAPE = 2 },
-	range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 10, 0.5, 0, 1)) end,
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end,
 	requires_target = true,
 	target = function(self, t)
-		return {type="hit", range=self:getTalentRange(t), nolock=true, nowarning=true}
+		return {type="hit", nolock=true, range=self:getTalentRange(t)}
 	end,
 	direct_hit = true,
 	no_energy = true,
 	is_teleport = true,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
-		local x, y = self:getTarget(tg)
+		local x, y, target = self:getTarget(tg)
 		if not x or not y then return nil end
-		if not self:hasLOS(x, y) or game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then
+		if not self:hasLOS(x, y) or game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then -- To prevent teleporting through walls
 			game.logSeen(self, "You do not have line of sight.")
 			return nil
 		end
-		local __, x, y = self:canProject(tg, x, y)
-
-		game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
-
-		-- since we're using a precise teleport we'll look for a free grid first
-		local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true})
-		if tx and ty then
-			if not self:teleportRandom(tx, ty, 0) then
-				game.logSeen(self, "The spell fizzles!")
+		local _ _, x, y = self:canProject(tg, x, y)
+		
+		-- Swap?
+		if self:getTalentLevel(t) >= 5 and target then
+			-- Hit?
+			if target:canBe("teleport") and self:checkHit(getParadoxSpellpower(self, t), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) then
+				-- Grab the caster's location
+				local ox, oy = self.x, self.y
+			
+				-- Remove the target so the destination tile is empty
+				game.level.map:remove(target.x, target.y, Map.ACTOR)
+				
+				-- Try to teleport to the target's old location
+				if self:teleportRandom(x, y, 0) then
+					-- Put the target back in the caster's old location
+					game.level.map(ox, oy, Map.ACTOR, target)
+					target.x, target.y = ox, oy
+					
+					game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport")
+					game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
+				else
+					-- If we can't teleport, return the target
+					game.level.map(target.x, target.y, Map.ACTOR, target)
+					game.logSeen(self, "The spell fizzles!")
+				end
+			else
+				game.logSeen(target, "%s resists the swap!", target.name:capitalize())
+			end
+		else
+			game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
+			-- since we're using a precise teleport we'll look for a free grid first
+			local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true})
+			if tx and ty then
+				if not self:teleportRandom(tx, ty, 0) then
+					game.logSeen(self, "The spell fizzles!")
+				else
+					game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
+				end
 			end
 		end
-
-		game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
+		
 		game:playSoundNear(self, "talents/teleport")
-
 		return true
 	end,
 	info = function(self, t)
 		local range = self:getTalentRange(t)
-		return ([[Teleports you to up to %d tiles away, to a targeted location in line of sight.  Additional talent points increase the range.
-		This spell takes no time to cast.]]):format(range)
+		return ([[Teleports you to up to %d tiles away, to a targeted location in line of sight.
+		At talent level 5 you may swap positions with a target creature.]]):format(range)
 	end,
 }
 
 newTalent{
-	name = "Phase Shift",
+	name = "Dimensional Shift",
 	type = {"chronomancy/spacetime-weaving", 2},
 	mode = "passive",
 	require = chrono_req2,
 	points = 5,
-	getChance = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)) end,
-	doPhaseShift = function(self, t)
+	getReduction = function(self, t) return math.ceil(self:getTalentLevel(t)) end,
+	getCount = function(self, t)
+		return 1 + math.floor(self:combatTalentLimit(t, 3, 0, 2))
+	end,
+	doShift = function(self, t)
 		local effs = {}
-		-- Go through all effects
 		for eff_id, p in pairs(self.tmp) do
 			local e = self.tempeffect_def[eff_id]
-			if e.status == "detrimental" and e.type ~= "other" then
-				effs[#effs+1] = {"effect", eff_id}
+			if e.type ~= "other" and e.status == "detrimental" and e.subtype ~= "cross tier" then
+				effs[#effs+1] = p
 			end
 		end
-
-		-- Roll to remove each one
-		for i = 1, #effs do
-			if #effs == 0 then break end
+		
+		for i=1, t.getCount(self, t) do
 			local eff = rng.tableRemove(effs)
-			if eff[1] == "effect" and rng.percent(t.getChance(self, t)) then
-				self:removeEffect(eff[2])
+			if not eff then break end
+			eff.dur = eff.dur - t.getReduction(self, t)
+			if eff.dur <= 0 then
+				self:removeEffect(eff.effect_id)
 			end
 		end
+
 	end,
 	info = function(self, t)
-		local chance = t.getChance(self, t)
-		return ([[When you teleport you have a %d%% chance to remove each detrimental status effect currently affecting you.
-		Each effect is checked individually and the chance scales with your Spellpower.]]):
-		format(chance)
+		local count = t.getCount(self, t)
+		local reduction = t.getReduction(self, t)
+		return ([[When you teleport you reduce the duration of up to %d detrimental effects by %d turns.]]):
+		format(count, reduction)
 	end,
 }
 
 newTalent{
-	name = "Wormhole",
+	name = "Phase Shift",
 	type = {"chronomancy/spacetime-weaving", 3},
 	require = chrono_req3,
 	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
-	cooldown = 10,
-	tactical = { ESCAPE = 2 },
-	range = 10,
-	radius = function(self, t) return math.floor(self:combatTalentLimit(t, 0, 7, 3)) end, -- Limit to radius 0
-	requires_target = true,
-	getDuration = function (self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 6, 10)) end,
-	no_npc_use = true,
+	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
+	cooldown = 24,
+	tactical = { DEFEND = 2 },
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentLimit(t, 25, 3, 7, true))) end,
 	action = function(self, t)
-		local tg = {type="bolt", nowarning=true, range=1, nolock=true, simple_dir_request=true, talent=t}
-		local entrance_x, entrance_y = self:getTarget(tg)
-		if not entrance_x or not entrance_y then return nil end
-		local _ _, entrance_x, entrance_y = self:canProject(tg, entrance_x, entrance_y)
-		local trap = game.level.map(entrance_x, entrance_y, engine.Map.TRAP)
-		if trap or game.level.map:checkEntity(entrance_x, entrance_y, Map.TERRAIN, "block_move") then game.logPlayer(self, "You can't place a wormhole entrance here.") return end
-
-		-- Finding the exit location
-		-- First, find the center possible exit locations
-		local x, y, radius, minimum_distance
-		radius = self:getTalentRadius(t)
-		minimum_distance = 0
-		local tg = {type="ball", nolock=true, pass_terrain=true, nowarning=true, range=self:getTalentRange(t), radius=radius}
-		x, y = self:getTarget(tg)
-		print("[Target]", x, y)
-		if not x then return nil end
-		-- Make sure the target is within range
-		if core.fov.distance(self.x, self.y, x, y) > self:getTalentRange(t) then
-			game.logPlayer(self, "Pick a valid location.")
-			return false
-		end
-
-		-- Second, select one of the possible exit locations
-		local poss = {}
-		for i = x - radius, x + radius do
-			for j = y - radius, y + radius do
-				if game.level.map:isBound(i, j) and
-					core.fov.distance(x, y, i, j) <= radius and
-					core.fov.distance(x, y, i, j) >= minimum_distance and
-					self:canMove(i, j) and not game.level.map(i, j, engine.Map.TRAP) then
-					poss[#poss+1] = {i,j}
-				end
-			end
-		end
-		if #poss == 0 then game.logPlayer(self, "No exit location could be found.")	return false end
-		local pos = poss[rng.range(1, #poss)]
-		local exit_x, exit_y = pos[1], pos[2]
-		print("[[wormhole]] entrance ", entrance_x, " :: ", entrance_y)
-		print("[[wormhole]] exit ", exit_x, " :: ", exit_y)
-
-		--checks for spacetime mastery hit bonus
-		local power = getParadoxSpellpower(self)
-		if self:knowTalent(self.T_SPACETIME_MASTERY) then
-			power = power * (1 + self:callTalent(self.T_SPACETIME_MASTERY, "getPower"))
-		end
-
-		-- Adding the entrance wormhole
-		local entrance = mod.class.Trap.new{
-			name = "wormhole",
-			type = "annoy", subtype="teleport", id_by_type=true, unided_name = "trap",
-			image = "terrain/wormhole.png",
-			display = '&', color_r=255, color_g=255, color_b=255, back_color=colors.STEEL_BLUE,
-			message = "@Target@ moves onto the wormhole.",
-			temporary = t.getDuration(self, t),
-			x = entrance_x, y = entrance_y,
-			canAct = false,
-			energy = {value=0},
-			disarm = function(self, x, y, who) return false end,
-			power = power,
-			summoned_by = self, -- "summoner" is immune to it's own traps
-			triggered = function(self, x, y, who)
-				if who == self.summoned_by or who:checkHit(self.power, who:combatSpellResist()+(who:attr("continuum_destabilization") or 0), 0, 95) and who:canBe("teleport") then -- Bug fix, Deprecrated checkhit call
-					-- since we're using a precise teleport we'll look for a free grid first
-					local tx, ty = util.findFreeGrid(self.dest.x, self.dest.y, 5, true, {[engine.Map.ACTOR]=true})
-					if tx and ty then
-						game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
-						if not who:teleportRandom(tx, ty, 0) then
-							game.logSeen(who, "%s tries to enter the wormhole but a violent force pushes it back.", who.name:capitalize())
-						elseif who ~= self.summoned_by then
-							who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self.destabilization_power})
-							game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport")
-						end
-					end
-				else
-					game.logSeen(who, "%s ignores the wormhole.", who.name:capitalize())
-				end
-				return true
-			end,
-			act = function(self)
-				self:useEnergy()
-				self.temporary = self.temporary - 1
-				if self.temporary <= 0 then
-					game.logSeen(self, "Reality asserts itself and forces the wormhole shut.")
-					if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
-					game.level:removeEntity(self)
-				end
-			end,
-		}
-		entrance.faction = nil
-		game.level:addEntity(entrance)
-		entrance:identify(true)
-		entrance:setKnown(self, true)
-		game.zone:addEntity(game.level, entrance, "trap", entrance_x, entrance_y)
-		game.level.map:particleEmitter(entrance_x, entrance_y, 1, "temporal_teleport")
-		game:playSoundNear(self, "talents/heal")
-
-		-- Adding the exit wormhole
-		local exit = entrance:clone()
-		exit.x = exit_x
-		exit.y = exit_y
-		game.level:addEntity(exit)
-		exit:identify(true)
-		exit:setKnown(self, true)
-		game.zone:addEntity(game.level, exit, "trap", exit_x, exit_y)
-		game.level.map:particleEmitter(exit_x, exit_y, 1, "temporal_teleport")
-
-		-- Linking the wormholes
-		entrance.dest = exit
-		exit.dest = entrance
-
-		game.logSeen(self, "%s folds the space between two points.", self.name)
+		self:setEffect(self.EFF_PHASE_SHIFT, t.getDuration(self, t), {})
+		game:playSoundNear(self, "talents/teleport")
 		return true
 	end,
 	info = function(self, t)
 		local duration = t.getDuration(self, t)
-		local radius = self:getTalentRadius(t)
-		return ([[You fold the space between yourself and a second point within range (radius %d accuracy), creating a pair of wormholes.  Any creature stepping on either wormhole will be teleported to the other.  The wormholes will last %d turns.
-		The chance of teleporting enemies will scale with your Spellpower.]])
-		:format(radius, duration)
+		return ([[Phase shift yourself for %d turns; any damage greater than 10%% of your maximum life will teleport you to an adjacent tile and be reduced by 50%% (can only happen once per turn).]]):
+		format(duration)
 	end,
 }
 
 newTalent{
-	name = "Displace Damage",
+	name = "Phase Pulse",
 	type = {"chronomancy/spacetime-weaving", 4},
-	mode = "sustained",
 	require = chrono_req4,
-	sustain_paradox = 48,
+	tactical = { ATTACKAREA = {TEMPORAL = 1, PHYSICAL = 1} },
+	mode = "sustained",
+	sustain_paradox = 36,
 	cooldown = 10,
-	tactical = { BUFF = 2 },
 	points = 5,
-	-- called by _M:onTakeHit function in mod\class\Actor.lua to perform the damage displacment
-	getDisplaceDamage = function(self, t) return self:combatTalentLimit(t, 25, 5, 15)/100 end, -- Limit < 25%
-	range = 10,
-	callbackOnTakeDamage = function(self, t, src, x, y, type, dam, tmp, no_martyr)
-		if dam > 0 and src ~= self then
-			-- find available targets
-			local tgts = {}
-			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
-					tgts[#tgts+1] = a
-				end
-			end end
-
-			-- Displace the damage
-			local a = rng.table(tgts)
-			if a then
-				local displace = dam * t.getDisplaceDamage(self, t)
-				game:delayedLogMessage(self, a, "displace_damage"..(a.uid or ""), "#PINK##Source# displaces some damage onto #Target#!")
-				DamageType.defaultProjector(self, a.x, a.y, type, displace, tmp, true)
-				dam = dam - displace
-			end
-		end
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 70, getParadoxSpellpower(self, t)) end,
+	range = 0,
+	radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.5, 3.5)) end,
+	target = function(self, t)
+		return {type="ball", range=100, radius=self:getTalentRadius(t), selffire=false, talent=t}
+	end,
+	doPulse = function(self, t, ox, oy, fail)
+		local tg = self:getTalentTarget(t)
+		local dam = self:spellCrit(t.getDamage(self, t))
+		local distance = core.fov.distance(self.x, self.y, ox, oy)
+		local chance = distance * 10
 		
-		return {dam=dam}
+		if not fail then
+			dam = dam * (1 + math.min(1, distance/10))
+			game:onTickEnd(function()
+				self:project(tg, ox, oy, DamageType.WARP, dam)
+				self:project(tg, self.x, self.y, DamageType.WARP, dam)
+			end)
+		else
+			dam = dam *2
+			chance = 100
+			tg.radius = tg.radius * 2
+			game:onTickEnd(function()
+				self:project(tg, self.x, self.y, DamageType.WARP, dam)
+			end)
+		end
 	end,
 	activate = function(self, t)
+		game:playSoundNear(self, "talents/spell_generic")
 		return {}
 	end,
 	deactivate = function(self, t, p)
 		return true
 	end,
 	info = function(self, t)
-		local displace = t.getDisplaceDamage(self, t) * 100
-		return ([[You bend space around you, displacing %d%% of any damage you receive onto a random enemy within range.
-		]]):format(displace)
+		local damage = t.getDamage(self, t)/2
+		local radius = self:getTalentRadius(t)
+		return ([[When you teleport with Phase Pulse active you deal %0.2f physical and %0.2f temporal (warp) damage to all targets in a radius of %d around you.
+		For each space you move from your original location the damage is increased by 10%% (to a maximum bonus of 100%%).  If the teleport fails, the blast radius and damage will be doubled.
+		This effect occurs both at the entrance and exist locations and the damage will scale with your Spellpower.]]):
+		format(damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage), radius)
 	end,
 }
diff --git a/game/modules/tome/data/talents/chronomancy/spatial-tears.lua b/game/modules/tome/data/talents/chronomancy/spatial-tears.lua
new file mode 100644
index 0000000000000000000000000000000000000000..a632e4b1508f9db9b7e06148742a009e884be087
--- /dev/null
+++ b/game/modules/tome/data/talents/chronomancy/spatial-tears.lua
@@ -0,0 +1,311 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 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
+
+-- EDGE TODO: Particles, Timed Effect Particles
+
+local Object = require "mod.class.Object"
+
+newTalent{
+	name = "Spatial Fragments",
+	type = {"chronomancy/spatial-tears",1},
+	require = chrono_req_high1,
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
+	cooldown = 3,
+	tactical = { ATTACK = { TEMPORAL = 1, PHYSICAL = 1 }, },
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end,
+	proj_speed = 4,
+	target = function(self, t)
+		return {type="bolt", range=self:getTalentRange(t), talent=t, nowarning=true, display={particle="arrow", particle_args={tile=("particles_images/spatial_fragment"):format(rng.range(1, 4))}}}
+	end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 100, getParadoxSpellpower(self, t)) end,
+	action = function(self, t)
+		local p = self:isTalentActive(self.T_FRACTURED_SPACE)
+		
+		local tg = self:getTalentTarget(t)
+		-- Beam?
+		local beam = self:isTalentActive(self.T_FRACTURED_SPACE) and self:isTalentActive(self.T_FRACTURED_SPACE).charges >=6 or false
+		if beam then
+			tg.type = "beam"
+		end
+		local x, y, target = self:getTarget(tg)
+		if not x or not y then return nil end
+		if beam then self:isTalentActive(self.T_FRACTURED_SPACE).charges = 0 end
+		
+		-- Fire one bolt per available target
+		if target == self then
+			-- Find available targets
+			local tgts = {}
+			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
+					tgts[#tgts+1] = a
+				end
+			end end
+			
+			-- Fire a bolt at each one			
+			local dam = self:spellCrit(t.getDamage(self, t))
+			for i = 1, 3 do
+				if #tgts <= 0 then break end
+				local a, id = rng.table(tgts)
+				table.remove(tgts, id)
+				self:projectile(tg, a.x, a.y, DamageType.WARP, dam, nil)
+			end
+		else
+			-- Fire all bolts at one target
+			local dam = self:spellCrit(t.getDamage(self, t))
+			for i = 1, 3 do
+				self:projectile(tg, x, y, DamageType.WARP, dam, nil)
+			end
+		end
+	
+		game:playSoundNear(self, "talents/earth")
+		return true
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)/2
+		return ([[Fire three Spatial Fragments at the target that each inflict %0.2f physical and %0.2f temporal (warp) damage.  If you target yourself you'll instead fire one Spatial Fragment at up to three targets within range.
+		If Fractured Space is fully charged the projectiles will be able to pierce through targets.  This will consume your Fractured Space charges.
+		The damage scales with your Spellpower.]])
+		:format(damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage))
+	end,
+}
+
+newTalent{
+	name = "Discontinuity",
+	type = {"chronomancy/spatial-tears", 2},
+	require = chrono_req_high2,
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
+	cooldown = 6,
+	tactical = { ATTACK = { TEMPORAL = 1, PHYSICAL = 1 }, },
+	range = 10,
+	direct_hit = true,
+	requires_target = true,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 50) end,
+	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end,
+	getLength = function(self, t) return 1 + math.floor(self:combatTalentScale(t, 3, 7)/2)*2 end,
+	target = function(self, t)
+		local halflength = math.floor(t.getLength(self,t)/2)
+		local block = function(_, lx, ly)
+			return game.level.map:checkAllEntities(lx, ly, "block_move")
+		end
+		return {type="wall", range=self:getTalentRange(t), halflength=halflength, talent=t, halfmax_spots=halflength+1, block_radius=block}
+	end,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local x, y = self:getTarget(tg)
+		if not x or not y then return nil end
+		local _ _, _, _, x, y = self:canProject(tg, x, y)
+		if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then return nil end
+		
+		local damage = self:spellCrit(t.getDamage(self, t))
+		local block = self:isTalentActive(self.T_FRACTURED_SPACE) and self:isTalentActive(self.T_FRACTURED_SPACE).charges >=6 or false
+		if block then self:isTalentActive(self.T_FRACTURED_SPACE).charges = 0 end
+		self:project(tg, x, y, function(px, py, tg, self)
+			local oe = game.level.map(px, py, Map.TERRAIN)
+			if not oe or oe.special then return end
+			if not oe or oe:attr("temporary") or game.level.map:checkAllEntities(px, py, "block_move") then return end
+			local e = Object.new{
+				old_feat = oe,
+				type = "void", subtype = "void",
+				name = "discontinuity",
+				display = ' ', image = ("terrain/rift/rift_floor_0%d.png"):format(rng.avg(0, 4, 3)),
+				_noalpha = false,
+				always_remember = true,
+				does_block_move = block,
+				block_move = block,
+				pass_projectile = true,
+				is_void = true,
+				can_pass = {pass_void=1},
+				show_tooltip = true,
+				temporary = t.getDuration(self, t),
+				x = px, y = py,
+				dam = damage,
+				canAct = false,
+				act = function(self)
+					local tg = {type="ball", range=0, friendlyfire=false, radius = 1, talent=t, x=self.x, y=self.y,}
+					self.summoner.__project_source = self
+					local grids = self.summoner:project(tg, self.x, self.y, engine.DamageType.WARP, self.dam)
+					if core.shader.active() then
+						game.level.map:particleEmitter(self.x, self.y, tg.radius, "starfall", {radius=tg.radius, tx=self.x, ty=self.y})
+					else
+						game.level.map:particleEmitter(self.x, self.y, tg.radius, "shadow_flash", {radius=tg.radius, grids=grids, tx=self.x, ty=self.y})
+					end
+					self.summoner.__project_source = nil
+					self:useEnergy()
+					self.temporary = self.temporary - 1
+					if self.temporary <= 0 then
+						game.level.map(self.x, self.y, engine.Map.TERRAIN, self.old_feat)
+						game.level:removeEntity(self)
+						game.level.map:updateMap(self.x, self.y)
+						game.nicer_tiles:updateAround(game.level, self.x, self.y)
+					end
+				end,
+				dig = function(src, x, y, old)
+					game.level:removeEntity(old)
+					return nil, old.old_feat
+				end,
+				summoner_gain_exp = true,
+				summoner = self,
+			}
+			e.tooltip = mod.class.Grid.tooltip
+			game.level:addEntity(e)
+			game.level.map(px, py, Map.TERRAIN, e)
+			if not block then
+				game.nicer_tiles:updateAround(game.level, px, py)
+				game.level.map:updateMap(px, py)
+			end
+		end)
+		
+		return true
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)/2
+		local length = t.getLength(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[Create a void wall of length %d that lasts %d turns.  Each turn the wall deals %0.2f physical and %0.2f temporal (warp) damage to all enemies within a radius of 1.
+		If Fractured Space is fully charged the wall will block movement, but not sight or projectiles.  This will consume your Fractured Space charges.
+		The damage will scale with your Spellpower.]])
+		:format(length, duration, damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage))
+	end,
+}
+
+newTalent{
+	name = "Fractured Space",
+	type = {"chronomancy/spatial-tears",3},
+	require = chrono_req_high3,
+	mode = "sustained",
+	sustain_paradox = 24,
+	cooldown = 10,
+	tactical = { BUFF = 2 },
+	points = 5,
+	getDamage = function(self, t) return self:combatTalentLimit(t, 100, 10, 75)/12 end,
+	getChance = function(self, t) return self:combatTalentLimit(t, 100, 10, 75)/6 end,
+	iconOverlay = function(self, t, p)
+		local val = p.charges or 0
+		if val <= 0 then return "" end
+		local fnt = "buff_font"
+		return tostring(math.ceil(val)), fnt
+	end,
+	callbackOnActBase = function(self, t)
+		-- Charge decay
+		local p = self:isTalentActive(self.T_FRACTURED_SPACE)
+		p.decay = p.decay + 1
+		if p.decay >=2 then
+			p.decay = 0
+			p.charges = math.max(p.charges - 1, 0)
+		end
+	end,
+	activate = function(self, t)
+		game:playSoundNear(self, "talents/heal")
+		--local particle = Particles.new("ultrashield", 1, { rm=0, rM=176, gm=196, gM=255, bm=222, bM=255, am=25, aM=125, radius=0.2, density=30, life=28, instop=-40})
+		return {
+			charges = 0, decay = 0
+		--	particle = self:addParticles(particle)
+		}
+	end,
+	deactivate = function(self, t, p)
+	--	self:removeParticles(p.particle)
+		return true
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)
+		local chance = t.getChance(self, t)
+		local charges = self:isTalentActive(self.T_FRACTURED_SPACE) and self:isTalentActive(self.T_FRACTURED_SPACE).charges or 0
+		return ([[Each time you deal warp damage Fractured Space gains one charge, up to a maximum of six charges.  If you're not generating charges one charge will decay every other turn.
+		Each charge increases warp damage by %d%% and gives your Warp damage a %d%% chance to stun, blind, pin, or confuse affected targets for 3 turns.
+		If Fractured Space is fully charged, your Spatial Tears talents will consume them when cast and have bonus effects (see indvidual talent descriptions).
+		
+		Current damage bonus:   %d%%
+		Current effect chance:  %d%%]]):format(damage, chance, damage * charges, chance * charges)
+	end,
+}
+
+newTalent{
+	name = "Sphere of Destruction",
+	type = {"chronomancy/spatial-tears", 4},
+	require = chrono_req_high4,
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 18) end,
+	cooldown = 18,
+	tactical = { ATTACKAREA = {PHYSICAL = 2, TEMPORAL = 2} },
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end,
+	radius = 3,
+	proj_speed = 3,
+	target = function(self, t)
+		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t}
+	end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 100, getParadoxSpellpower(self, t)) end,
+	action = function(self, t)
+		local tg = {type="beam", range=self:getTalentRange(t), talent=t, display={particle="icestorm"}}
+		local x, y = self:getTarget(tg)
+		if not x or not y  then return nil end
+		local _ _, _, _, x, y = self:canProject(tg, x, y)
+		
+		-- Store some values inside the target table so they're not lost when we reload
+		tg.chrono_sphere = {}
+		tg.chrono_sphere.dam = self:spellCrit(t.getDamage(self, t))
+		tg.chrono_sphere.strip = self:isTalentActive(self.T_FRACTURED_SPACE) and self:isTalentActive(self.T_FRACTURED_SPACE).charges >=6 or false
+		if tg.chrono_sphere.strip then self:isTalentActive(self.T_FRACTURED_SPACE).charges = 0; tg.chrono_sphere.power = getParadoxSpellpower(self, t) end
+		
+		-- A beam projectile
+		self:projectile(tg, x, y, function(px, py, tg, self)
+			-- That projects balls as it moves
+			local tg2 = self:getTalentTarget(self:getTalentFromId(self.T_SPHERE_OF_DESTRUCTION))
+			self:project(tg2, px, py, function(px2, py2)
+				local DamageType = require "engine.DamageType"
+				DamageType:get(DamageType.WARP).projector(self, px2, py2, DamageType.WARP, tg.chrono_sphere.dam)
+				
+				-- Do we strip a sustain?
+				if tg.chrono_sphere.strip then
+					local target = game.level.map(px2, py2, engine.Map.ACTOR)
+					if not target then return end
+
+					local effs = {}
+					-- Go through all sustained spells
+					for tid, act in pairs(target.sustain_talents) do
+						if act then
+							effs[#effs+1] = {"talent", tid}
+						end
+					end
+
+					if #effs == 0 then return end
+					local eff = rng.table(effs)
+
+					if self:checkHit(tg.chrono_sphere.power, target:combatSpellResist(), 0, 95, 5) then
+						target:crossTierEffect(target.EFF_SPELLSHOCKED, tg.chrono_sphere.power)
+						target:forceUseTalent(eff[2], {ignore_energy=true})
+					end
+				end
+			end)
+		end)
+		
+		game:playSoundNear(self, "talents/icestorm")
+		return true
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)/2
+		return ([[Create a Sphere of Destruction that travels towards the target location, inflicting %0.2f physical and %0.2f temporal (warp) damage in a radius of three.
+		If Fractured Space is fully charged the Sphere will remove a single sustain from targets it damages.  This will consume your Fractured Space charges.
+		The sphere can hit a single target multiple times in one turn and the damage will scale with your Spellpower.]])
+		:format(damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage))
+	end,
+}
diff --git a/game/modules/tome/data/talents/chronomancy/speed-control.lua b/game/modules/tome/data/talents/chronomancy/speed-control.lua
index 254cc935943bb6ef9d7963bb4afe78cd8a8612ab..acbd0d107ce320cf708386123ebeeeda24bd9857 100644
--- a/game/modules/tome/data/talents/chronomancy/speed-control.lua
+++ b/game/modules/tome/data/talents/chronomancy/speed-control.lua
@@ -17,7 +17,7 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
--- EDGE TODO: Icons, Particles, Timed Effect Particles
+-- EDGE TODO: Particles, Timed Effect Particles
 
 newTalent{
 	name = "Celerity",
@@ -25,19 +25,18 @@ newTalent{
 	require = chrono_req1,
 	points = 5,
 	mode = "passive",
-	getSpeed = function(self, t) return self:combatTalentScale(t, 0.1, 0.25, 0.5) end,
-	doCelerity = function(self, t)
-		local speed = t.getSpeed(self, t)
-		
-		self:setEffect(self.EFF_CELERITY, 3, {speed=speed, charges=1, max_charges=3})
-		
+	getSpeed = function(self, t) return self:combatTalentScale(t, 10, 30)/100 end,
+	doCelerity = function(self, t, x, y)
+		if self.x ~= x or self.y ~= x then
+			local speed = t.getSpeed(self, t)
+			self:setEffect(self.EFF_CELERITY, 1, {speed=speed, charges=1, max_charges=3})
+		end
 		return true
 	end,
 	info = function(self, t)
 		local speed = t.getSpeed(self, t) * 100
-		return ([[When you move you gain %d%% movement speed, stacking up to three times.
-		Performing any action other than movement will break this effect.]]):
-		format(speed, speed)
+		return ([[When you move you gain %d%% movement speed for one turn.  This effect stacks up to three times.
+		]]):format(speed)
 	end,
 }
 
@@ -48,11 +47,12 @@ newTalent{
 	points = 5,
 	sustain_paradox = 36,
 	mode = "sustained",
+	no_sustain_autoreset = true,
 	cooldown = 12,
 	tactical = { ATTACKAREA = 1, DISABLE = 3 },
 	range = 0,
-	radius = 3,
-	getSlow = function(self, t) return math.min(self:combatTalentSpellDamage(t, 10, 25, getParadoxSpellpower(self))/ 100 , 0.6) end,
+	radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4.5)) end,
+	getSlow = function(self, t) return paradoxTalentScale(self, t, 20, 40, 60)/100 end,
 	target = function(self, t)
 		return {type="ball", range=self:getTalentRange(t), friendlyfire=false, radius = self:getTalentRadius(t), talent=t}
 	end,
@@ -70,18 +70,23 @@ newTalent{
 		else
 			p.charges = math.min(p.charges + 1, 3)
 		end
+		
 		-- Dilate Time
 		if p.charges > 0 and not self.resting then
 			self:project(self:getTalentTarget(t), self.x, self.y, function(px, py)
 				local target = game.level.map(px, py, Map.ACTOR)
 				if not target then return end
-				target:setEffect(target.EFF_SLOW, 2, {power=p.power*p.charges, apply_power=self:combatSpellpower(), no_ct_effect=true})		
+				target:setEffect(target.EFF_SLOW, 1, {power=p.power*p.charges, apply_power=getParadoxSpellpower(self, t), no_ct_effect=true})		
 			end)
 		end
 	end,
+	updateOnTeleport = function(self, t, x, y)
+		local p = self:isTalentActive(self.T_TIME_DILATION)
+		p.x, p.y = x, y
+	end,
 	activate = function(self, t)
 		local ret ={
-			x = self.x, y=self.y, power = t.getSlow(self, t), charges = 0
+			x = self.x, y=self.y, power = t.getSlow(self, t)/3, charges = 0
 		}
 		game:playSoundNear(self, "talents/arcane")
 		return ret
@@ -92,9 +97,10 @@ newTalent{
 	end,
 	info = function(self, t)
 		local slow = t.getSlow(self, t) * 100
-		return ([[Time Dilates around you, reducing the speed of all enemies within a radius of three by up to %d%%.  This effect builds gradually over three turns and loses %d%% power each time you move.
-		The speed decrease will scale with your Spellpower]]):
-		format(slow*3, slow)
+		local radius = self:getTalentRadius(t)
+		return ([[Time Dilates around you, reducing the speed of all enemies within a radius of %d by up to %d%%.  This effect builds gradually over three turns and loses %d%% power each time you move.
+		Movement via teleport will not lower the power and the speed decrease will scale with your Spellpower.]]):
+		format(radius, slow, slow/3)
 	end,
 }
 
@@ -106,15 +112,23 @@ newTalent{
 	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
 	cooldown = 24,
 	tactical = { BUFF = 2, CLOSEIN = 2, ESCAPE = 2 },
-	getPower = function(self, t) return self:combatScale(self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self)), 0, 0, 0.57, 57, 0.75) end,
+	getPower= function(self, t) return paradoxTalentScale(self, t, 20, 40, 60)/300 end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, 4) end,
+	no_energy = true,
 	action = function(self, t)
-		self:setEffect(self.EFF_SPEED, 4, {power=t.getPower(self, t)})
+		local celerity = self:hasEffect(self.EFF_CELERITY) and self:hasEffect(self.EFF_CELERITY).charges or 0
+		local dilation = self:isTalentActive(self.T_TIME_DILATION) and self:isTalentActive(self.T_TIME_DILATION).charges or 0
+		local move = t.getPower(self, t) * celerity
+		local speed = t.getPower(self, t) * dilation
+		
+		self:setEffect(self.EFF_HASTE, t.getDuration(self, t), {move=move, speed=speed})
 		return true
 	end,
 	info = function(self, t)
-		local power = t.getPower(self, t)
-		return ([[Increases your global speed by %d%% for the next 4 game turns.
-		The speed increase will scale with your Spellpower.]]):format(100 * power)
+		local duration = t.getDuration(self, t)
+		local power = t.getPower(self, t) * 100
+		return ([[Increases your movement speed by %d%% per stack of Celerity and your attack, spell, and mind speed by %d%% per stack of Time Dilation for the next %d game turns.
+		The speed increase will scale with your Spellpower.]]):format(power, power, duration)
 	end,
 }
 
@@ -123,31 +137,33 @@ newTalent{
 	type = {"chronomancy/speed-control", 4},
 	require = chrono_req4,
 	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 48) end,
+	paradox = function (self, t) return getParadoxCost(self, t, 24) end,
 	cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 10, 45, 25)) end, -- Limit >10
 	tactical = { BUFF = 2, CLOSEIN = 2, ESCAPE = 2 },
 	no_energy = true,
 	on_pre_use = function(self, t, silent)
 		local time_dilated = false
 		if self:isTalentActive(self.T_TIME_DILATION) then
-			local t, p = self:getTalentFromId(self.T_TIME_DILATION), self:isTalentActive(self.T_TIME_DILATION)
+			local p = self:isTalentActive(self.T_TIME_DILATION)
 			if p.charges == 3 then
 				time_dilated = true
 			end
 		end
-		if not time_dilated then if not silent then game.logPlayer(self, "Time must be fully dilated in order to stop time.") end return false end return true 
+		if not time_dilated then if not silent then game.logPlayer(self, "Time must be fully dilated in order to cast time stop.") end return false end return true 
 	end,
-	getReduction = function(self, t) return 80 - math.min(self:combatTalentSpellDamage(t, 0, 20, getParadoxSpellpower(self)), 30) end,
+	getReduction = function(self, t) return 80 - paradoxTalentScale(self, t, 0, 20, 40) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, 2) end,
 	action = function(self, t)
-		self.energy.value = self.energy.value + 2000
-		self:setEffect(self.EFF_TIME_STOP, 2, {power=t.getReduction(self, t)})
+		self.energy.value = self.energy.value + (t.getDuration(self, t) * 1000)
+		self:setEffect(self.EFF_TIME_STOP, 1, {power=t.getReduction(self, t)})
 		game.logSeen(self, "#STEEL_BLUE#%s has stopped time!#LAST#", self.name:capitalize())
 		return true
 	end,
 	info = function(self, t)
+		local duration = t.getDuration(self, t)
 		local reduction = t.getReduction(self, t)
-		return ([[Gain two turns.  During this time your damage will be reduced by %d%%.
+		return ([[Gain %d turns.  During this time your damage will be reduced by %d%%.
 		Time must be fully dilated in order to use this talent.
-		The damage reduction penalty will be lessened by your Spellpower.]]):format(reduction)
+		The damage reduction penalty will be lessened by your Spellpower.]]):format(duration, reduction)
 	end,
 }
diff --git a/game/modules/tome/data/talents/chronomancy/spellbinding.lua b/game/modules/tome/data/talents/chronomancy/spellbinding.lua
new file mode 100644
index 0000000000000000000000000000000000000000..1921cdc7c78bd1952ad84e4875037fb47e59223e
--- /dev/null
+++ b/game/modules/tome/data/talents/chronomancy/spellbinding.lua
@@ -0,0 +1,156 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 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
+
+-- EDGE TODO: Particles, Timed Effect Particles
+
+newTalent{
+	name = "Empower",
+	type = {"chronomancy/spellbinding", 1},
+	require = chrono_req_high1,
+	points = 5,
+	sustain_paradox = 24,
+	mode = "sustained",
+	no_sustain_autoreset = true,
+	cooldown = 10,
+	no_npc_use = true,
+	allow_temporal_clones = true,
+	getPower = function(self, t) return self:combatTalentLimit(t, 1, 0.1, 0.5) end,
+	on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end,  -- temporal clones *CAN* learn spellbinding talents but can't change the value
+	activate = function(self, t)
+		local talent = self:talentDialog(require("mod.dialogs.talents.ChronomancyEmpower").new(self))
+		if not talent then return nil end
+
+		return {
+			talent = talent, rest_count = 0
+		}
+	end,
+	deactivate = function(self, t, p)
+		return true
+	end,
+	info = function(self, t)
+		local power = t.getPower(self, t) * 100
+		local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None"
+		return ([[Empowers the selected chronomancy spell, increasing spellpower when casting it by %d%%.
+		
+		Current Empowered Spell: %s]]):
+		format(power, talent)
+	end,
+}
+
+newTalent{
+	name = "Extension",
+	type = {"chronomancy/spellbinding", 1},
+	require = chrono_req_high1,
+	points = 5,
+	sustain_paradox = 24,
+	mode = "sustained",
+	no_sustain_autoreset = true,
+	cooldown = 10,
+	no_npc_use = true,
+	allow_temporal_clones = true,
+	getPower = function(self, t) return self:combatTalentLimit(t, 0.5, 0.05, 0.25) end,
+	on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end,  -- temporal clones *CAN* learn spellbinding talents but can't change the value
+	activate = function(self, t)
+		local talent = self:talentDialog(require("mod.dialogs.talents.ChronomancyExtension").new(self))
+		if not talent then return nil end
+				
+		return {
+			talent = talent, rest_count = 0
+		}
+	end,
+	deactivate = function(self, t, p)
+		return true
+	end,
+	info = function(self, t)
+		local power = t.getPower(self, t) * 100
+		local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None"
+		return ([[Extends the duration of the selected chronomancy spell by %d%%.
+		
+		Current Extended Spell: %s]]):
+		format(power, talent)
+	end,
+}
+
+newTalent{
+	name = "Matrix",
+	type = {"chronomancy/spellbinding", 1},
+	require = chrono_req_high1,
+	points = 5,
+	sustain_paradox = 24,
+	mode = "sustained",
+	no_sustain_autoreset = true,
+	cooldown = 10,
+	no_npc_use = true,
+	allow_temporal_clones = true,
+	getPower = function(self, t) return self:combatTalentLimit(t, 0.5, 0.05, 0.25) end,
+	on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end,  -- temporal clones *CAN* learn spellbinding talents but can't change the value
+	activate = function(self, t)
+		local talent = self:talentDialog(require("mod.dialogs.talents.ChronomancyMatrix").new(self))
+		if not talent then return nil end
+				
+		return {
+			talent = talent, rest_count = 0
+		}
+	end,
+	deactivate = function(self, t, p)
+		return true
+	end,
+	info = function(self, t)
+		local power = t.getPower(self, t) * 100
+		local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None"
+		return ([[Reduces the cooldown of the selected chronomancy spell by %d%%.
+		
+		Current Matrix Spell: %s]]):
+		format(power, talent)
+	end,
+}
+
+newTalent{
+	name = "Quicken",
+	type = {"chronomancy/spellbinding", 1},
+	require = chrono_req_high1,
+	points = 5,
+	sustain_paradox = 24,
+	mode = "sustained",
+	no_sustain_autoreset = true,
+	cooldown = 10,
+	no_npc_use = true,  -- so rares don't learn useless talents
+	allow_temporal_clones = true,  -- let clones copy it anyway so they can benefit from the effects
+	on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end,  -- but don't let them cast it
+	getPower = function(self, t) return self:combatTalentLimit(t, 1, 0.1, 0.5) end,
+	activate = function(self, t)
+		local talent = self:talentDialog(require("mod.dialogs.talents.ChronomancyQuicken").new(self))
+		if not talent then return nil end
+				
+		return {
+			talent = talent, rest_count = 0
+		}
+	end,
+	deactivate = function(self, t, p)
+		return true
+	end,
+	info = function(self, t)
+		local power = t.getPower(self, t) * 100
+		local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None"
+		return ([[Reduces the casting speed of the selected chronomancy spell by %d%%.
+		
+		Current Quickened Spell: %s]]):
+		format(power, talent)
+	end,
+}
diff --git a/game/modules/tome/data/talents/chronomancy/temporal-archery.lua b/game/modules/tome/data/talents/chronomancy/temporal-archery.lua
index 777cf9335558e9f37ab4315b2502cad9971d8f0e..41dac39492c063bff0fe3fa15b622db83b63c225 100644
--- a/game/modules/tome/data/talents/chronomancy/temporal-archery.lua
+++ b/game/modules/tome/data/talents/chronomancy/temporal-archery.lua
@@ -80,7 +80,7 @@ newTalent{
 	cooldown = 10,
 	tactical = { BUFF = 2 },
 	no_energy = true,
-	getPower = function(self, t) return 10 + self:combatTalentSpellDamage(t, 10, 40, getParadoxSpellpower(self)) end,
+	getPower = function(self, t) return 10 + self:combatTalentSpellDamage(t, 10, 40, getParadoxSpellpower(self, t)) end,
 	activate = function(self, t)
 		local power = t.getPower(self, t)
 		return {
diff --git a/game/modules/tome/data/talents/chronomancy/temporal-hounds.lua b/game/modules/tome/data/talents/chronomancy/temporal-hounds.lua
index 7622814fe18cf7d58f6f8837c4244de08a5317b5..87d8930df7ca122992cbc1419f93a2d301280d74 100644
--- a/game/modules/tome/data/talents/chronomancy/temporal-hounds.lua
+++ b/game/modules/tome/data/talents/chronomancy/temporal-hounds.lua
@@ -29,7 +29,7 @@ summonTemporalHound = function(self, t)
 	end
 	
 	local m = require("mod.class.NPC").new{
-		type = "animal", subtype = "hounds",
+		type = "animal", subtype = "canine",
 		display = "C", image = "npc/summoner_wardog.png",
 		color=colors.LIGHT_DARK, shader = "shadow_simulacrum",
 		shader_args = { color = {0.6, 0.6, 0.2}, base = 0.8, time_factor = 1500 },
@@ -51,9 +51,10 @@ summonTemporalHound = function(self, t)
 		infravision = 10,
 
 		combat_armor = 2, combat_def = 4,
-		combat = { dam=self:getTalentLevel(t) * 10 + rng.avg(12,25), atk=10, apr=10, dammod={str=0.8}, damtype=DamageType.MATTER, sound="creatures/wolves/wolf_attack_1" },
+		combat = { dam=self:getTalentLevel(t) * 10 + rng.avg(12,25), atk=10, apr=10, dammod={str=0.8, mag=0.8}, damtype=DamageType.WARP, sound="creatures/wolves/wolf_attack_1" },
 		
 		summoner = self, summoner_gain_exp=true,
+		resolvers.sustains_at_birth(),
 	}
 	
 	m:resolve()
@@ -130,6 +131,7 @@ summonTemporalHound = function(self, t)
 		})
 	end
 	
+	self:attr("summoned_times", 1)
 end
 
 newTalent{
@@ -187,7 +189,7 @@ newTalent{
 		
 		-- unsummon the hounds :(
 		for _, e in pairs(game.level.entities) do
-			if e.summoner and e.summoner == self and e.subtype == "hounds" then
+			if e.summoner and e.summoner == self and e.name == "temporal hound" then
 				e.summon_time = 0
 			end
 		end
@@ -217,7 +219,7 @@ newTalent{
 	requires_target = true,
 	on_pre_use = function(self, t, silent)
 		local p = self:isTalentActive(self.T_TEMPORAL_HOUNDS)
-		if not p then
+		if not p or p.hounds < 1 then
 			if not silent then
 				game.logPlayer(self, "You must have temporal hounds to use this talent.")
 			end
@@ -230,7 +232,7 @@ newTalent{
 	end,
 	direct_hit = true,
 	getDefense = function(self, t)
-		return self:combatTalentSpellDamage(t, 10, 40, getParadoxSpellpower(self))
+		return self:combatTalentSpellDamage(t, 10, 40, getParadoxSpellpower(self, t))
 	end,
 	action = function(self, t)
 		-- Pick our target
@@ -244,17 +246,19 @@ newTalent{
 		local __, x, y = self:canProject(tg, x, y)
 	
 		-- Summon a new Hound
-		local p = self:isTalentActive(self.T_TEMPORAL_HOUNDS)
-		local talent = self:getTalentFromId(self.T_TEMPORAL_HOUNDS)
-		if p.hounds < p.max_hounds then
-			summonTemporalHound(self, talent)
-			p.hounds = p.hounds + 1
-		end		
+		if self:getTalentLevel(t) >=5 then
+			local p = self:isTalentActive(self.T_TEMPORAL_HOUNDS)
+			local talent = self:getTalentFromId(self.T_TEMPORAL_HOUNDS)
+			if p.hounds < p.max_hounds then
+				summonTemporalHound(self, talent)
+				p.hounds = p.hounds + 1
+			end
+		end
 	
 		-- Find our hounds
 		local hnds = {}
 		for _, e in pairs(game.level.entities) do
-			if e.summoner and e.summoner == self and e.subtype == "hounds" then
+			if e.summoner and e.summoner == self and e.name == "temporal hound" then
 				hnds[#hnds+1] = e
 			end
 		end
@@ -290,7 +294,7 @@ newTalent{
 		local defense = t.getDefense(self, t)
 		return ([[Command your Temporal Hounds to teleport to the targeted location.  If you target a creature with this effect your hounds will set that creature as their target.
 		When you learn this talent, your hounds gain %d defense and %d%% resist all after any teleport.
-		If you're not at your maximum number of hounds when you cast this spell a new one will be summoned.
+		At talent level five, if you're not at your maximum number of hounds when you cast this spell a new one will be summoned.
 		The teleportation bonuses scale with your Spellpower.]]):format(defense, defense, defense/2, defense/2)
 	end,
 }
@@ -304,9 +308,9 @@ newTalent{
 	getImmunities = function(self, t)
 		return self:combatTalentLimit(t, 1, 0.15, 0.50) -- Limit <100%
 	end,
-	getRegen = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)) end,
-	getHaste = function(self, t) return self:combatScale(self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self)), 0, 0, 0.57, 57, 0.75) end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end,
+	getRegen = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t)) end,
+	getHaste = function(self, t) return paradoxTalentScale(self, t, 20, 50, 80)/100 end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 2, 6))) end,
 	doBlink = function(self, t, hound)  -- Triggered when the hounds is hit
 		local regen, haste = t.getRegen(self, t), t.getHaste(self, t)
 		if hound:hasEffect(hound.EFF_UNRAVEL) then
@@ -348,7 +352,7 @@ newTalent{
 	direct_hit = true,
 	on_pre_use = function(self, t, silent)
 		local p = self:isTalentActive(self.T_TEMPORAL_HOUNDS)
-		if not p then
+		if not p or p.hounds < 1 then
 			if not silent then
 				game.logPlayer(self, "You must have temporal hounds to use this talent.")
 			end
@@ -359,7 +363,7 @@ newTalent{
 	getResists = function(self, t)
 		return self:combatTalentLimit(t, 100, 15, 50) -- Limit <100%
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 150, getParadoxSpellpower(self)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 150, getParadoxSpellpower(self, t)) end,
 	getDamageStat = function(self, t) return 2 + math.ceil(t.getDamage(self, t) / 15) end,
 	target = function(self, t)
 		return {type="cone", range=0, radius=self:getTalentRadius(t), selffire=false, talent=t}
@@ -371,7 +375,7 @@ newTalent{
 		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 a.summoner == self and a.subtype == "hounds" then
+			if a and a.summoner == self and a.name == "temporal hound" then
 				hounds[#hounds+1] = a
 				tg[#tg+1] = {type="cone", range=0, radius=self:getTalentRadius(t), start_x=a.x, start_y=a.y, selffire=false, talent=t}
 			end
@@ -397,7 +401,7 @@ newTalent{
 				DamageType:get(DamageType.TEMPORAL).projector(a, px, py, DamageType.TEMPORAL, dam)
 				-- Don't turn back the clock other hounds
 				local target = game.level.map(px, py, Map.ACTOR)
-				if target and target.subtype ~= "hounds" then
+				if target and target.name ~= "temporal hound" then
 					target:setEffect(target.EFF_TURN_BACK_THE_CLOCK, 3, {power=t.getDamageStat(self, t), apply_power=a:combatSpellpower(), min_dur=1})
 				end	
 			end)
diff --git a/game/modules/tome/data/talents/chronomancy/threaded-combat.lua b/game/modules/tome/data/talents/chronomancy/threaded-combat.lua
index 482b4bb921b0c97518832938e7a3b549f3d047a1..6501ddbac60aba6450f790710f89f49e26bbe51d 100644
--- a/game/modules/tome/data/talents/chronomancy/threaded-combat.lua
+++ b/game/modules/tome/data/talents/chronomancy/threaded-combat.lua
@@ -17,7 +17,7 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
--- EDGE TODO: Talents, Icons, Particles, Timed Effect Particles
+-- EDGE TODO: Particles, Timed Effect Particles
 
 newTalent{
 	name = "Frayed Threads",
@@ -25,12 +25,12 @@ newTalent{
 	require = chrono_req_high1,
 	mode = "passive",
 	points = 5,
-	getPercent = function(self, t) return math.min(100, self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self)))/100 end,
+	getPercent = function(self, t) return paradoxTalentScale(self, t, 40, 80, 100)/100 end,
 	getRadius = function(self, t) return self:getTalentLevel(t) > 4 and 2 or 1 end,
 	info = function(self, t)
 		local percent = t.getPercent(self, t) * 100
 		local radius = t.getRadius(self, t)
-		return ([[Your Weapon Folding and Impact spells now deal %d%% of their damage in a radius of %d.
+		return ([[Your Weapon Folding and Impact spells now deal an additional %d%% damage in a radius of %d.
 		The damage percent will scale with your Spellpower.]])
 		:format(percent, radius)
 	end
@@ -46,7 +46,7 @@ newTalent{
 	tactical = { ATTACKAREA = { weapon = 3 } , DISABLE = 3 },
 	requires_target = true,
 	range = archery_range,
-	no_energy = "fake",
+	speed = function(self, t) return self:hasArcheryWeapon("bow") and "archery" or "weapon" end,
 	getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.2, 1.9) end,
 	getCooldown = function(self, t) return self:getTalentLevel(t) >= 5 and 2 or 1 end,
 	on_pre_use = function(self, t, silent) if self:attr("disarmed") then if not silent then game.logPlayer(self, "You require a weapon to use this talent.") end return false end return true end,
@@ -62,7 +62,7 @@ newTalent{
 		for tid, cd in pairs(self.talents_cd) do
 			local tt = self:getTalentFromId(tid)
 			if tt.type[1]:find("^chronomancy/blade") then
-				self.talents_cd[tid] = cd - t.getCooldown(self, t)
+				self:alterTalentCoolingdown(tt, - t.getCooldown(self, t))
 			end
 		end
 	end,
@@ -73,7 +73,7 @@ newTalent{
 				
 		if self:hasArcheryWeapon("bow") then
 			-- Ranged attack
-			local targets = self:archeryAcquireTargets(tg, {one_shot=true})
+			local targets = self:archeryAcquireTargets(tg, {one_shot=true, no_energy = true})
 			if not targets then return end
 			self:archeryShoot(targets, t, tg, {mult=dam})
 		elseif mainhand then
@@ -81,13 +81,13 @@ newTalent{
 			self:project(tg, self.x, self.y, function(px, py, tg, self)
 				local target = game.level.map(px, py, Map.ACTOR)
 				if target and target ~= self then
-					local hit = self:attackTarget(target, nil, dam)
+					local hit = self:attackTarget(target, nil, dam, true)
 					-- Refresh bow talents
 					if hit then
 						for tid, cd in pairs(self.talents_cd) do
 							local tt = self:getTalentFromId(tid)
 							if tt.type[1]:find("^chronomancy/bow") then
-								self.talents_cd[tid] = cd - t.getCooldown(self, t)
+								self:alterTalentCoolingdown(tt, - t.getCooldown(self, t))
 							end
 						end
 					end
@@ -118,7 +118,7 @@ newTalent{
 	require = chrono_req_high3,
 	mode = "passive",
 	points = 5,
-	getPercent = function(self, t) return math.min(50, 10 + self:combatTalentSpellDamage(t, 0, 30, getParadoxSpellpower(self)))/100 end,
+	getPercent = function(self, t) return paradoxTalentScale(self, t, 10, 30, 50)/100 end,
 	info = function(self, t)
 		local percent = t.getPercent(self, t) * 100
 		return ([[Your Bow Threading and Blade Threading spells now deal %d%% more weapon damage if you did not have the appropriate weapon equipped when you initated the attack.
@@ -136,8 +136,8 @@ newTalent{
 	cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 15, 45, 25)) end, -- Limit >15
 	tactical = { ATTACK = {weapon = 4} },
 	range = 10,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 12)) end,
-	getDamagePenalty = function(self, t) return 60 - math.min(self:combatTalentSpellDamage(t, 0, 20, getParadoxSpellpower(self)), 30) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 6, 12))) end,
+	getDamagePenalty = function(self, t) return 60 - paradoxTalentScale(self, t, 0, 20, 30) end,
 	requires_target = true,
 	target = function(self, t)
 		return {type="hit", range=self:getTalentRange(t)}
@@ -168,7 +168,6 @@ newTalent{
 			local tids = {}
 			for tid, _ in pairs(m.talents) do
 				local t = m:getTalentFromId(tid)
-				if t.remove_on_clone then tids[#tids+1] = t end
 				local tt = self:getTalentFromId(tid)
 				if not tt.type[1]:find("^chronomancy/blade") and not tt.type[1]:find("^chronomancy/threaded") and not tt.type[1]:find("^chronomancy/guardian") then
 					tids[#tids+1] = t 
@@ -181,11 +180,12 @@ newTalent{
 			
 			m.ai_state = { talent_in=2, ally_compassion=10 }	
 			m.generic_damage_penalty = t.getDamagePenalty(self, t)
-			m:setTarget(target or nil)
 			m.remove_from_party_on_death = true
 			
 			game.zone:addEntity(game.level, m, "actor", tx, ty)
 			
+			m:setTarget(target or nil)
+			
 			if game.party:hasMember(self) then
 				game.party:addMember(m, {
 					control="no",
@@ -234,7 +234,6 @@ newTalent{
 			local tids = {}
 			for tid, _ in pairs(m.talents) do
 				local t = m:getTalentFromId(tid)
-				if t.remove_on_clone then tids[#tids+1] = t end
 				local tt = self:getTalentFromId(tid)
 				if not tt.type[1]:find("^chronomancy/bow") and not tt.type[1]:find("^chronomancy/threaded") and not tt.type[1]:find("^chronomancy/guardian") and not t.innate then
 					tids[#tids+1] = t 
@@ -248,11 +247,12 @@ newTalent{
 			m.ai_state = { talent_in=2, ally_compassion=10 }
 			m.generic_damage_penalty = t.getDamagePenalty(self, t)
 			m:attr("archery_pass_friendly", 1)
-			m:setTarget(target or nil)
 			m.remove_from_party_on_death = true
 			
 			game.zone:addEntity(game.level, m, "actor", tx, ty)
 			
+			m:setTarget(target or nil)
+			
 			if game.party:hasMember(self) then
 				game.party:addMember(m, {
 					control="no",
@@ -283,6 +283,6 @@ newTalent{
 		local damage_penalty = t.getDamagePenalty(self, t)
 		return ([[Summons a blade warden and a bow warden from an alternate timeline for %d turns.  The wardens are out of phase with this reality and deal %d%% less damage but the bow warden's arrows will pass through friendly targets.
 		Each warden knows all Threaded Combat, Temporal Guardian, and Blade Threading or Bow Threading spells you know.
-		The damage reduction penalty will be lessened by your Spellpower.]]):format(duration, damage_penalty)
+		The damage penalty will be lessened by your Spellpower.]]):format(duration, damage_penalty)
 	end,
 }
diff --git a/game/modules/tome/data/talents/chronomancy/timeline-threading.lua b/game/modules/tome/data/talents/chronomancy/timeline-threading.lua
index a2d943c2fa4747b54a6d72bfb659e404c09ef317..609beb7d1586925fc8c5243f716557a6aaa05e3e 100644
--- a/game/modules/tome/data/talents/chronomancy/timeline-threading.lua
+++ b/game/modules/tome/data/talents/chronomancy/timeline-threading.lua
@@ -17,157 +17,453 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
+-- EDGE TODO: Particles, Timed Effect Particles
+
 newTalent{
-	name = "Gather the Threads",
+	name = "Rethread",
 	type = {"chronomancy/timeline-threading", 1},
-	require = chrono_req_high1,
+	require = chrono_req1,
 	points = 5,
+	cooldown = 4,
 	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
-	cooldown = 12,
-	tactical = { BUFF = 2 },
-	getThread = function(self, t) return self:combatTalentScale(t, 7, 30, 0.75) end,
-	getReduction = function(self, t) return self:combatTalentScale(t, 3.6, 15, 0.75) end,
+	tactical = { ATTACKAREA = {TEMPORAL = 2}, PARADOX = 2 },
+	range = 10,
+	direct_hit = true,
+	reflectable = true,
+	requires_target = true,
+	target = function (self, t)
+		return {type="bolt", range=self:getTalentRange(t), talent=t}
+	end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end,
+	getTargetCount = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6, "log")) end,
 	action = function(self, t)
-		self:setEffect(self.EFF_GATHER_THE_THREADS, 5, {power=t.getThread(self, t), reduction=t.getReduction(self, t)})
-		game:playSoundNear(self, "talents/spell_generic2")
+		local tg = self:getTalentTarget(t)
+		local fx, fy = self:getTarget(tg)
+		if not fx or not fy then return nil end
+
+		local nb = t.getTargetCount(self, t)
+		local affected = {}
+		local braid_targets = {}
+		local first = nil
+		
+		self:project(tg, fx, fy, function(dx, dy)
+			local actor = game.level.map(dx, dy, Map.ACTOR)
+			if actor and not affected[actor] then
+				affected[actor] = true
+				first = actor
+
+				self:project({type="ball", selffire=false, x=dx, y=dy, radius=10, range=0}, dx, dy, function(bx, by)
+					local actor = game.level.map(bx, by, Map.ACTOR)
+					if actor and not affected[actor] and self:reactionToward(actor) < 0 then
+						affected[actor] = true
+					end
+				end)
+				return true
+			end
+		end)
+
+		if not first then return end
+		local targets = { first }
+		affected[first] = nil
+		local possible_targets = table.listify(affected)
+
+		for i = 2, nb do
+			if #possible_targets == 0 then break end
+			local act = rng.tableRemove(possible_targets)
+			targets[#targets+1] = act[1]
+		end
+
+		local sx, sy = self.x, self.y
+		local damage = self:spellCrit(t.getDamage(self, t))
+		for i, actor in ipairs(targets) do
+			local tgr = {type="beam", range=self:getTalentRange(t), selffire=false, talent=t, x=sx, y=sy}
+			self:project(tgr, actor.x, actor.y, function(px, py)
+				DamageType:get(DamageType.TEMPORAL).projector(self, px, py, DamageType.TEMPORAL, damage)
+
+				-- Get our braid targets
+				local target = game.level.map(px, py, Map.ACTOR)
+				if target and not target.dead and self:knowTalent(self.T_BRAID_LIFELINES) then
+					braid_targets[#braid_targets+1] = target
+				end
+			end)
+
+			game.level.map:particleEmitter(sx, sy, math.max(math.abs(actor.x-sx), math.abs(actor.y-sy)), "temporalbeam", {tx=actor.x-sx, ty=actor.y-sy})
+			sx, sy = actor.x, actor.y
+		end
+		
+		-- Braid them
+		if #braid_targets > 1 then
+			for i = 1, #braid_targets do
+				local target = braid_targets[i]
+				local t = self:getTalentFromId(self.T_BRAID_LIFELINES)
+				target:setEffect(target.EFF_BRAIDED, t.getDuration(self, t), {power=t.getBraid(self, t), src=self, targets=braid_targets})
+			end
+		end
+		
+		game:playSoundNear(self, "talents/heal")
 		return true
 	end,
 	info = function(self, t)
-		local primary = t.getThread(self, t)
-		local reduction = t.getReduction(self, t)
-		return ([[You begin to gather energy from other timelines. Your Spellpower will increase by %0.2f on the first turn and %0.2f more each additional turn.
-		The effect ends either when you cast a spell, or after five turns.
-		Eacn turn the effect is active, your Paradox will be reduced by %d.
-		This spell will not break Spacetime Tuning, nor will it be broken by activating Spacetime Tuning.]]):format(primary + (primary/5), primary/5, reduction)
+		local damage = t.getDamage(self, t)
+		local targets = t.getTargetCount(self, t)
+		return ([[Rethread the timeline, dealing %0.2f temporal damage to the target before moving on to a second target.
+		Rethread can hit up to %d targets up to 10 grids apart, and will never hit the same one twice; nor will it hit the caster.
+		The damage will increase with your Spellpower.]]):
+		format(damDesc(self, DamageType.TEMPORAL, damage), targets)
 	end,
 }
 
 newTalent{
-	name = "Rethread",
+	name = "Temporal Fugue",
 	type = {"chronomancy/timeline-threading", 2},
-	require = chrono_req_high2,
+	require = chrono_req2,
 	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
-	cooldown = 4,
-	tactical = { ATTACK = {TEMPORAL = 2} },
-	range = 10,
-	direct_hit = true,
-	reflectable = true,
-	requires_target = true,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 200, getParadoxSpellpower(self)) end,
-	getReduction = function(self, t) return self:combatTalentScale(t, 1.2, 5, 0.75) end,
+	cooldown = 24,
+	paradox = function(self, t) return getParadoxCost(self, t, 20) end,
+	tactical = { ATTACK = 2, DISABLE = 2 },
+	remove_on_clone = true,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 8))) end,
+	on_pre_use = function(self, t, silent) if self:hasEffect(self.EFF_TEMPORAL_FUGUE) then return false end return true end,
 	action = function(self, t)
-		local tg = {type="beam", range=self:getTalentRange(t), talent=t}
-		local x, y = self:getTarget(tg)
-		if not x or not y then return nil end
-		local _ _, x, y = self:canProject(tg, x, y, t.paradox)
-		self:project(tg, x, y, DamageType.RETHREAD, {dam=self:spellCrit(t.getDamage(self, t)), reduction = t.getReduction(self, t)})
-		game.level.map:particleEmitter(self.x, self.y, tg.radius, "temporalbeam", {tx=x-self.x, ty=y-self.y})
-		game:playSoundNear(self, "talents/heal")
+		local clones = {self}
+		
+		 -- Clone the caster
+		local function makeFugueClone(self, t)
+			local sex = game.player.female and "she" or "he"
+			local m = makeParadoxClone(self, self, t.getDuration(self, t))
+			-- Add and change some values
+			m.name = self.name
+			m.desc = [[The real you... or so ]]..sex..[[ says.]]
+			m.shader = nil
+			m.shader_args = nil
+			m.faction = self.faction
+			m.summoner = self
+			m.remove_from_party_on_death = true
+			
+			-- Handle some AI stuff
+			m.ai_state = { talent_in=1, ally_compassion=10 }
+			m.ai_tactic = { closein=0, defend=4, disable=4, escape=4, heal=2, safe_range=3}
+			
+			return m
+		end
+		
+		-- Add our clones
+		for i = 1, 2 do
+			local tx, ty = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true})
+			if tx and ty then
+				-- Make our clone and add to the party
+				local m = makeFugueClone(self, t)
+				if game.party:hasMember(self) then
+					game.party:addMember(m, {
+						control="full",
+						type="fugue clone",
+						title="Fugue Clone",
+						orders = {target=true},
+					})
+				end
+				
+				-- and the level
+				game.zone:addEntity(game.level, m, "actor", tx, ty)
+				game.level.map:particleEmitter(m.x, m.y, 1, "temporal_teleport")
+				clones[#clones+1] = m
+			end
+		end
+		
+		-- No clones?  Exit the spell
+		if #clones < 2 then
+			game.logPlayer(self, "Not enough space to summon!")
+			return
+		end
+		
+		-- Link them
+		for i = 1, #clones do
+			local target = clones[i]
+			target:setEffect(target.EFF_TEMPORAL_FUGUE, 10, {targets=clones})
+		end
+		
+		game:playSoundNear(self, "talents/spell_generic")
+		
 		return true
 	end,
 	info = function(self, t)
-		local damage = t.getDamage(self, t)
-		local reduction = t.getReduction(self, t)
-		return ([[Creates a wake of temporal energy that deals %0.2f damage in a beam, as you attempt to rethread the timeline.  Affected targets may be stunned, blinded, pinned, or confused for 3 turns.
-		Each target you hit with Rethread will reduce your Paradox by %0.1f.
-		The damage will increase with your Spellpower.]]):
-		format(damDesc(self, DamageType.TEMPORAL, damage), reduction)
+		local duration = t.getDuration(self, t)
+		return ([[For the next %d turns two alternate versions of you enter your timeline.  While the effect is active all damage done by you or your copies is reduced by two thirds and all damage recieved is split between the three of you.
+		Temporal Fugue does not normally cooldown while active.  You may take direct control of your clones.]]):
+		format(duration)
 	end,
 }
 
 newTalent{
-	name = "Temporal Clone",
+	name = "Braid Lifelines",
 	type = {"chronomancy/timeline-threading", 3},
-	require = chrono_req_high3,
+	require = chrono_req3,
+	mode = "passive",
 	points = 5,
-	cooldown = 30,
-	paradox = function (self, t) return getParadoxCost(self, t, 30) end,
-	tactical = { ATTACK = 2, DISABLE = 2 },
+	getBraid = function(self, t) return self:combatTalentSpellDamage(t, 25, 40, getParadoxSpellpower(self, t)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end,
+	info = function(self, t)
+		local braid = t.getBraid(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[Your Rethread now braids the lifelines of all targets it hits for %d turns.  Braided targets take %d%% of all damage dealt to other braided targets.
+		The amount of damage shared will scale with your Spellpower.]])
+		:format(duration, braid)
+	end
+}
+
+newTalent{
+	name = "Cease to Exist",
+	type = {"chronomancy/timeline-threading", 4},
+	require = chrono_req4,
+	points = 5,
+	cooldown = 24,
+	paradox = function (self, t) return getParadoxCost(self, t, 25) end,
+	range = 10,
+	tactical = { ATTACK = 2 },
 	requires_target = true,
-	range = 6,
-	no_npc_use = true,
-	getDuration = function(self, t) -- limit < cooldown (30)
-		return math.floor(self:combatTalentLimit(self:getTalentLevel(t), t.cooldown, 4, 8))
+	direct_hit = true,
+	no_npc_use = true,  -- so rares don't learn useless talents
+	allow_temporal_clones = true,  -- let clones copy it anyway so they can benefit from the effects
+	on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end,  -- but don't let them cast it
+	getDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 5, 9)) end,
+	getPower = function(self, t) return paradoxTalentScale(self, t, 20, 50, 100) end,
+	target = function(self, t)
+		return {type="hit", range=self:getTalentRange(t), talent=t}
+	end,
+	on_pre_use = function(self, t, silent)
+		if checkTimeline(self) then
+			if not silent then
+				game.logPlayer(self, "The timeline is too fractured to do this now.")
+			end
+			return false
+		end
+		return true
+	end,
+	do_instakill = function(self, t)
+		-- search for target because it's ID will change when the chrono restore takes place
+		local tg = false
+		local grids = core.fov.circle_grids(self.x, self.y, 10, 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 a:hasEffect(a.EFF_CEASE_TO_EXIST) then
+				tg = a
+			end
+		end end
+		
+		if tg then
+			game:onTickEnd(function()
+				tg:removeEffect(tg.EFF_CEASE_TO_EXIST)
+				game.logSeen(tg, "#LIGHT_BLUE#%s never existed, this never happened!", tg.name:capitalize())
+				tg:die(self)
+			end)
+		end
 	end,
-	getSize = function(self, t) return 2 + math.ceil(self:getTalentLevelRaw(t) / 2 ) end,
 	action = function(self, t)
-		local tg = {type="hit", range=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)
+		-- get our target
+		local tg = self:getTalentTarget(t)
+		local tx, ty = self:getTarget(tg)
 		if not tx or not ty then return nil end
+		local _ _, tx, ty = self:canProject(tg, tx, ty)
+		
 		local target = game.level.map(tx, ty, Map.ACTOR)
-		if not target or self:reactionToward(target) >= 0 then return end
+		if not target then return end
 
-		-- Find space
-		local x, y = util.findFreeGrid(tx, ty, 1, true, {[Map.ACTOR]=true})
-		if not x then
-			game.logPlayer(self, "Not enough space to summon!")
-			return
+		if target == self then
+			game.logSeen(self, "#LIGHT_STEEL_BLUE#%s tries to remove %sself from existance!", self.name, string.his_her(self))
+			self:incParadox(400)
+			game.level.map:particleEmitter(self.x, self.y, 1, "ball_temporal", {radius=1, tx=self.x, ty=self.y})
+			return true
 		end
-
-		local allowed = t.getSize(self, t)
-
-		if target.rank >= 3.5 or -- No boss
-			target:reactionToward(self) >= 0 or -- No friends
-			target.size_category > allowed
-			then
+		
+		-- does the spell hit?  if not nothing happens
+		if not self:checkHit(getParadoxSpellpower(self, t), target:combatSpellResist()) then
 			game.logSeen(target, "%s resists!", target.name:capitalize())
 			return true
 		end
+	
+		-- Manualy start cooldown before the chronoworld is made
+		game.player:startTalentCooldown(t)
+		
+		-- set up chronoworld next, we'll load it when the target dies in class\actor
+		game:onTickEnd(function()
+			game:chronoClone("cease_to_exist")
+		end)
+		
+		-- Set our effect
+		target:setEffect(target.EFF_CEASE_TO_EXIST, t.getDuration(self,t), {src=self, power=t.getPower(self,t)})
+				
+		return true
+	end,
+	info = function(self, t)
+		local duration = t.getDuration(self, t)
+		local power = t.getPower(self, t)
+		return ([[Over the next %d turns, you attempt to remove the target from the timeline, lowering all its resistance by %d%%.
+		If you manage to kill the target while the spell is in effect, you'll be returned to the point in time you cast this spell and the target will be slain.
+		This spell splits the timeline.  Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful.
+		The resistance penalty will scale with your Spellpower.]])
+		:format(duration, power)
+	end,
+}
 
-		local m = target:cloneFull{
-			no_drops = true,
-			faction = self.faction,
-			summoner = self, summoner_gain_exp=true,
-			exp_worth = 0, -- bug fix
-			summon_time = t.getDuration(self, t),
-			ai_target = {actor=target},
-			ai = "summoned", ai_real = target.ai,
-		}
-		m:removeAllMOs()
-		m.make_escort = nil
-		m.on_added_to_level = nil
-		
-		m.energy.value = 0
-		m.life = m.life
-		m.forceLevelup = function() end
-		-- Handle special things
-		m.on_die = nil
-		m.on_acquire_target = nil
-		m.seen_by = nil
-		m.can_talk = nil
-		m.clone_on_hit = nil
-		if m.talents.T_SUMMON then m.talents.T_SUMMON = nil end
-		if m.talents.T_MULTIPLY then m.talents.T_MULTIPLY = nil end
-
+-- This was a really cool affect but the game really assumes that the player is on the map
+-- I suspect there's a lot of unforseen bugs in this code but I'm leaving it here incase someone else wants to attempt to do something similar in the future
+--[=[newTalent{
+	name = "Temporal Fugue",
+	type = {"chronomancy/timeline-threading", 2},
+	require = chrono_req2,
+	points = 5,
+	cooldown = 12,
+	paradox = function (self, t) return getParadoxCost(self, t, 15) end,
+	tactical = { DISABLE = 3 },
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 4, 8))) end,
+	getDamagePenalty = function(self, t) return 80 - paradoxTalentScale(self, t, 0, 20, 40) end,
+	getClones = function(self, t) return 3 end,
+	on_pre_use = function(self, t, silent) if self ~= game.player and self.fugue_clones then return false end return true end,
+	action = function(self, t)
+		-- If the spell is active cancel the effect	
+		if self.fugue_clones then
+			for _, e in pairs(game.level.entities) do
+				if e.summoner and e.summoner == self.summoner and e.fugue_clones and e ~= self then
+					e:die()
+				end
+			end
+			return true
+		end
+		
+		-- Remove the player
+		local x, y = self.x, self.y
+		game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
+		game.level:removeEntity(self, true)
+		
+		-- Setup our Fugue Clones
+		local fugue_count = 0
+		local function makeFugueClone(self, t, x, y)
+			local m = makeParadoxClone(self, self, t.getDuration(self, t))
+			
+			-- Flavor :)
+			local sex = self.female and "she" or "he"
+			m.name = self.name
+			m.desc = [[The real ]]..self.name:capitalize()..[[... or so ]]..sex..[[ says.]]
+			m.shader = nil
+			m.shader_args = nil
+			 
+			-- Add and change some values
+			m.generic_damage_penalty = t.getDamagePenalty(self, t)
+			m.max_life = m.max_life * (100 - t.getDamagePenalty(self, t))/100
+			m.life = m.max_life
+			m.remove_from_party_on_death = true
+			m.timetravel_immune = 1
+			
+			-- Handle some AI stuff
+			m.ai_state = { talent_in=2, ally_compassion=10 }
+			
+			-- Track clones when we die
+			m.on_die = function(self)
+				-- If we're the last return the caster
+				if self.fugue_clones <= 1 then
+					-- don't level up on return
+					local old_levelup = self.summoner.forceLevelup
+					local old_check = self.summoner.check
+					self.summoner.forceLevelup = function() end
+					self.summoner.check = function() end
+					game.zone:addEntity(game.level, self.summoner, "actor", self.x, self.y)
+					self.summoner.forceLevelup = old_levelup
+					self.summoner.check = old_check
+					if game.party:hasMember(self) then
+						game.party:setPlayer(self.summoner)
+					end
+				else
+					-- find fellow clones and update the clone count
+					for _, e in pairs(game.level.entities) do
+						if e.summoner and e.summoner == self.summoner and e.fugue_clones then
+							e.fugue_clones = e.fugue_clones - 1
+							if e.fugue_clones <= 1 then
+								e.summon_time = 0
+							end
+						end
+					end
+				end
+				local add_paradox = math.max(0, self:getParadox() - self.summoner:getParadox())
+				self.summoner:incParadox(add_paradox/3)
+			end
+	
+			return m
+		end
+		
+		-- Add the primary clone to the game
+		local m = makeFugueClone(self, t, x, y)
+		if game.party:hasMember(self) then
+			game.party:addMember(m, {
+				control="full",
+				type="fugue clone",
+				title="Fugue Clone",
+				orders = {target=true},
+			})
+		end
 		game.zone:addEntity(game.level, m, "actor", x, y)
-		game.level.map:particleEmitter(x, y, 1, "temporal_teleport")
-
-		-- force target to attack double
-		local a = game.level.map(tx, ty, Map.ACTOR)
-		if a and self:reactionToward(a) < 0 then
-			a:setTarget(m)
+		local fugue_count = 1
+		
+		-- Swap control to the primary clone
+		if game.party:hasMember(self) then
+			game.party:setPlayer(m)
+			m.summon_time = m.summon_time + 1
 		end
+		
+		m:resetCanSeeCache()
+		
+		-- Make the rest of our clones
+		for i = 1, t.getClones(self, t)-1 do
+			local m = makeFugueClone(self, t, x, y)
+			
+			local poss = {}
+			local range = t.getClones(self, t)
+			for i = x - range, x + range do
+				for j = y - range, y + range do
+					if game.level.map:isBound(i, j) and
+						core.fov.distance(x, y, i, j) <= range and -- make sure they're within range
+						self:canMove(i, j) and self:hasLOS(i, j) then  -- keep them in line of sight
+						poss[#poss+1] = {i,j}
+					end
+				end
+			end
+			if #poss == 0 then break  end
+			local pos = poss[rng.range(1, #poss)]
+			tx, ty = pos[1], pos[2]
 
-		game:playSoundNear(self, "talents/spell_generic")
+			-- And add to the party
+			if game.party:hasMember(self) then
+				game.party:addMember(m, {
+					control="full",
+					type="fugue clone",
+					title="Fugue Clone",
+					orders = {target=true},
+				})
+			end
+			
+			-- Add us to the level
+			game.zone:addEntity(game.level, m, "actor", tx, ty)
+			game.level.map:particleEmitter(m.x, m.y, 1, "temporal_teleport")
+			fugue_count = fugue_count + 1
+		end
+		
+		-- Add the fugue counter
+		for _, e in pairs(game.level.entities) do
+			if e.summoner and e.summoner == self then
+				e.fugue_clones = fugue_count
+			end
+		end
+		
+		game.level.map:particleEmitter(m.x, m.y, 1, "temporal_teleport")
+		game:playSoundNear(m, "talents/teleport")
+		
 		return true
 	end,
 	info = function(self, t)
 		local duration = t.getDuration(self, t)
-		local allowed = t.getSize(self, t)
-		local size = "gargantuan"
-		if allowed < 4 then
-			size = "medium"
-		elseif allowed < 5 then
-			size = "big"
-		elseif allowed < 6 then
-			size = "huge"
-		end
-		return ([[Pulls a %s size or smaller copy of the target from another timeline; the copy stays for %d turns. The copy and the target will be compelled to attack each other immediately.]]):
-		format(size, duration)
+		local damage_penalty = t.getDamagePenalty(self, t)
+		return ([[Split yourself into three fugue clones.  The clones have %d%% of your maximum life, deal %d%% less damage, and last %d turns.
+		Each clone that dies will increase your Paradox by 33%% of the difference between its Paradox and your own.  This will never reduce your Paradox.
+		When only one clone is left, or if you cast the spell while in the fugue state, the spell will end, returning you to the position of the last active clone.
+		The life and damage penalties will be reduced by your Spellpower.]]):
+		format(100 - damage_penalty, damage_penalty, duration)
 	end,
-}
-
-
+}]=]--
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/chronomancy/timetravel.lua b/game/modules/tome/data/talents/chronomancy/timetravel.lua
index e416f07b35642eed324e9a017433a67b7cc6f37b..b0b3538d09792bce08417176cc893023d54d6ddd 100644
--- a/game/modules/tome/data/talents/chronomancy/timetravel.lua
+++ b/game/modules/tome/data/talents/chronomancy/timetravel.lua
@@ -17,101 +17,146 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
--- EDGE TODO: Icons, Particles, Timed Effect Particles
+-- EDGE TODO:Particles, Timed Effect Particles
 
 newTalent{
-	name = "Time Skip",
+	name = "Temporal Bolt",
 	type = {"chronomancy/timetravel",1},
-	require = chrono_req_high1,
+	require = chrono_req1,
 	points = 5,
-	cooldown = 4,
+	cooldown = 3,
+	fixed_cooldown = true,
 	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
-	tactical = { ATTACK = {TEMPORAL = 1}, DISABLE = 2 },
+	tactical = { ATTACK = {TEMPORAL = 2}, DISABLE = 1 },
 	range = 10,
+	reflectable = true,
+	proj_speed = 3,
 	direct_hit = true,
 	requires_target = true,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 150, getParadoxSpellpower(self, t)) end,
 	target = function(self, t)
-		return {type="hit", range=self:getTalentRange(t), talent=t}
+		return {type="hit", range=self:getTalentRange(t), friendlyfire=false, talent=t}
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 250, getParadoxSpellpower(self)) end,
-	getDuration = function(self, t) return 2 + math.ceil(self:combatTalentScale(self:getTalentLevel(t), 0.3, 2.3)) end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		local _ _, x, y = self:canProject(tg, x, y)
-		local target = game.level.map(x, y, Map.ACTOR)
-		if not target then return end
+		
+		local dam = self:spellCrit(t.getDamage(self, t))
+		
+		-- Hit our initial target; quality of life hack
+		self:project(tg, x, y, function(px, py)
+			local target = game.level.map(px, py, Map.ACTOR)
+			if not target then return end
+			-- Refresh talent
+			for tid, cd in pairs(self.talents_cd) do
+				local tt = self:getTalentFromId(tid)
+				if tt.type[1]:find("^chronomancy/") and not tt.fixed_cooldown then
+					self:alterTalentCoolingdown(tt, - 1)
+				end
+			end
+			DamageType:get(DamageType.TEMPORAL).projector(self, x, y, DamageType.TEMPORAL, dam)
+		end)
+		
+		-- Make our homing missile
+		self:project(tg, x, y, function(x, y)
+			local proj = require("mod.class.Projectile"):makeHoming(
+				self,
+				{particle="arrow", particle_args={tile="particles_images/temporal_bolt", proj_x=self.x, proj_y=self.y, src_x=x, src_y=y},  trail="trail_paradox"},
+				{speed=3, name="Temporal Bolt", dam=dam, apply=getParadoxSpellpower(self, t), start_x=x, start_y=y},
+				self, self:getTalentRange(t),
+				function(self, src)
+					local target = game.level.map(self.x, self.y, engine.Map.ACTOR)
+					if target and not target.dead and src:reactionToward(target) < 0 then
+						local DT = require("engine.DamageType")
+						local multi = (10 - self.homing.count)/20
+						local dam = self.def.dam * (1 + multi)
+						src:project({type="hit", friendlyfire=false, talent=t}, self.x, self.y, DT.TEMPORAL, dam)
+						
+						-- Refresh talent
+						for tid, cd in pairs(src.talents_cd) do
+							local tt = src:getTalentFromId(tid)
+							if tt.type[1]:find("^chronomancy/") and not tt.fixed_cooldown then
+								src:alterTalentCoolingdown(tt, - 1)
+							end
+						end
+					end
+				end,
+				function(self, src)
+				end
+			)
+			game.zone:addEntity(game.level, proj, "projectile", x, y)
+		end)
+			
+		game:playSoundNear({x=x, y=y}, "talents/distortion")
 
-		if target:attr("timetravel_immune") then
-			game.logSeen(target, "%s is immune!", target.name:capitalize())
-			return
-		end
+		return true
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)
+		return ([[Pull a bolt of temporal energy back through time.  The bolt will home in on your location, dealing %0.2f temporal damage to enemies, and reducing the cooldown of one chronomancy talent on cooldown by one turn per enemy hit.
+		The bolt gains 5%% damage each time it moves and the damage will scale with your Spellpower.]]):
+		format(damDesc(self, DamageType.TEMPORAL, damage))
+	end,
+}
 
-		local power = self:combatSpellpower() * (1 + self:callTalent(self.T_SPACETIME_MASTERY, "getPower"))
-		local hit = self:checkHit(power, target:combatSpellResist() + (target:attr("continuum_destabilization") or 0))
-		if not hit then game.logSeen(target, "%s resists!", target.name:capitalize()) return true end
-		self:project(tg, x, y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t)))
-		game.level.map:particleEmitter(x, y, 1, "temporal_thrust")
-		game:playSoundNear(self, "talents/arcane")
-		if target.dead or target.player then return true end
-		target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self:combatSpellpower(0.3)})
+newTalent{
+	name = "Echoes From The Past",
+	type = {"chronomancy/timetravel", 2},
+	require = chrono_req2,
+	points = 5,
+	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
+	cooldown = 10,
+	tactical = { ATTACKAREA = {TEMPORAL = 2} },
+	range = 0,
+	radius = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end,
+	target = function(self, t)
+		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t}
+	end,
+	direct_hit = true,
+	requires_target = true,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 18, 160, getParadoxSpellpower(self, t)) end,
+	getPercent = function(self, t) return paradoxTalentScale(self, t, 20, 40, 60)/100 end,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
 		
-		-- Replace the target with a temporal instability for a few turns
-		local oe = game.level.map(target.x, target.y, engine.Map.TERRAIN)
-		if not oe or oe:attr("temporary") then return true end
-		local e = mod.class.Object.new{
-			old_feat = oe, type = oe.type, subtype = oe.subtype,
-			name = "temporal instability", image = oe.image, add_mos = {{image="object/temporal_instability.png"}},
-			display = '&', color=colors.LIGHT_BLUE,
-			temporary = t.getDuration(self, t),
-			canAct = false,
-			target = target,
-			act = function(self)
-				self:useEnergy()
-				self.temporary = self.temporary - 1
-				-- return the rifted actor
-				if self.temporary <= 0 then
-					game.level.map(self.target.x, self.target.y, engine.Map.TERRAIN, self.old_feat)
-					game.nicer_tiles:updateAround(game.level, self.target.x, self.target.y)
-					game.level:removeEntity(self)
-					local mx, my = util.findFreeGrid(self.target.x, self.target.y, 20, true, {[engine.Map.ACTOR]=true})
-					local old_levelup = self.target.forceLevelup
-					self.target.forceLevelup = function() end
-					game.zone:addEntity(game.level, self.target, "actor", mx, my)
-					self.target.forceLevelup = old_levelup
-				end
-			end,
-			summoner_gain_exp = true, summoner = self,
-		}
+		local damage = self:spellCrit(t.getDamage(self, t))
+		self:project(tg, self.x, self.y, function(px, py)
+			DamageType:get(DamageType.TEMPORAL).projector(self, px, py, DamageType.TEMPORAL, damage)
+			
+			-- Echo
+			local target = game.level.map(px, py, Map.ACTOR)
+			if not target then return end
+			local percent = t.getPercent(self, t)/target.rank
+			local dam = (target.max_life - target.life) * percent
+			DamageType:get(DamageType.TEMPORAL).projector(self, px, py, DamageType.TEMPORAL, dam)
+		end)
 		
-		game.logSeen(target, "%s has moved forward in time!", target.name:capitalize())
-		game.level:removeEntity(target, true)
-		game.level:addEntity(e)
-		game.level.map(x, y, Map.TERRAIN, e)
-		game.nicer_tiles:updateAround(game.level, x, y)
-		game.level.map:updateMap(x, y)
+		game.level.map:particleEmitter(self.x, self.y, tg.radius, "ball_temporal", {radius=tg.radius})
+		game:playSoundNear(self, "talents/teleport")
 		return true
 	end,
 	info = function(self, t)
+		local percent = t.getPercent(self, t) * 100
+		local radius = self:getTalentRadius(t)
 		local damage = t.getDamage(self, t)
-		local duration = t.getDuration(self, t)
-		return ([[Inflicts %0.2f temporal damage, if the target fails a spell save.  If your target survives, it will be removed from time for %d turns.
-		The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration)
+		return ([[Creates a temporal echo in a radius of %d around you.  Affected targets will take %0.2f temporal damage, as well as up to %d%% of the difference between their current life and max life as additional temporal damage.
+		The additional damage will be divided by the target's rank and the damage scales with your Spellpower.]]):
+		format(radius, damDesc(self, DamageType.TEMPORAL, damage), percent)
 	end,
 }
 
 newTalent{
 	name = "Temporal Reprieve",
-	type = {"chronomancy/timetravel", 2},
-	require = chrono_req_high2,
+	type = {"chronomancy/timetravel", 3},
+	require = chrono_req3,
 	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 36) end,
-	cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 10, 45, 25)) end, -- Limit >10
+	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
+	cooldown = 40,
 	no_npc_use = true,
-	on_pre_use = function(self, t) return self:canBe("planechange") end,
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end,
 	fixed_cooldown = true,
+	on_pre_use = function(self, t) return self:canBe("planechange") end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 2, 6))) end,
 	action = function(self, t)
 		if game.zone.is_temporal_reprieve then
 			game.logPlayer(self, "This talent cannot be used from within the reprieve.")
@@ -157,7 +202,7 @@ newTalent{
 
 			self.temporal_reprieve_on_die = self.on_die
 			self.on_die = function(self, ...)
-				self:removeEffect(self.EFF_DREAMSCAPE)
+				self:removeEffect(self.EFF_TEMPORAL_REPRIEVE)
 				local args = {...}
 				game:onTickEnd(function()
 					if self.temporal_reprieve_on_die then self:temporal_reprieve_on_die(unpack(args)) end
@@ -182,175 +227,96 @@ newTalent{
 }
 
 newTalent{
-	name = "Echoes From The Past",
-	type = {"chronomancy/timetravel", 3},
-	require = chrono_req_high3,
+	name = "Time Skip",
+	type = {"chronomancy/timetravel",4},
+	require = chrono_req4,
 	points = 5,
-	paradox = function (self, t) return getParadoxCost(self, t, 20) end,
-	cooldown = 10,
-	tactical = { ATTACKAREA = {TEMPORAL = 2} },
-	range = 0,
-	radius = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end,
+	cooldown = 4,
+	paradox = function (self, t) return getParadoxCost(self, t, 10) end,
+	tactical = { ATTACK = {TEMPORAL = 1}, DISABLE = 2 },
+	range = 10,
+	direct_hit = true,
+	requires_target = true,
 	target = function(self, t)
-		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t}
+		return {type="hit", range=self:getTalentRange(t), talent=t}
 	end,
-	direct_hit = true,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 18, 160, getParadoxSpellpower(self)) end,
-	getPercent = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 1, 10, getParadoxSpellpower(self)), 0.5, .1, 0, 0.1575, 5.75) end, -- Limit to <50%
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 250, getParadoxSpellpower(self, t)) end,
+	getDuration = function(self, t) return getExtensionModifier(self, t, 2 + math.ceil(self:combatTalentScale(self:getTalentLevel(t), 0.3, 2.3))) end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
-		self:project(tg, self.x, self.y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t)))
-		self:project(tg, self.x, self.y, DamageType.TEMPORAL_ECHO, t.getPercent(self, t))
-		game.level.map:particleEmitter(self.x, self.y, tg.radius, "ball_temporal", {radius=tg.radius})
-		game:playSoundNear(self, "talents/teleport")
-		return true
-	end,
-	info = function(self, t)
-		local percent = t.getPercent(self, t) * 100
-		local radius = self:getTalentRadius(t)
-		local damage = t.getDamage(self, t)
-		return ([[Creates a temporal echo in a nova around you, in a radius of %d.  Affected targets will take %0.2f temporal damage, as well as %d%% of the difference between their current life and max life as additional temporal damage.
-		The percentage and damage scales with your Spellpower.]]):
-		format(radius, damDesc(self, DamageType.TEMPORAL, damage), percent)
-	end,
-}
-
-newTalent{
-	name = "Damage Smearing",
-	type = {"chronomancy/timetravel", 4},
-	mode = "sustained",
-	require = chrono_req_high4,
-	sustain_paradox = 84,
-	cooldown = 24,
-	tactical = { DEFEND = 2 },
-	points = 5,
-	-- called in damage_types to split the damage
-	getPercent = function(self, t) return self:combatTalentLimit(t, 50, 10, 30)/100 end, -- Limit < 50%
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 6)) end,
-	activate = function(self, t)
-		game:playSoundNear(self, "talents/spell_generic")
-		return {}
-	end,
-	deactivate = function(self, t, p)
-		return true
-	end,
-	info = function(self, t)
-		local percent = t.getPercent(self, t) * 100
-		local duration = t.getDuration(self, t)
-		return ([[You convert %d%% of all non-temporal damage you receive into temporal damage spread out over %d turns.
-		]]):format(percent, duration)
-	end,
-}
+		local x, y = self:getTarget(tg)
+		if not x or not y then return nil end
+		local _ _, x, y = self:canProject(tg, x, y)
+		local target = game.level.map(x, y, Map.ACTOR)
+		if not target then return end
 
---[=[
-newTalent{
-	name = "Door to the Past",
-	type = {"chronomancy/timetravel", 4},
-	require = chrono_req_high4, no_sustain_autoreset = true,
-	points = 5,
-	mode = "sustained",
-	sustain_paradox = 150,
-	cooldown = 25,
-	no_npc_use = true,
-	getAnomalyCount = function(self, t) return math.ceil(self:getTalentLevel(t)) end,
-	on_learn = function(self, t)
-		if not self:knowTalent(self.T_REVISION) then
-			self:learnTalent(self.T_REVISION, nil, nil, {no_unlearn=true})
-		end
-	end,
-	on_unlearn = function(self, t)
-		if not self:knowTalent(t) then
-			self:unlearnTalent(self.T_REVISION)
-		end
-	end,
-	do_anomalyCount = function(self, t)
-		if self.dttp_anomaly_count == 0 then
-			-- check for anomaly
-			if not game.zone.no_anomalies and not self:attr("no_paradox_fail") and self:paradoxFailChance() then
-				-- Random anomaly
-				local ts = {}
-				for id, t in pairs(self.talents_def) do
-					if t.type[1] == "chronomancy/anomalies" then ts[#ts+1] = id end
-				end
-				if not silent then game.logPlayer(self, "Your Door to the Past has caused an anomaly!") end
-				self:forceUseTalent(rng.table(ts), {ignore_energy=true})
-			end
-			-- reset count
-			self.dttp_anomaly_count = t.getAnomalyCount(self, t)
-		else
-			self.dttp_anomaly_count = self.dttp_anomaly_count - 1
-		end
-	end,
-	activate = function(self, t)
-		if checkTimeline(self) == true then
-			return
+		if target:attr("timetravel_immune") then
+			game.logSeen(target, "%s is immune!", target.name:capitalize())
+			return true
 		end
 
-		-- set the counter
-		self.dttp_anomaly_count = t.getAnomalyCount(self, t)
-
+		-- Project our damage
+		self:project(tg, x, y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t)))
+		
+		game.level.map:particleEmitter(x, y, 1, "temporal_thrust")
 		game:playSoundNear(self, "talents/arcane")
-		return {
-			game:onTickEnd(function()
-				game:chronoClone("revision")
-			end),
-			particle = self:addParticles(Particles.new("temporal_aura", 1)),
+		
+		-- If they're dead don't remove them from time
+		if target.dead or target.player then return true end
+		
+		-- Check hit
+		local power = getParadoxSpellpower(self, t)
+		local hit = self:checkHit(power, target:combatSpellResist() + (target:attr("continuum_destabilization") or 0))
+		if not hit then game.logSeen(target, "%s resists!", target.name:capitalize()) return true end
+		
+		-- Apply spellshock and destabilization
+		target:crossTierEffect(target.EFF_SPELLSHOCKED, getParadoxSpellpower(self, t))
+		target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=getParadoxSpellpower(self, t, 0.3)})
+		
+		-- Placeholder for the actor
+		local oe = game.level.map(target.x, target.y, engine.Map.TERRAIN)
+		if not oe or oe:attr("temporary") then return true end
+		local e = mod.class.Object.new{
+			old_feat = oe, type = oe.type, subtype = oe.subtype,
+			name = "temporal instability", image = oe.image, add_mos = {{image="object/temporal_instability.png"}},
+			display = '&', color=colors.LIGHT_BLUE,
+			temporary = t.getDuration(self, t),
+			canAct = false,
+			target = target,
+			act = function(self)
+				self:useEnergy()
+				self.temporary = self.temporary - 1
+				-- return the rifted actor
+				if self.temporary <= 0 then
+					game.level.map(self.target.x, self.target.y, engine.Map.TERRAIN, self.old_feat)
+					game.nicer_tiles:updateAround(game.level, self.target.x, self.target.y)
+					game.level:removeEntity(self)
+					local mx, my = util.findFreeGrid(self.target.x, self.target.y, 20, true, {[engine.Map.ACTOR]=true})
+					local old_levelup = self.target.forceLevelup
+					local old_check = self.target.check
+					self.target.forceLevelup = function() end
+					self.target.check = function() end
+					game.zone:addEntity(game.level, self.target, "actor", mx, my)
+					self.target.forceLevelup = old_levelup
+					self.target.check = old_check
+				end
+			end,
+			summoner_gain_exp = true, summoner = self,
 		}
-	end,
-	deactivate = function(self, t, p)
-		if game._chronoworlds then game._chronoworlds = nil end
-		self.dttp_anomaly_count = nil
-		self:removeParticles(p.particle)
-		return true
-	end,
-	info = function(self, t)
-		local count = t.getAnomalyCount(self, t)
-		return ([[This powerful spell allows you to mark a point in time that you can later return to by casting Revision (which you'll automatically learn upon learning this spell).  Maintaining such a doorway causes constant strain on the spacetime continuum and can possibly trigger an anomaly (using your current anomaly chance) once every %d turns.
-		This spell splits the timeline.  Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful.
-		Additional talent points will increase the time between anomaly checks.]]):
-		format(count)
-	end,
-}
-
-newTalent{
-	name = "Revision",
-	type = {"chronomancy/other", 1},
-	type_no_req = true,
-	points = 1,
-	message = "@Source@ revises history.",
-	cooldown = 50,
-	paradox = 25,
-	no_npc_use = true,
-	on_pre_use = function(self, t, silent) if not self:isTalentActive(self.T_DOOR_TO_THE_PAST) then if not silent then game.logPlayer(self, "Door to the Past must be active to use this talent.") end return false end return true end,
-	no_unlearn_last = true,
-	action = function(self, t)
-
-		-- Prevent Revision After Death
-		if game._chronoworlds == nil then
-			game.logPlayer(game.player, "#LIGHT_RED#Your spell fizzles.")
-			return
-		end
-
-		game:onTickEnd(function()
-			if not game:chronoRestore("revision", true) then
-				game.logSeen(self, "#LIGHT_RED#The spell fizzles.")
-				return
-			end
-			game.logPlayer(game.player, "#LIGHT_BLUE#You unfold the spacetime continuum to a previous state!")
-
-			-- Manualy start the cooldown of the "old player"
-			game.player:startTalentCooldown(t)
-			game.player:incParadox(t.paradox * (1 + (game.player.paradox / 300)))
-			game.player:forceUseTalent(game.player.T_DOOR_TO_THE_PAST, {ignore_energy=true})
-			-- remove anomaly count
-			if self.dttp_anomaly_count then self.dttp_anomaly_count = nil end
-			if game._chronoworlds then game._chronoworlds = nil end
-		end)
-
+		
+		-- Replace the target with the instability and update the map
+		game.logSeen(target, "%s has moved forward in time!", target.name:capitalize())
+		game.level:removeEntity(target, true)
+		game.level:addEntity(e)
+		game.level.map(x, y, Map.TERRAIN, e)
+		game.nicer_tiles:updateAround(game.level, x, y)
+		game.level.map:updateMap(x, y)
 		return true
 	end,
 	info = function(self, t)
-		return ([[Casting Revision will return you to the point in time you created a temporal marker using Door to the Past.]])
-		:format()
+		local damage = t.getDamage(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[Inflicts %0.2f temporal damage.  If your target survives, it may be removed from time for %d turns.
+		The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration)
 	end,
-}]=]
+}
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/misc/inscriptions.lua b/game/modules/tome/data/talents/misc/inscriptions.lua
index 2851df5e6fbcf4424504c3795868a35efab3c145..1fdda0c562ac260aed2b31db3e5979cbfd669f4c 100644
--- a/game/modules/tome/data/talents/misc/inscriptions.lua
+++ b/game/modules/tome/data/talents/misc/inscriptions.lua
@@ -1012,7 +1012,7 @@ newInscription{
 
 		if target:attr("timetravel_immune") then
 			game.logSeen(target, "%s is immune!", target.name:capitalize())
-			return
+			return true
 		end
 
 		local hit = self:checkHit(self:combatSpellpower(), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0))
@@ -1021,6 +1021,7 @@ newInscription{
 		self:project(tg, x, y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t)))
 		game.level.map:particleEmitter(x, y, 1, "temporal_thrust")
 		game:playSoundNear(self, "talents/arcane")
+		self:incParadox(-25)
 		if target.dead or target.player then return true end
 		target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self:combatSpellpower(0.3)})
 		
@@ -1044,9 +1045,12 @@ newInscription{
 					game.nicer_tiles:updateAround(game.level, self.target.x, self.target.y)
 					local mx, my = util.findFreeGrid(self.target.x, self.target.y, 20, true, {[engine.Map.ACTOR]=true})
 					local old_levelup = self.target.forceLevelup
+					local old_check = self.target.check
 					self.target.forceLevelup = function() end
+					self.target.check = function() end
 					game.zone:addEntity(game.level, self.target, "actor", mx, my)
 					self.target.forceLevelup = old_levelup
+					self.target.check = old_check
 				end
 			end,
 			summoner_gain_exp = true, summoner = self,
@@ -1064,6 +1068,7 @@ newInscription{
 		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 25 (if you have any).
 		Note that messing with the spacetime continuum may have unforeseen consequences.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration)
 	end,
 	short_info = function(self, t)
diff --git a/game/modules/tome/data/talents/misc/npcs.lua b/game/modules/tome/data/talents/misc/npcs.lua
index dd5d107c2afceee41e74c02d31f07fb516b1977d..954bb1faf659a9c1820f0a38b5a22fd6726f7aa2 100644
--- a/game/modules/tome/data/talents/misc/npcs.lua
+++ b/game/modules/tome/data/talents/misc/npcs.lua
@@ -1550,7 +1550,7 @@ newTalent{
 	direct_hit = true,
 	reflectable = true,
 	requires_target = true,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 220, getParadoxSpellpower(self)) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 220, getParadoxSpellpower(self, t)) end,
 	action = function(self, t)
 		local tg = {type="hit", range=self:getTalentRange(t), talent=t}
 		local x, y = self:getTarget(tg)
@@ -1637,7 +1637,7 @@ newTalent{
 	range = 10,
 	direct_hit = true,
 	requires_target = true,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 220, getParadoxSpellpower(self)) * 10000 end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 220, getParadoxSpellpower(self, t)) * 10000 end,
 	action = function(self, t)
 		local tg = {type="hit", range=self:getTalentRange(t), talent=t}
 		local x, y = self:getTarget(tg)
diff --git a/game/modules/tome/data/talents/uber/mag.lua b/game/modules/tome/data/talents/uber/mag.lua
index 0f1d0971c6b903d069f8c2ddff34eeb8eb8efc68..4d8ea5aa1760ddbb595e928f96091ca509342c68 100644
--- a/game/modules/tome/data/talents/uber/mag.lua
+++ b/game/modules/tome/data/talents/uber/mag.lua
@@ -108,8 +108,7 @@ uberTalent{
 	info = function(self, t)
 		return ([[You can wrap temporal threads around you, assuming the form of a telugoroth for 10 turns.
 		While in this form you gain pinning, bleeding, blindness and stun immunity, 30%% temporal resistance, your temporal damage bonus is set to your current highest damage bonus + 30%%, 50%% of the damage you deal becomes temporal, and you gain 20%% temporal resistance penetration.
-		You also are able to cast two anomalies: Anomaly Rearrange and Anomaly Temporal Storm.
-		Transforming to this form will increase your paradox by 400 but also grant the equivalent of +200 willpower to control paradoxes and failures. This reverts back at the end of the effect.]])
+		You also are able to cast two anomalies: Anomaly Rearrange and Anomaly Temporal Storm.]])
 		:format()
 	end,
 }
@@ -182,6 +181,8 @@ uberTalent{
 			who:learnTalent(who.T_BONE_SHIELD,true,math.ceil(tlevel*2/3))
 		elseif who.name == "mucus ooze" then
 			who:learnTalent(who.T_VIRULENT_DISEASE,true,tlevel)
+		elseif who.name == "temporal hound" then
+			who:learnTalent(who.T_ELEMENTAL_DISCORD,true,tlevel)
 		else
 --			print("Error: attempting to apply talent Blighted Summoning to incorrect creature type")
 			return false
@@ -214,6 +215,7 @@ uberTalent{
 		- Yeek Wayists: Dark Portal
 		- Bloated Oozes: Bone Shield (level %d)
 		- Mucus Oozes: Virulent Disease
+		- Temporal Hounds: Elemental Discord
 		Your necrotic minions and wild-summons get a bonus to Magic equal to yours.
 		The talent levels increase with your level, and other race- or object-based summons may also be affected.
 		]]):format(tl,math.ceil(tl*2/3))
diff --git a/game/modules/tome/data/timed_effects/magical.lua b/game/modules/tome/data/timed_effects/magical.lua
index 0392f2f86f229afcb200ac0d6d338ab345d64501..f2bba636a0bdf19cf5fae1fe5544f1b2f5e8487b 100644
--- a/game/modules/tome/data/timed_effects/magical.lua
+++ b/game/modules/tome/data/timed_effects/magical.lua
@@ -1479,15 +1479,11 @@ newEffect{
 newEffect{
 	name = "REDUX", image = "talents/redux.png",
 	desc = "Redux",
-	long_desc = function(self, eff) return "The next activated chronomancy talent that the target uses will be cast twice." end,
+	long_desc = function(self, eff) return ("Chronomancy spells with cooldown less than %d will not go on cooldown when cast."):format(eff.max_cd) end,
 	type = "magical",
 	subtype = { temporal=true },
 	status = "beneficial",
-	parameters = { power=1},
-	activate = function(self, eff)
-	end,
-	deactivate = function(self, eff)
-	end,
+	parameters = { max_cd=1},
 }
 
 newEffect{
@@ -1533,18 +1529,18 @@ newEffect{
 newEffect{
 	name = "HASTE", image = "talents/haste.png",
 	desc = "Haste",
-	long_desc = function(self, eff) return ("Increases global action speed by %d%%."):format(eff.power * 100) end,
+	long_desc = function(self, eff) return ("Increases movement speed by %d%% and attack, spell, and mind speed by %d%%."):format(eff.move * 100, eff.speed * 100) end,
 	type = "magical",
 	subtype = { temporal=true },
 	status = "beneficial",
-	parameters = { power=0.1 },
+	parameters = { move=0.1, speed=0.1 },
 	on_gain = function(self, err) return "#Target# speeds up.", "+Haste" end,
 	on_lose = function(self, err) return "#Target# slows down.", "-Haste" end,
-	damageOnAct = function(self, eff)
-		DamageType:get(DamageType.MATTER).projector(eff.src or self, self.x, self.y, DamageType.MATTER, eff.src:spellCrit(eff.dam))
-	end,
 	activate = function(self, eff)
-		eff.glbid = self:addTemporaryValue("global_speed_add", eff.power)
+		self:effectTemporaryValue(eff, "movement_speed", eff.move)
+		self:effectTemporaryValue(eff, "combat_physspeed", eff.speed)
+		self:effectTemporaryValue(eff, "combat_spellspeed", eff.speed)
+		self:effectTemporaryValue(eff, "combat_mindspeed", eff.speed)
 	end,
 	deactivate = function(self, eff)
 		self:removeTemporaryValue("global_speed_add", eff.glbid)
@@ -1554,11 +1550,11 @@ newEffect{
 newEffect{
 	name = "CEASE_TO_EXIST", image = "talents/cease_to_exist.png",
 	desc = "Cease to Exist",
-	long_desc = function(self, eff) return ("The target is being removed from the timeline, reducing all resistances by %d%%."):format(eff.power) end,
+	long_desc = function(self, eff) return ("The target is being removed from the timeline, all its resistances have been lowered by %d%%."):format(eff.power) end,
 	type = "magical",
 	subtype = { temporal=true },
 	status = "detrimental",
-	parameters = { power = 1 },
+	parameters = { power = 1, damage=1 },
 	on_gain = function(self, err) return "#Target# is being removed from the timeline.", "+Cease to Exist" end,
 	activate = function(self, eff)
 		eff.resists = self:addTemporaryValue("resists", { all = -eff.power})
@@ -1634,13 +1630,13 @@ newEffect{
 newEffect{
 	name = "SPIN_FATE", image = "talents/spin_fate.png",
 	desc = "Spin Fate",
-	long_desc = function(self, eff) return ("The target's defense and saves have been increased by %d and it's resist all by %d%%."):format(eff.save_bonus * eff.spin, eff.resists * eff.spin) end,
+	long_desc = function(self, eff) return ("The target's defense and saves have been increased by %d."):format(eff.save_bonus * eff.spin) end,
 	display_desc = function(self, eff) return eff.spin.." Spin" end,
 	charges = function(self, eff) return eff.spin end,
 	type = "magical",
 	subtype = { temporal=true },
 	status = "beneficial",
-	parameters = { save_bonus=0, spin=0, resists=0, max_spin=3},
+	parameters = { save_bonus=0, spin=0, max_spin=3},
 	on_gain = function(self, err) return "#Target# spins fate.", "+Spin Fate" end,
 	on_lose = function(self, err) return "#Target#'s fate is no longer being spun.", "-Spin Fate" end,
 	on_merge = function(self, old_eff, new_eff)
@@ -1649,7 +1645,6 @@ newEffect{
 		self:removeTemporaryValue("combat_physresist", old_eff.physid)
 		self:removeTemporaryValue("combat_spellresist", old_eff.spellid)
 		self:removeTemporaryValue("combat_mentalresist", old_eff.mentalid)
-		self:removeTemporaryValue("resists", old_eff.resid)
 		
 		-- add some spin
 		old_eff.spin = math.min(old_eff.spin + 1, new_eff.max_spin)
@@ -1659,7 +1654,6 @@ newEffect{
 		old_eff.physid = self:addTemporaryValue("combat_physresist", old_eff.save_bonus * old_eff.spin)
 		old_eff.spellid = self:addTemporaryValue("combat_spellresist", old_eff.save_bonus * old_eff.spin)
 		old_eff.mentalid = self:addTemporaryValue("combat_mentalresist", old_eff.save_bonus * old_eff.spin)
-		old_eff.resid = self:addTemporaryValue("resists", {all =  old_eff.resists * old_eff.spin})
 
 		old_eff.dur = new_eff.dur
 		
@@ -1671,7 +1665,6 @@ newEffect{
 		eff.physid = self:addTemporaryValue("combat_physresist", eff.save_bonus * eff.spin)
 		eff.spellid = self:addTemporaryValue("combat_spellresist", eff.save_bonus * eff.spin)
 		eff.mentalid = self:addTemporaryValue("combat_mentalresist", eff.save_bonus * eff.spin)
-		eff.resid = self:addTemporaryValue("resists", {all = eff.resists *eff.spin})
 		eff.particle = self:addParticles(Particles.new("arcane_power", 1))
 	end,
 	deactivate = function(self, eff)
@@ -1679,7 +1672,6 @@ newEffect{
 		self:removeTemporaryValue("combat_physresist", eff.physid)
 		self:removeTemporaryValue("combat_spellresist", eff.spellid)
 		self:removeTemporaryValue("combat_mentalresist", eff.mentalid)
-		self:removeTemporaryValue("resists", eff.resid)
 		self:removeParticles(eff.particle)
 	end,
 }
@@ -2311,10 +2303,8 @@ newEffect{
 		self:effectTemporaryValue(eff, "resists", {[DamageType.TEMPORAL] = 30})
 		self:effectTemporaryValue(eff, "resists_pen", {[DamageType.TEMPORAL] = 20})
 		self:effectTemporaryValue(eff, "talent_cd_reduction", {[self.T_ANOMALY_REARRANGE] = -4, [self.T_ANOMALY_TEMPORAL_STORM] = -4})
-		self:effectTemporaryValue(eff, "paradox_reduce_anomalies", 200)
 		self:learnTalent(self.T_ANOMALY_REARRANGE, true)
 		self:learnTalent(self.T_ANOMALY_TEMPORAL_STORM, true)
-		self:incParadox(400)
 
 		self.replace_display = mod.class.Actor.new{
 			image = "npc/elemental_temporal_telugoroth.png",
@@ -2325,7 +2315,6 @@ newEffect{
 		game.level.map:updateMap(self.x, self.y)
 	end,
 	deactivate = function(self, eff)
-		self:incParadox(-400)
 		self:unlearnTalent(self.T_ANOMALY_REARRANGE)
 		self:unlearnTalent(self.T_ANOMALY_TEMPORAL_STORM)
 		self.replace_display = nil
@@ -2870,28 +2859,10 @@ newEffect{
 	end,
 }
 
-newEffect{
-	name = "TIME_DILATION", image = "talents/time_dilation.png",
-	desc = "Time Dilation",
-	long_desc = function(self, eff) return ("Reduces global action speed by %d%%."):format(eff.slow * 100) end,
-	type = "magical",
-	subtype = { temporal=true, slow=true },
-	status = "detrimental",
-	parameters = { slow=0.1},
-	on_gain = function(self, err) return "#Target# slows down.", "+Time Dilation" end,
-	on_lose = function(self, err) return "#Target# speeds up.", "-Time Dilation" end,
-	activate = function(self, eff)
-		eff.tmpid = self:addTemporaryValue("global_speed_add", -eff.slow)
-	end,
-	deactivate = function(self, eff)
-		self:removeTemporaryValue("global_speed_add", eff.tmpid)
-	end,
-}
-
 newEffect{
 	name = "DIMENSIONAL_ANCHOR", image = "talents/dimensional_anchor.png",
 	desc = "Dimensional Anchor",
-	long_desc = function(self, eff) return ("The target is unable to teleport and takes %0.2f temporal and %0.2f physical damage if they try."):format(eff.slow * 100) end,
+	long_desc = function(self, eff) return ("The target is unable to teleport and takes %0.2f temporal and %0.2f physical damage if they try."):format(eff.damage, eff.damage) end,
 	type = "magical",
 	subtype = { temporal=true, slow=true },
 	status = "detrimental",
@@ -2941,15 +2912,27 @@ newEffect{
 	parameters = { power=0 },
 	on_gain = function(self, err) return "#Target#'s lifeline has been braided.", "+Braided" end,
 	on_lose = function(self, err) return "#Target#'s lifeline is no longer braided.", "-Braided" end,
-	on_timeout = function(self, eff)
-		local braids = 0
-		if eff.braid_one and eff.braid_one:hasEffect(eff.braid_one.EFF_BRAIDED) and not eff.braid_one.dead then
-			braids = braids + 1
+	doBraid = function(self, eff, dam)
+		local braid_damage = dam * eff.power/ 100
+		for i = 1, #eff.targets do
+			local target = eff.targets[i]
+			if target ~= self and not target.dead then
+				game:delayedLogMessage(eff.src, target, "braided", "#CRIMSON##Source# damages #Target# through the Braid!")
+				game:delayedLogDamage(eff.src, target, braid_damage, ("#PINK#%d braided #LAST#"):format(braid_damage), false)
+				target:takeHit(braid_damage, eff.src)
+			end
 		end
-		if eff.braid_two and eff.braid_two:hasEffect(eff.braid_two.EFF_BRAIDED) and not eff.braid_two.dead then
-			braids = braids + 1
+	end,
+	on_timeout = function(self, eff)
+		local alive = false
+		for i = 1, #eff.targets do
+			local target = eff.targets[i]
+			if target ~=self and not target.dead then
+				alive = true
+				break
+			end
 		end
-		if braids == 0 then		
+		if not alive then
 			self:removeEffect(self.EFF_BRAIDED)
 		end
 	end,
@@ -2980,16 +2963,16 @@ newEffect{
 newEffect{
 	name = "WEBS_OF_FATE", image = "talents/webs_of_fate.png",
 	desc = "Webs of Fate",
-	long_desc = function(self, eff) return ("Moving along the webs of fate, increasing move speed by %d%% and pin immunity by %d%%."):format(eff.move*100, eff.pin*100) end,
+	long_desc = function(self, eff) return ("Moving along the webs of fate, increasing stun and pin immunity by %d%%."):format(eff.imm*100) end,
 	type = "magical",
 	subtype = { temporal=true, speed=true },
 	status = "beneficial",
 	on_gain = function(self, err) return "#Target# moves along the webs of fate.", "+Fate Webs" end,
 	on_lose = function(self, err) return "#Target# is no longer moving along the webs of fate.", "-Fate Webs" end,
-	parameters = { move=0.1, pin=0.1 },
+	parameters = { imm=0.1 },
 	activate = function(self, eff)
-		self:effectTemporaryValue(eff, "pin_immune", eff.pin)
-		self:effectTemporaryValue(eff, "movement_speed", eff.move)
+		self:effectTemporaryValue(eff, "pin_immune", eff.imm)
+		self:effectTemporaryValue(eff, "stun_immune", eff.imm)
 	end,
 	deactivate = function(self, eff)
 	end,
@@ -2998,18 +2981,45 @@ newEffect{
 newEffect{
 	name = "SEAL_FATE", image = "talents/seal_fate.png",
 	desc = "Seal Fate",
-	long_desc = function(self, eff) return ("The target is sealing fate, increasing critical chance and critical power by %d%%."):format(eff.crit) end,
-	type = "mental",
+	long_desc = function(self, eff) return ("The target is sealing fate, increasing the duration of detrimental status effects on targets it damages by one."):format() end,
+	type = "magical",
 	subtype = { focus=true },
 	status = "beneficial",
-	parameters = { crit=10 },
+	parameters = { procs=1 },
 	on_gain = function(self, err) return "#Target# seals fate.", "+Seal Fate" end,
 	on_lose = function(self, err) return "#Target# is no longer sealing fate.", "-Seal Fate" end,
+	doDamage = function(self, eff, target)
+		if self.turn_procs and target.tmp then
+			if self.turn_procs.seal_fate and self.turn_procs.seal_fate >= eff.procs then return end
+			local chance = 50
+			local spin = self:hasEffect(self.EFF_SPIN_FATE)
+			if spin then
+				chance = chance * (1 + spin.spin/3)
+			end
+			
+			if rng.percent(chance) then
+				local effs = {}
+				-- Go through all spell effects
+				for eff_id, p in pairs(target.tmp) do
+					local e = target.tempeffect_def[eff_id]
+					if e.status == "detrimental" and e.type ~= "other" then
+						effs[#effs+1] = p
+					end
+				end
+				
+				if #effs > 0 then
+					local p = rng.table(effs)
+					game.logPlayer(self, "%s", p.name)
+					game.logPlayer(self, "%s", p.dur)
+					p.dur = p.dur + 1
+				end
+			
+				self.turn_procs.seal_fate = (self.turn_procs.seal_fate or 0) + 1
+			end
+			
+		end
+	end,
 	activate = function(self, eff)
-		self:effectTemporaryValue(eff, "combat_physcrit", eff.crit)
-		self:effectTemporaryValue(eff, "combat_spellcrit", eff.crit)
-		self:effectTemporaryValue(eff, "combat_mindcrit", eff.crit)
-		self:effectTemporaryValue(eff, "combat_critical_power", eff.crit)
 	end,
 }
 
@@ -3045,6 +3055,80 @@ newEffect{
 	end,
 }
 
+newEffect{
+	name = "ENTROPY", image = "talents/entropy.png",
+	desc = "Entropy",
+	long_desc = function(self, eff) return ("The target's timed effects are ticking twice as fast and it's taking %d temporal damage per turn, per timed effect."):format(eff.power) end,
+	on_gain = function(self, err) return "#Target# is caught in an entropic field!", "+Entropy" end,
+	on_lose = function(self, err) return "#Target# is free from the entropy.", "-Entropy" end,
+	type = "magical",
+	subtype = { temporal=true },
+	status = "detrimental",
+	parameters = {power=10},
+	on_timeout = function(self, eff)
+		local count = 0
+		local todel = {}
+		
+		-- Go through all spell effects
+		for eff_id, p in pairs(self.tmp) do
+			local e = self.tempeffect_def[eff_id]
+			if e.type ~= "other" and e.status == "beneficial" then
+				if p.dur <= 0 then 
+					todel[#todel+1] = eff 
+				else
+					if e.on_timeout then
+						if p.src then p.src.__project_source = p end -- intermediate projector source
+						if e.on_timeout(self, p) then
+							todel[#todel+1] = eff
+						end
+						if p.src then p.src.__project_source = nil end
+					end
+				end
+				count = count + 1
+				p.dur = p.dur - e.decrease
+			end
+		end
+
+		DamageType:get(DamageType.TEMPORAL).projector(eff.src, self.x, self.y, DamageType.TEMPORAL, eff.power*count)
+				
+		while #todel > 0 do
+			self:removeEffect(table.remove(todel))
+		end
+	end,
+}
+
+newEffect{
+	name = "PHASE_SHIFT", image = "talents/phase_shift.png",
+	desc = "Phase Shift",
+	long_desc = function(self, eff) return ("When hit for more than 10%% of your maximum life you teleport and reappear near where you were, reducuing the damage by 50%%.") end,
+	type = "magical",
+	subtype = { arcane=true },
+	status = "beneficial",
+	parameters = { },
+	activate = function(self, eff)
+		self:effectTemporaryValue(eff, "phase_shift_chrono", 1)
+	end,
+}
+
+newEffect{
+	name = "REGRESSION", image = "talents/temporal_bolt.png",
+	desc = "Regression",
+	long_desc = function(self, eff)	return ("Reduces your three highest stats by %d."):format(eff.power) end,
+	on_gain = function(self, err) return "#Target# has regressed.", "+Regression" end,
+	on_lose = function(self, err) return "#Target# has returned to its natural state.", "-Regression" end,
+	type = "physical",
+	subtype = { temporal=true },
+	status = "detrimental",
+	parameters = { power=1},
+	activate = function(self, eff)
+		local l = { {Stats.STAT_STR, self:getStat("str")}, {Stats.STAT_DEX, self:getStat("dex")}, {Stats.STAT_CON, self:getStat("con")}, {Stats.STAT_MAG, self:getStat("mag")}, {Stats.STAT_WIL, self:getStat("wil")}, {Stats.STAT_CUN, self:getStat("cun")}, }
+		table.sort(l, function(a,b) return a[2] > b[2] end)
+		local inc = {}
+		for i = 1, 3 do inc[l[i][1]] = -eff.power end
+		self:effectTemporaryValue(eff, "inc_stats", inc)
+	end,
+}
+
 newEffect{
 	name = "TRIM_THREADS", image = "talents/trim_threads.png",
 	desc = "Trim Threads",
@@ -3067,4 +3151,4 @@ newEffect{
 	on_timeout = function(self, eff)
 		DamageType:get(DamageType.TEMPORAL).projector(eff.src, self.x, self.y, DamageType.TEMPORAL, eff.power)
 	end,
-}
+}
\ No newline at end of file
diff --git a/game/modules/tome/data/timed_effects/other.lua b/game/modules/tome/data/timed_effects/other.lua
index ed2708d9d863e4bfee1b763966555e13a3618310..68aa850bba3cffea88b8ce58a068883863a3e62e 100644
--- a/game/modules/tome/data/timed_effects/other.lua
+++ b/game/modules/tome/data/timed_effects/other.lua
@@ -396,48 +396,24 @@ newEffect{
 newEffect{
 	name = "DAMAGE_SMEARING", image = "talents/damage_smearing.png",
 	desc = "Damage Smearing",
-	long_desc = function(self, eff) return ("Passes damage received in the present onto the future self."):format(eff.power) end,
-	type = "other",
-	subtype = { time=true },
-	status = "beneficial",
-	parameters = { power=10 },
-	on_gain = function(self, err) return "The fabric of time alters around #target#.", "+Damage Smearing" end,
-	on_lose = function(self, err) return "The fabric of time around #target# stabilizes.", "-Damage Smearing" end,
-	activate = function(self, eff)
-		eff.particle = self:addParticles(Particles.new("time_shield", 1))
-	end,
-	deactivate = function(self, eff)
-		self:removeParticles(eff.particle)
-	end,
-}
-
-newEffect{
-	name = "SMEARED",
-	desc = "Smeared",
-	long_desc = function(self, eff) return ("Damage received in the past is returned as %0.2f temporal damage per turn."):format(eff.power) end,
+	long_desc = function(self, eff) return ("Damage received in the past is returned as %0.2f temporal damage per turn."):format(eff.dam) end,
 	type = "other",
 	subtype = { time=true },
 	status = "detrimental",
-	parameters = { power=10 },
+	parameters = { dam=10 },
 	on_gain = function(self, err) return "#Target# is taking damage received in the past!", "+Smeared" end,
 	on_lose = function(self, err) return "#Target# stops taking damage received in the past.", "-Smeared" end,
 	on_merge = function(self, old_eff, new_eff)
 		-- Merge the flames!
-		local olddam = old_eff.power * old_eff.dur
-		local newdam = new_eff.power * new_eff.dur
-		local dur = math.ceil((old_eff.dur + new_eff.dur) / 2)
-		old_eff.dur = dur
-		old_eff.power = (olddam + newdam) / dur
-		return old_eff
+		local olddam = old_eff.dam * old_eff.dur
+		local newdam = new_eff.dam * new_eff.dur
+		new_eff.dam = (olddam + newdam) / new_eff.dur
+		return new_eff
 	end,
 	on_timeout = function(self, eff)
-		DamageType:get(DamageType.TEMPORAL).projector(eff.src, self.x, self.y, DamageType.TEMPORAL, eff.power)
-	end,
-	activate = function(self, eff)
-		eff.particle = self:addParticles(Particles.new("time_shield", 1))
-	end,
-	deactivate = function(self, eff)
-		self:removeParticles(eff.particle)
+		local dead, val = self:takeHit(eff.dam, self, {special_death_msg="was smeared across all space and time"})
+
+		game:delayedLogDamage(eff, self, val, ("%s%d %s#LAST#"):format(DamageType:get(DamageType.TEMPORAL).text_color or "#aaaaaa#", math.ceil(val), DamageType:get(DamageType.TEMPORAL).name), false)
 	end,
 }
 
@@ -2469,13 +2445,9 @@ newEffect{
 	subtype = { time=true },
 	status = "beneficial",
 	parameters = { power=10},
-	on_gain = function(self, err) return "#Target# retunes the fabric of spaceime.", "+Spacetime Tuning" end,
+	on_gain = function(self, err) return "#Target# retunes the fabric of spacetime.", "+Spacetime Tuning" end,
 	on_timeout = function(self, eff)
-		if not self.resting then
-			self:removeEffect(self.EFF_SPACETIME_TUNING)
-		else
-			self:incParadox(eff.power)
-		end
+		self:incParadox(eff.power)
 	end,
 }
 
@@ -2486,17 +2458,17 @@ newEffect{
 		return ("The target has stopped time and is dealing %d%% less damage."):format(eff.power)
 	end,
 	charges = function(self, eff)
-		local energy = self.energy.value
-		if energy < 2000 then
+		local charges = math.floor(self.energy.value/1000) - 1
+		if charges <= 0 then
 			self:removeEffect(self.EFF_TIME_STOP)
 		end
-		return math.floor(energy/1000) 
+		return charges
 	end,
 	type = "other",
 	subtype = {time=true},
 	status = "detrimental",
-	decrease = 0,
-	no_stop_enter_worlmap = true, no_stop_resting = true,
+	--decrease = 0,
+	--no_stop_enter_worlmap = true, no_stop_resting = true,
 	parameters = {power=50},
 	activate = function(self, eff)
 		self:effectTemporaryValue(eff, "generic_damage_penalty", eff.power)
@@ -2516,18 +2488,8 @@ newEffect{
 	on_timeout = function(self, eff)
 		-- Clone protection
 		if not self.on_die then return end
-		-- Certain talents don't cooldown in the reprieve
-		if not self:attr("no_talents_cooldown") then
-			for tid, _ in pairs(self.talents_cd) do
-				local t = self:getTalentFromId(tid)
-				if t and t.fixed_cooldown then
-					self.talents_cd[tid] = self.talents_cd[tid] + 1
-				end
-			end
-		end
 	end,
 	activate = function(self, eff)
-
 	end,
 	deactivate = function(self, eff)
 		-- Clone protection
@@ -2590,6 +2552,71 @@ newEffect{
 	end,
 }
 
+newEffect{
+	name = "TEMPORAL_FUGUE", image = "talents/temporal_fugue.png",
+	desc = "Temporal Fugue",
+	long_desc = function(self, eff) return "This target is splitting all damage with its fugue clones." end,
+	type = "other",
+	subtype = { time=true },
+	status = "beneficial",
+	parameters = { power=10 },
+	decrease = 0,
+	callbackOnHit = function(self, eff, cb, src)
+		local clones = {}
+		-- Find our clones
+		for i = 1, #eff.targets do
+			local target = eff.targets[i]
+			if target ~= self and not target.dead and game.level:hasEntity(target) then
+				clones[#clones+1] = target
+			end
+		end
+		
+		-- Split the damage
+		if #clones > 0 and not src.turn_procs.temporal_fugue_damage then
+			self.turn_procs.temporal_fugue_damage = true
+			cb.value = cb.value/#clones
+			game:delayedLogMessage(self, nil, "fugue_damage", "#STEEL_BLUE##Source# shares damage with %s fugue clones!", string.his_her(self))
+			for i = 1, #clones do
+				local target = clones[i]
+				if target ~= self then
+					target:takeHit(cb.value, self)
+					game:delayedLogDamage(src or self, self, 0, ("#STEEL_BLUE#(%d shared)#LAST#"):format(cb.value), nil)
+				end
+			end
+			
+			self.turn_procs.temporal_fugue_damage = nil
+		end
+		
+		-- If we're the last clone remove the effect
+		if #clones <= 0 then
+			self:removeEffect(self.EFF_TEMPORAL_FUGUE)
+		end
+		
+		return cb.value
+	end,
+	on_timeout = function(self, eff)
+		-- Temporal Fugue does not cooldown while active
+		if self.talents_cd[self.T_TEMPORAL_FUGUE] then
+			self.talents_cd[self.T_TEMPORAL_FUGUE] = self.talents_cd[self.T_TEMPORAL_FUGUE] + 1
+		end
+	
+		local alive = false
+		for i = 1, #eff.targets do
+			local target = eff.targets[i]
+			if target ~=self and not target.dead then
+				alive = true
+				break
+			end
+		end
+		if not alive then
+			self:removeEffect(self.EFF_TEMPORAL_FUGUE)
+		end
+	end,
+	activate = function(self, eff)
+		self:effectTemporaryValue(eff, "generic_damage_penalty", 66)
+	end,
+}
+
 newEffect{
 	name = "DRACONIC_WILL", image = "talents/draconic_will.png",
 	desc = "Draconic Will",
@@ -2604,3 +2631,25 @@ newEffect{
 		self:effectTemporaryValue(eff, "negative_status_effect_immune", 1)
 	end,
 }
+
+newEffect{
+	name = "PRESERVE_PATTERN", image = "talents/preserve_pattern.png",
+	desc = "Preserve Pattern",
+	long_desc = function(self, eff) return ("Damage received in the past is returned as %0.2f paradox damage per turn."):format(eff.paradox) end,
+	type = "other",
+	subtype = { time=true },
+	status = "detrimental",
+	parameters = { paradox=10 },
+	on_gain = function(self, err) return "#Target# converts damage into paradox.", "+Preserve" end,
+	on_lose = function(self, err) return "#Target# stops converting damage to paradox..", "-Preserve" end,
+	on_merge = function(self, old_eff, new_eff)
+		-- Merge the flames!
+		local oldparadox = old_eff.paradox * old_eff.dur
+		local newparadox = new_eff.paradox * new_eff.dur
+		new_eff.paradox = (oldparadox + newparadox) / new_eff.dur
+		return new_eff
+	end,
+	on_timeout = function(self, eff)
+		self:incParadox(eff.paradox)
+	end,
+}
\ No newline at end of file
diff --git a/game/modules/tome/data/timed_effects/physical.lua b/game/modules/tome/data/timed_effects/physical.lua
index 62518ff3272f245b018aa62c8f4ec8afb5d8c4da..ea966eb9b2293b2f0a5f5a76b9734956635f8eed 100644
--- a/game/modules/tome/data/timed_effects/physical.lua
+++ b/game/modules/tome/data/timed_effects/physical.lua
@@ -2825,30 +2825,68 @@ newEffect {
 newEffect{
 	name = "CELERITY", image = "talents/celerity.png",
 	desc = "Celerity",
-	long_desc = function(self, eff) return ("The target is moving is %d%% faster."):format(eff.speed * 100 * eff.stack) end,
+	long_desc = function(self, eff) return ("The target is moving is %d%% faster."):format(eff.speed * 100 * eff.charges) end,
 	type = "physical",
-	display_desc = function(self, eff) return eff.stack.." Celerity" end,
-	charges = function(self, eff) return eff.stack end,
+	display_desc = function(self, eff) return eff.charges.." Celerity" end,
+	charges = function(self, eff) return eff.charges end,
 	subtype = { speed=true, temporal=true },
 	status = "beneficial",
-	parameters = {speed=0.1, stack=1, max_stack=3},
+	parameters = {speed=0.1, charges=1, max_charges=3},
 	on_merge = function(self, old_eff, new_eff)
 		-- remove the old value
 		self:removeTemporaryValue("movement_speed", old_eff.tmpid)
 		
 		-- add a charge
-		old_eff.stack = math.min(old_eff.stack + 1, new_eff.max_stack)
+		old_eff.charges = math.min(old_eff.charges + 1, new_eff.max_charges)
 		
 		-- and apply the current values	
-		old_eff.tmpid = self:addTemporaryValue("movement_speed", old_eff.speed * old_eff.stack)
+		old_eff.tmpid = self:addTemporaryValue("movement_speed", old_eff.speed * old_eff.charges)
 		
 		old_eff.dur = new_eff.dur
 		return old_eff
 	end,
 	activate = function(self, eff)
-		eff.tmpid = self:addTemporaryValue("movement_speed", eff.speed * eff.stack)
+		eff.tmpid = self:addTemporaryValue("movement_speed", eff.speed * eff.charges)
 	end,
 	deactivate = function(self, eff)
 		self:removeTemporaryValue("movement_speed", eff.tmpid)
 	end,
 }
+
+newEffect{
+	name = "GRAVITY_SLOW", image = "talents/gravity_well.png",
+	desc = "Gravity Slow",
+	long_desc = function(self, eff) return ("The target is caught in a gravity well, reducing movement speed by %d%%."):format(eff.slow* 100) end,
+	type = "physical",
+	subtype = { speed=true },
+	status = "detrimental",
+	parameters = { slow=0.15 },
+	on_gain = function(self, err) return "#Target# is slowed by gravity.", "+Gravity Slow" end,
+	on_lose = function(self, err) return "#Target# is free from the gravity well.", "-Gravity Slow" end,
+	activate = function(self, eff)
+		eff.slowid = self:addTemporaryValue("movement_speed", -eff.slow)
+	end,
+	deactivate = function(self, eff)
+		self:removeTemporaryValue("movement_speed", eff.slowid)
+	end,
+}
+
+newEffect{
+	name = "ANTI_GRAVITY", image = "talents/gravity_locus.png",
+	desc = "Anti-Gravity",
+	long_desc = function(self, eff) return ("Target is caught in an anti-gravity field, halving its knockback resistance."):format() end,
+	type = "physical",
+	subtype = { spacetime=true },
+	status = "detrimental",
+	on_gain = function(self, err) return nil, "+Anti-Gravity" end,
+	on_lose = function(self, err) return nil, "-Anti-Gravity" end,
+	on_merge = function(self, old_eff, new_eff)
+		old_eff.dur = new_eff.dur
+		return old_eff
+	end,
+	activate = function(self, eff)
+		if self:attr("knockback_immune") then
+			self:effectTemporaryValue(eff, "knockback_immune", -self:attr("knockback_immune") / 2)
+		end
+	end,
+}
\ No newline at end of file
diff --git a/game/modules/tome/data/zones/town-point-zero/npcs.lua b/game/modules/tome/data/zones/town-point-zero/npcs.lua
index 733eac9649b09aeeea31b3df3f24677043eb5433..2884c41c470455774a4c3d4d981d6502fc0557ac 100644
--- a/game/modules/tome/data/zones/town-point-zero/npcs.lua
+++ b/game/modules/tome/data/zones/town-point-zero/npcs.lua
@@ -188,7 +188,7 @@ newEntity{ base = "BASE_NPC_POINT_ZERO_TOWN", define_as = "ZEMEKKYS",
 		[Talents.T_GRAVITY_SPIKE]=5,
 		[Talents.T_REPULSION_FIELD]=5,
 		[Talents.T_GRAVITY_WELL]=5,
-		[Talents.T_ENERGY_DECOMPOSITION]=5,
+		[Talents.T_ENTROPY]=5,
 		[Talents.T_ENERGY_ABSORPTION]=5,
 		[Talents.T_REDUX]=5,
 		[Talents.T_TEMPORAL_FUGUE]=5,
diff --git a/game/modules/tome/data/zones/unhallowed-morass/npcs.lua b/game/modules/tome/data/zones/unhallowed-morass/npcs.lua
index 341a568b64404f07217d94f14d6ab4c6e7666a01..822c713a853fec88f713e2068a6ede1881b2fad3 100644
--- a/game/modules/tome/data/zones/unhallowed-morass/npcs.lua
+++ b/game/modules/tome/data/zones/unhallowed-morass/npcs.lua
@@ -33,7 +33,6 @@ newEntity{
 
 	autolevel = "spider",
 	ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=2, },
-	global_speed_base = 1.2,
 	stats = { str=10, dex=17, mag=3, con=7 },
 	combat = { dammod={dex=0.8} },
 	combat_armor = 1, combat_def = 1,
diff --git a/game/modules/tome/dialogs/DeathDialog.lua b/game/modules/tome/dialogs/DeathDialog.lua
index 6ee772a12dff7f34e4d6d9390619d15a0a9e0d60..d6b1c6cabaf8f3f10df924158368684ab19f7ff7 100644
--- a/game/modules/tome/dialogs/DeathDialog.lua
+++ b/game/modules/tome/dialogs/DeathDialog.lua
@@ -263,6 +263,10 @@ function _M:use(item)
 		self.actor:updateModdableTile()
 		self.actor:check("on_resurrect", "lichform")
 		game:saveGame()
+	elseif act == "threads" then
+		game:chronoRestore("see_threads_base", true)
+		game:onTickEnd(function()game.player:removeEffect(game.player.EFF_SEE_THREADS)end)
+		game:saveGame()
 	elseif act == "easy_mode" then
 		self:eidolonPlane()
 	elseif act == "skeleton" then
@@ -308,6 +312,11 @@ function _M:generateList()
 
 	if config.settings.cheat then list[#list+1] = {name="Resurrect by cheating", action="cheat"} end
 	if not self.actor.no_resurrect and allow_res then
+		if self.actor:hasEffect(self.actor.EFF_SEE_THREADS) and game._chronoworlds then
+			self:use{action="threads"}
+			self.dont_show =true
+			return
+		end
 		if self.actor:isTalentActive(self.actor.T_LICHFORM) then
 			self:use{action="lichform"}
 			self.dont_show = true
diff --git a/game/modules/tome/dialogs/talents/ChronomancyContingency.lua b/game/modules/tome/dialogs/talents/ChronomancyContingency.lua
index ebb807e82fb2f95f08fc973050d51b10afcf0050..cbe55e7965e5553388ae2a8f5e0fd5701e2a7aac 100644
--- a/game/modules/tome/dialogs/talents/ChronomancyContingency.lua
+++ b/game/modules/tome/dialogs/talents/ChronomancyContingency.lua
@@ -43,7 +43,7 @@ function _M:init(actor)
 	Dialog.init(self, "Contingency", game.w * 0.6, game.h * 0.8)
 
 	self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[
-You may select a spell for Contingency to automatically use when your life falls below 25%.  Only active spells that do not require a target may be chosen.
+You may select a spell for Contingency to automatically use when your life falls below a percentage.  Only active spells that do not require a target may be chosen.
 ]]}
 	self.c_desc = TextzoneList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, scrollbar=true, no_color_bleed=true}
 
@@ -104,7 +104,7 @@ function _M:generateList()
 
 	-- Generate lists of all talents by category
 	for j, t in pairs(self.actor.talents_def) do
-		if self.actor:knowTalent(t.id) and t.mode == "activated" and t.is_spell and not t.requires_target and not t.hide then
+		if self.actor:knowTalent(t.id) and t.mode == "activated" and t.is_spell and not t.requires_target and not t.hide and not t.fixed_cooldown then
 			local nodes = talents
 			local status = tstring{{"color", "LIGHT_GREEN"}, "Talents"}
 			
diff --git a/game/modules/tome/dialogs/talents/ChronomancyEmpower.lua b/game/modules/tome/dialogs/talents/ChronomancyEmpower.lua
new file mode 100644
index 0000000000000000000000000000000000000000..6ce95321905c62e1bc7409b3bc665e00f78beef0
--- /dev/null
+++ b/game/modules/tome/dialogs/talents/ChronomancyEmpower.lua
@@ -0,0 +1,133 @@
+-- TE4 - T-Engine 4
+-- Copyright (C) 2009 - 2014 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
+
+require "engine.class"
+local Dialog = require "engine.ui.Dialog"
+local TreeList = require "engine.ui.TreeList"
+local ListColumns = require "engine.ui.ListColumns"
+local Textzone = require "engine.ui.Textzone"
+local TextzoneList = require "engine.ui.TextzoneList"
+local Separator = require "engine.ui.Separator"
+
+module(..., package.seeall, class.inherit(Dialog))
+-- Could use better icons when available
+--local confirmMark = require("engine.Entity").new({image="ui/chat-icon.png"})
+--local autoMark = require("engine.Entity").new({image = "/ui/hotkeys/mainmenu.png"})
+
+-- generate talent status separately to enable quicker refresh of Dialog
+local function TalentStatus(who,t) 
+	local status = tstring{{"color", "LIGHT_GREEN"}, "Active"} 
+
+	return tostring(status) 
+end
+
+function _M:init(actor)
+	self.actor = actor
+	actor.hotkey = actor.hotkey or {}
+	Dialog.init(self, "Empower", game.w * 0.6, game.h * 0.8)
+
+	self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[
+You may select a chronomancy spell to Empower, increasing your effective spellpower when casting that spell.
+]]}
+	self.c_desc = TextzoneList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, scrollbar=true, no_color_bleed=true}
+
+	self:generateList()
+
+	local cols = {
+		{name="", width={40,"fixed"}, display_prop="char"},
+		{name="Talent", width=80, display_prop="name"},
+	}
+	self.c_list = TreeList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, all_clicks=true, scrollbar=true, columns=cols, tree=self.list, fct=function(item, sel, button) self:use(item, button) end, select=function(item, sel) self:select(item) end}
+	self.c_list.cur_col = 2
+
+	self:loadUI{
+		{left=0, top=0, ui=self.c_list},
+		{right=0, top=self.c_tut.h + 20, ui=self.c_desc},
+		{right=0, top=0, ui=self.c_tut},
+		{hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI()
+
+	self.key:addBinds{
+		EXIT = function() game:unregisterDialog(self) end,
+	}
+end
+
+function _M:on_register()
+	game:onTickEnd(function() self.key:unicodeInput(true) end)
+end
+
+function _M:select(item)
+	if item then
+		self.c_desc:switchItem(item, item.desc)
+		self.cur_item = item
+	end
+end
+
+function _M:use(item)
+	if not item or not item.talent then return end
+	self.actor:talentDialogReturn(item.talent)
+	game:unregisterDialog(self)
+end
+
+-- Display the player tile
+function _M:innerDisplay(x, y, nb_keyframes)
+	if self.cur_item and self.cur_item.entity then
+		self.cur_item.entity:toScreen(game.uiset.hotkeys_display_icons.tiles, x + self.iw - 64, y + self.iy + self.c_tut.h - 32 + 10, 64, 64)
+	end
+end
+
+function _M:generateList()
+	-- Makes up the list
+	local list = {}
+	local letter = 1
+
+	local talents = {}
+	local chars = {}
+
+	-- Generate lists of all talents by category
+	for j, t in pairs(self.actor.talents_def) do
+		if self.actor:knowTalent(t.id) and t.type[1]:find("^chronomancy/") and not t.type[1]:find("^chronomancy/spellbinding") and not t.hide and t.mode ~= "passive" then
+			local nodes = talents
+			local status = tstring{{"color", "LIGHT_GREEN"}, "Talents"}
+			
+			-- Pregenerate icon with the Tiles instance that allows images
+			if t.display_entity then t.display_entity:getMapObjects(game.uiset.hotkeys_display_icons.tiles, {}, 1) end
+
+			nodes[#nodes+1] = {
+				name=((t.display_entity and t.display_entity:getDisplayString() or "")..t.name):toTString(),
+				cname=t.name,
+				status=status,
+				entity=t.display_entity,
+				talent=t.id,
+				desc=self.actor:getTalentFullDescription(t),
+				color=function() return {0xFF, 0xFF, 0xFF} end
+			}
+		end
+	end
+	table.sort(talents, function(a,b) return a.cname < b.cname end)
+	for i, node in ipairs(talents) do node.char = self:makeKeyChar(letter) chars[node.char] = node letter = letter + 1 end
+
+	list = {
+		{ char='', name=('#{bold}#Choose a talent#{normal}#'):toTString(), status='', hotkey='', desc="All talents that can be used with Empower.", color=function() return colors.simple(colors.LIGHT_GREEN) end, nodes=talents, shown=true },
+		chars = chars,
+	}
+	self.list = list
+end
diff --git a/game/modules/tome/dialogs/talents/ChronomancyExtension.lua b/game/modules/tome/dialogs/talents/ChronomancyExtension.lua
new file mode 100644
index 0000000000000000000000000000000000000000..aaed588ea83c6d4c6096aa69ccc0bbb5b4f19bd9
--- /dev/null
+++ b/game/modules/tome/dialogs/talents/ChronomancyExtension.lua
@@ -0,0 +1,133 @@
+-- TE4 - T-Engine 4
+-- Copyright (C) 2009 - 2014 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
+
+require "engine.class"
+local Dialog = require "engine.ui.Dialog"
+local TreeList = require "engine.ui.TreeList"
+local ListColumns = require "engine.ui.ListColumns"
+local Textzone = require "engine.ui.Textzone"
+local TextzoneList = require "engine.ui.TextzoneList"
+local Separator = require "engine.ui.Separator"
+
+module(..., package.seeall, class.inherit(Dialog))
+-- Could use better icons when available
+--local confirmMark = require("engine.Entity").new({image="ui/chat-icon.png"})
+--local autoMark = require("engine.Entity").new({image = "/ui/hotkeys/mainmenu.png"})
+
+-- generate talent status separately to enable quicker refresh of Dialog
+local function TalentStatus(who,t) 
+	local status = tstring{{"color", "LIGHT_GREEN"}, "Active"} 
+
+	return tostring(status) 
+end
+
+function _M:init(actor)
+	self.actor = actor
+	actor.hotkey = actor.hotkey or {}
+	Dialog.init(self, "Extension", game.w * 0.6, game.h * 0.8)
+
+	self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[
+You may select a chronomancy spell to Extend, increasing the duration of that spell.
+]]}
+	self.c_desc = TextzoneList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, scrollbar=true, no_color_bleed=true}
+
+	self:generateList()
+
+	local cols = {
+		{name="", width={40,"fixed"}, display_prop="char"},
+		{name="Talent", width=80, display_prop="name"},
+	}
+	self.c_list = TreeList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, all_clicks=true, scrollbar=true, columns=cols, tree=self.list, fct=function(item, sel, button) self:use(item, button) end, select=function(item, sel) self:select(item) end}
+	self.c_list.cur_col = 2
+
+	self:loadUI{
+		{left=0, top=0, ui=self.c_list},
+		{right=0, top=self.c_tut.h + 20, ui=self.c_desc},
+		{right=0, top=0, ui=self.c_tut},
+		{hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI()
+
+	self.key:addBinds{
+		EXIT = function() game:unregisterDialog(self) end,
+	}
+end
+
+function _M:on_register()
+	game:onTickEnd(function() self.key:unicodeInput(true) end)
+end
+
+function _M:select(item)
+	if item then
+		self.c_desc:switchItem(item, item.desc)
+		self.cur_item = item
+	end
+end
+
+function _M:use(item)
+	if not item or not item.talent then return end
+	self.actor:talentDialogReturn(item.talent)
+	game:unregisterDialog(self)
+end
+
+-- Display the player tile
+function _M:innerDisplay(x, y, nb_keyframes)
+	if self.cur_item and self.cur_item.entity then
+		self.cur_item.entity:toScreen(game.uiset.hotkeys_display_icons.tiles, x + self.iw - 64, y + self.iy + self.c_tut.h - 32 + 10, 64, 64)
+	end
+end
+
+function _M:generateList()
+	-- Makes up the list
+	local list = {}
+	local letter = 1
+
+	local talents = {}
+	local chars = {}
+
+	-- Generate lists of all talents by category
+	for j, t in pairs(self.actor.talents_def) do
+		if self.actor:knowTalent(t.id) and t.type[1]:find("^chronomancy/") and not t.type[1]:find("^chronomancy/spellbinding") and not t.hide and t.getDuration and t.mode ~= "passive" then
+			local nodes = talents
+			local status = tstring{{"color", "LIGHT_GREEN"}, "Talents"}
+			
+			-- Pregenerate icon with the Tiles instance that allows images
+			if t.display_entity then t.display_entity:getMapObjects(game.uiset.hotkeys_display_icons.tiles, {}, 1) end
+
+			nodes[#nodes+1] = {
+				name=((t.display_entity and t.display_entity:getDisplayString() or "")..t.name):toTString(),
+				cname=t.name,
+				status=status,
+				entity=t.display_entity,
+				talent=t.id,
+				desc=self.actor:getTalentFullDescription(t),
+				color=function() return {0xFF, 0xFF, 0xFF} end
+			}
+		end
+	end
+	table.sort(talents, function(a,b) return a.cname < b.cname end)
+	for i, node in ipairs(talents) do node.char = self:makeKeyChar(letter) chars[node.char] = node letter = letter + 1 end
+
+	list = {
+		{ char='', name=('#{bold}#Choose a talent#{normal}#'):toTString(), status='', hotkey='', desc="All talents that can be used with Extension.", color=function() return colors.simple(colors.LIGHT_GREEN) end, nodes=talents, shown=true },
+		chars = chars,
+	}
+	self.list = list
+end
diff --git a/game/modules/tome/dialogs/talents/ChronomancyMatrix.lua b/game/modules/tome/dialogs/talents/ChronomancyMatrix.lua
new file mode 100644
index 0000000000000000000000000000000000000000..9a1d579cc5edb68d54e419068dc2735c87634446
--- /dev/null
+++ b/game/modules/tome/dialogs/talents/ChronomancyMatrix.lua
@@ -0,0 +1,133 @@
+-- TE4 - T-Engine 4
+-- Copyright (C) 2009 - 2014 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
+
+require "engine.class"
+local Dialog = require "engine.ui.Dialog"
+local TreeList = require "engine.ui.TreeList"
+local ListColumns = require "engine.ui.ListColumns"
+local Textzone = require "engine.ui.Textzone"
+local TextzoneList = require "engine.ui.TextzoneList"
+local Separator = require "engine.ui.Separator"
+
+module(..., package.seeall, class.inherit(Dialog))
+-- Could use better icons when available
+--local confirmMark = require("engine.Entity").new({image="ui/chat-icon.png"})
+--local autoMark = require("engine.Entity").new({image = "/ui/hotkeys/mainmenu.png"})
+
+-- generate talent status separately to enable quicker refresh of Dialog
+local function TalentStatus(who,t) 
+	local status = tstring{{"color", "LIGHT_GREEN"}, "Active"} 
+
+	return tostring(status) 
+end
+
+function _M:init(actor)
+	self.actor = actor
+	actor.hotkey = actor.hotkey or {}
+	Dialog.init(self, "Matrix", game.w * 0.6, game.h * 0.8)
+
+	self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[
+You may select a chronomancy spell to Matrix, reducing the cooldown of that spell.
+]]}
+	self.c_desc = TextzoneList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, scrollbar=true, no_color_bleed=true}
+
+	self:generateList()
+
+	local cols = {
+		{name="", width={40,"fixed"}, display_prop="char"},
+		{name="Talent", width=80, display_prop="name"},
+	}
+	self.c_list = TreeList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, all_clicks=true, scrollbar=true, columns=cols, tree=self.list, fct=function(item, sel, button) self:use(item, button) end, select=function(item, sel) self:select(item) end}
+	self.c_list.cur_col = 2
+
+	self:loadUI{
+		{left=0, top=0, ui=self.c_list},
+		{right=0, top=self.c_tut.h + 20, ui=self.c_desc},
+		{right=0, top=0, ui=self.c_tut},
+		{hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI()
+
+	self.key:addBinds{
+		EXIT = function() game:unregisterDialog(self) end,
+	}
+end
+
+function _M:on_register()
+	game:onTickEnd(function() self.key:unicodeInput(true) end)
+end
+
+function _M:select(item)
+	if item then
+		self.c_desc:switchItem(item, item.desc)
+		self.cur_item = item
+	end
+end
+
+function _M:use(item)
+	if not item or not item.talent then return end
+	self.actor:talentDialogReturn(item.talent)
+	game:unregisterDialog(self)
+end
+
+-- Display the player tile
+function _M:innerDisplay(x, y, nb_keyframes)
+	if self.cur_item and self.cur_item.entity then
+		self.cur_item.entity:toScreen(game.uiset.hotkeys_display_icons.tiles, x + self.iw - 64, y + self.iy + self.c_tut.h - 32 + 10, 64, 64)
+	end
+end
+
+function _M:generateList()
+	-- Makes up the list
+	local list = {}
+	local letter = 1
+
+	local talents = {}
+	local chars = {}
+
+	-- Generate lists of all talents by category
+	for j, t in pairs(self.actor.talents_def) do
+		if self.actor:knowTalent(t.id) and t.type[1]:find("^chronomancy/") and not t.type[1]:find("^chronomancy/spellbinding") and not t.hide and t.cooldown and t.mode ~= "passive" and not t.fixed_cooldown then
+			local nodes = talents
+			local status = tstring{{"color", "LIGHT_GREEN"}, "Talents"}
+			
+			-- Pregenerate icon with the Tiles instance that allows images
+			if t.display_entity then t.display_entity:getMapObjects(game.uiset.hotkeys_display_icons.tiles, {}, 1) end
+
+			nodes[#nodes+1] = {
+				name=((t.display_entity and t.display_entity:getDisplayString() or "")..t.name):toTString(),
+				cname=t.name,
+				status=status,
+				entity=t.display_entity,
+				talent=t.id,
+				desc=self.actor:getTalentFullDescription(t),
+				color=function() return {0xFF, 0xFF, 0xFF} end
+			}
+		end
+	end
+	table.sort(talents, function(a,b) return a.cname < b.cname end)
+	for i, node in ipairs(talents) do node.char = self:makeKeyChar(letter) chars[node.char] = node letter = letter + 1 end
+
+	list = {
+		{ char='', name=('#{bold}#Choose a talent#{normal}#'):toTString(), status='', hotkey='', desc="All talents that can be used with Matrix.", color=function() return colors.simple(colors.LIGHT_GREEN) end, nodes=talents, shown=true },
+		chars = chars,
+	}
+	self.list = list
+end
diff --git a/game/modules/tome/dialogs/talents/ChronomancyQuicken.lua b/game/modules/tome/dialogs/talents/ChronomancyQuicken.lua
new file mode 100644
index 0000000000000000000000000000000000000000..a0b19f6f782ea889b5cb893d7a7a1330c565cda3
--- /dev/null
+++ b/game/modules/tome/dialogs/talents/ChronomancyQuicken.lua
@@ -0,0 +1,133 @@
+-- TE4 - T-Engine 4
+-- Copyright (C) 2009 - 2014 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
+
+require "engine.class"
+local Dialog = require "engine.ui.Dialog"
+local TreeList = require "engine.ui.TreeList"
+local ListColumns = require "engine.ui.ListColumns"
+local Textzone = require "engine.ui.Textzone"
+local TextzoneList = require "engine.ui.TextzoneList"
+local Separator = require "engine.ui.Separator"
+
+module(..., package.seeall, class.inherit(Dialog))
+-- Could use better icons when available
+--local confirmMark = require("engine.Entity").new({image="ui/chat-icon.png"})
+--local autoMark = require("engine.Entity").new({image = "/ui/hotkeys/mainmenu.png"})
+
+-- generate talent status separately to enable quicker refresh of Dialog
+local function TalentStatus(who,t) 
+	local status = tstring{{"color", "LIGHT_GREEN"}, "Active"} 
+
+	return tostring(status) 
+end
+
+function _M:init(actor)
+	self.actor = actor
+	actor.hotkey = actor.hotkey or {}
+	Dialog.init(self, "Quicken", game.w * 0.6, game.h * 0.8)
+
+	self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[
+You may select a chronomancy spell to Quicken, reducing the time it takes you to cast that spell.
+]]}
+	self.c_desc = TextzoneList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, scrollbar=true, no_color_bleed=true}
+
+	self:generateList()
+
+	local cols = {
+		{name="", width={40,"fixed"}, display_prop="char"},
+		{name="Talent", width=80, display_prop="name"},
+	}
+	self.c_list = TreeList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, all_clicks=true, scrollbar=true, columns=cols, tree=self.list, fct=function(item, sel, button) self:use(item, button) end, select=function(item, sel) self:select(item) end}
+	self.c_list.cur_col = 2
+
+	self:loadUI{
+		{left=0, top=0, ui=self.c_list},
+		{right=0, top=self.c_tut.h + 20, ui=self.c_desc},
+		{right=0, top=0, ui=self.c_tut},
+		{hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}},
+	}
+	self:setFocus(self.c_list)
+	self:setupUI()
+
+	self.key:addBinds{
+		EXIT = function() game:unregisterDialog(self) end,
+	}
+end
+
+function _M:on_register()
+	game:onTickEnd(function() self.key:unicodeInput(true) end)
+end
+
+function _M:select(item)
+	if item then
+		self.c_desc:switchItem(item, item.desc)
+		self.cur_item = item
+	end
+end
+
+function _M:use(item)
+	if not item or not item.talent then return end
+	self.actor:talentDialogReturn(item.talent)
+	game:unregisterDialog(self)
+end
+
+-- Display the player tile
+function _M:innerDisplay(x, y, nb_keyframes)
+	if self.cur_item and self.cur_item.entity then
+		self.cur_item.entity:toScreen(game.uiset.hotkeys_display_icons.tiles, x + self.iw - 64, y + self.iy + self.c_tut.h - 32 + 10, 64, 64)
+	end
+end
+
+function _M:generateList()
+	-- Makes up the list
+	local list = {}
+	local letter = 1
+
+	local talents = {}
+	local chars = {}
+
+	-- Generate lists of all talents by category
+	for j, t in pairs(self.actor.talents_def) do
+		if self.actor:knowTalent(t.id) and t.type[1]:find("^chronomancy/") and not t.type[1]:find("^chronomancy/spellbinding") and not t.hide and not t.no_energy and t.mode ~= "passive" then
+			local nodes = talents
+			local status = tstring{{"color", "LIGHT_GREEN"}, "Talents"}
+			
+			-- Pregenerate icon with the Tiles instance that allows images
+			if t.display_entity then t.display_entity:getMapObjects(game.uiset.hotkeys_display_icons.tiles, {}, 1) end
+
+			nodes[#nodes+1] = {
+				name=((t.display_entity and t.display_entity:getDisplayString() or "")..t.name):toTString(),
+				cname=t.name,
+				status=status,
+				entity=t.display_entity,
+				talent=t.id,
+				desc=self.actor:getTalentFullDescription(t),
+				color=function() return {0xFF, 0xFF, 0xFF} end
+			}
+		end
+	end
+	table.sort(talents, function(a,b) return a.cname < b.cname end)
+	for i, node in ipairs(talents) do node.char = self:makeKeyChar(letter) chars[node.char] = node letter = letter + 1 end
+
+	list = {
+		{ char='', name=('#{bold}#Choose a talent#{normal}#'):toTString(), status='', hotkey='', desc="All talents that can be used with Quicken.", color=function() return colors.simple(colors.LIGHT_GREEN) end, nodes=talents, shown=true },
+		chars = chars,
+	}
+	self.list = list
+end