diff --git a/game/engines/default/engine/Target.lua b/game/engines/default/engine/Target.lua
index 56a5076ef33e37a5432ad06f1c64b7a6eb4b9fa3..f6175fcb72f75ef7f84a8aeffbc1442da816fe0d 100644
--- a/game/engines/default/engine/Target.lua
+++ b/game/engines/default/engine/Target.lua
@@ -53,6 +53,10 @@ function _M:init(map, source_actor)
 end
 
 function _M:display(dispx, dispy)
+	-- Make sure we have a source
+	if not self.target_type.source_actor then
+		self.target_type.source_actor = self.source_actor
+	end
 	-- Entity tracking, if possible and if visible
 	if self.target.entity and self.target.entity.x and self.target.entity.y and game.level.map.seens(self.target.entity.x, self.target.entity.y) then
 		self.target.x, self.target.y = self.target.entity.x, self.target.entity.y
@@ -71,67 +75,92 @@ function _M:display(dispx, dispy)
 	local lx, ly = l()
 	local initial_dir = lx and coord_to_dir[lx - self.source_actor.x][ly - self.source_actor.y] or 5
 	local stopx, stopy = self.source_actor.x, self.source_actor.y
+	local stop_radius_x, stop_radius_y = stopx, stopy
 	while lx and ly do
-		if s == self.sb then stopx, stopy = lx, ly end
-		if not self.target_type.no_restrict then
-			if not game.level.map.seens(lx, ly) then s = self.sr end
-			if self.target_type.stop_block and game.level.map:checkAllEntities(lx, ly, "block_move") then s = self.sr
-			elseif game.level.map:checkEntity(lx, ly, Map.TERRAIN, "block_move") then s = self.sr end
+		if s == self.sb then
+			stop_radius_x, stop_radius_y = stopx, stopy
+			stopx, stopy = lx, ly
 		end
-		if self.target_type.range and math.sqrt((self.source_actor.x-lx)^2 + (self.source_actor.y-ly)^2) > self.target_type.range then s = self.sr end
+		if self.target_type.block_path and self.target_type:block_path(lx, ly) then s = self.sr end
 		s:toScreen(self.display_x + (lx - game.level.map.mx) * self.tile_w * Map.zoom, self.display_y + (ly - game.level.map.my) * self.tile_h * Map.zoom, self.tile_w * Map.zoom, self.tile_h * Map.zoom)
 		lx, ly = l()
 	end
 	self.cursor:toScreen(self.display_x + (self.target.x - game.level.map.mx) * self.tile_w * Map.zoom, self.display_y + (self.target.y - game.level.map.my) * self.tile_h * Map.zoom, self.tile_w * Map.zoom, self.tile_h * Map.zoom)
 
-	if s == self.b then stopx, stopy = self.target.x, self.target.y end
+	-- If we reached the end without mishap then both the beam and radius effect should stop here
+	if s == self.sb then
+		stopx, stopy = self.target.x, self.target.y
+		stop_radius_x, stop_radius_y = stopx, stopy
+	end
 
 	if self.target_type.ball and self.target_type.ball > 0 then
-		core.fov.calc_circle(stopx, stopy, self.target_type.ball, function(_, lx, ly)
-			self.sg:toScreen(self.display_x + (lx - game.level.map.mx) * self.tile_w * Map.zoom, self.display_y + (ly - game.level.map.my) * self.tile_h * Map.zoom, self.tile_w * Map.zoom, self.tile_h * Map.zoom)
-			if not self.target_type.no_restrict and game.level.map:checkEntity(lx, ly, Map.TERRAIN, "block_move") then return true end
+		core.fov.calc_circle(stop_radius_x, stop_radius_y, self.target_type.ball, function(_, px, py)
+			self.sg:toScreen(self.display_x + (px - game.level.map.mx) * self.tile_w * Map.zoom, self.display_y + (py - game.level.map.my) * self.tile_h * Map.zoom, self.tile_w * Map.zoom, self.tile_h * Map.zoom)
+			if self.target_type.block_radius and self.target_type:block_radius(px, py) then return true end
 		end, function()end, nil)
 	elseif self.target_type.cone and self.target_type.cone > 0 then
-		core.fov.calc_beam(stopx, stopy, self.target_type.cone, initial_dir, self.target_type.cone_angle, function(_, lx, ly)
-			self.sg:toScreen(self.display_x + (lx - game.level.map.mx) * self.tile_w * Map.zoom, self.display_y + (ly - game.level.map.my) * self.tile_h * Map.zoom, self.tile_w * Map.zoom, self.tile_h * Map.zoom)
-			if not self.target_type.no_restrict and game.level.map:checkEntity(lx, ly, Map.TERRAIN, "block_move") then return true end
+		core.fov.calc_beam(stop_radius_x, stop_radius_y, self.target_type.cone, initial_dir, self.target_type.cone_angle, function(_, px, py)
+			self.sg:toScreen(self.display_x + (px - game.level.map.mx) * self.tile_w * Map.zoom, self.display_y + (py - game.level.map.my) * self.tile_h * Map.zoom, self.tile_w * Map.zoom, self.tile_h * Map.zoom)
+			if self.target_type.block_radius and self.target_type:block_radius(px, py) then return true end
 		end, function()end, nil)
 	end
 
 	self.display_x, self.display_y = ox, oy
 end
 
---- Returns data for the given target type
+-- @return t The target table used by ActorProject, Projectile, GameTargeting, etc.
+-- @param t Target table used to generate the 
+-- @param t.type The engine-defined type, populates other more complex variables (see below)
 -- Hit: simple project in LOS<br/>
 -- Beam: hits everything in LOS<br/>
 -- Bolt: hits first thing in path<br/>
 -- Ball: hits everything in a ball aounrd the target<br/>
 -- Cone: hits everything in a cone in the direction<br/>
+-- @param t.radius The radius of the ball/cone AoE
+-- @param t.cone_angle The angle for the cone AoE (default 55°)
+-- @param t.no_restrict Boolean that removes all restrictions in the t.type defined block functions.
+-- @param t.stop_block Boolean that stops the target on the first tile that has an entity that blocks move.
+-- @param t.range The range the target can be from the origin.
+-- @param t.pass_terrain Boolean that allows the target to pass through terrain to remembered tiles on the other side.
+-- @param t.block_path(typ, lx, ly) Function called on each tile to determine if the targeting is blocked.  Automatically set when using t.typ, but the user can provide their own if they know what they are doing.
+-- @param t.block_radius(typ, lx, ly) Function called on each tile when projecting the radius to determine if the radius projection is blocked.  Automatically set when using t.typ, but the user can provide their own if they know what they are doing.
 function _M:getType(t)
-	if not t or not t.type then return {} end
-	t.range = t.range or 20
-	if t.friendlyfire == nil then t.friendlyfire = true end
-	if t.type == "hit" then
-		return {range=t.range, friendlyfire=t.friendlyfire, no_restrict=t.no_restrict}
-	elseif t.type == "beam" then
-		return {range=t.range, friendlyfire=t.friendlyfire, no_restrict=t.no_restrict, line=true}
-	elseif t.type == "bolt" then
-		return {range=t.range, friendlyfire=t.friendlyfire, no_restrict=t.no_restrict, stop_block=true}
-	elseif t.type == "ball" then
-		return {range=t.range, friendlyfire=t.friendlyfire, no_restrict=t.no_restrict, ball=t.radius}
-	elseif t.type == "cone" then
-		return {range=t.range, friendlyfire=t.friendlyfire, no_restrict=t.no_restrict, cone=t.radius, cone_angle=t.cone_angle or 55}
-	elseif t.type == "ballbolt" then
-		return {range=t.range, friendlyfire=t.friendlyfire, no_restrict=t.no_restrict, ball=t.radius, stop_block=true}
-	elseif t.type == "conebolt" then
-		return {range=t.range, friendlyfire=t.friendlyfire, no_restrict=t.no_restrict, cone=t.radius, cone_angle=t.cone_angle or 55, stop_block=true}
-	elseif t.type == "ballbeam" then
-		return {range=t.range, friendlyfire=t.friendlyfire, no_restrict=t.no_restrict, ball=t.radius, line=true}
-	elseif t.type == "conebeam" then
-		return {range=t.range, friendlyfire=t.friendlyfire, no_restrict=t.no_restrict, cone=t.radius, cone_angle=t.cone_angle or 55, line=true}
-	else
-		return {}
+	if not t then return {} end
+	-- Add the default values
+	t = table.clone(t)
+	target_type = {
+		range=20,
+		friendlyfire=true,
+		block_path = function(typ, lx, ly)
+			if not typ.no_restrict then
+				if not game.level.map.remembers(lx, ly) then return true end
+				if typ.stop_block and game.level.map:checkAllEntities(lx, ly, "block_move") then return true
+				elseif not typ.pass_terrain and game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") then return true end
+				if typ.range and typ.source_actor and math.sqrt((typ.source_actor.x-lx)^2 + (typ.source_actor.y-ly)^2) > typ.range then return true end
+			end
+		end,
+		block_radius=function(typ, lx, ly)
+			return not typ.no_restrict and game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move")
+		end
+	}
+	table.update(t, target_type)
+	-- And now modify for the default types
+	if t.type then
+		if t.type:find("ball") then
+			target_type.ball = t.radius
+		end
+		if t.type:find("cone") then
+			target_type.cone = t.radius
+			target_type.cone_angle = t.cone_angle or 55
+		end
+		if t.type:find("bolt") then
+			target_type.stop_block = true
+		elseif t.type:find("beam") then
+			target_type.line = true
+		end
 	end
+	table.update(t, target_type)
+	return t
 end
 
 function _M:setActive(v, type)
diff --git a/game/engines/default/engine/interface/ActorProject.lua b/game/engines/default/engine/interface/ActorProject.lua
index 7df788286aba9df175ef190cedf1fda32677c73b..db3eb477ecc548f6056cd17e23514ed05af7aa0c 100644
--- a/game/engines/default/engine/interface/ActorProject.lua
+++ b/game/engines/default/engine/interface/ActorProject.lua
@@ -45,6 +45,7 @@ function _M:project(t, x, y, damtype, dam, particles)
 
 --	if type(dam) == "number" and dam < 0 then return end
 	local typ = Target:getType(t)
+	typ.source_actor = self
 
 	local grids = {}
 	local function addGrid(x, y)
@@ -59,13 +60,11 @@ function _M:project(t, x, y, damtype, dam, particles)
 	local l = line.new(srcx, srcy, x, y)
 	lx, ly = l()
 	local initial_dir = lx and coord_to_dir[lx - srcx][ly - srcy] or 5
+	local stop_radius_x, stop_radius_y = lx, ly
 	while lx and ly do
-		if not typ.no_restrict then
-			if typ.stop_block and game.level.map:checkAllEntities(lx, ly, "block_move") then break
-			elseif game.level.map:checkEntity(lx, ly, Map.TERRAIN, "block_move") then break end
-			if typ.range and math.sqrt((srcx-lx)^2 + (srcy-ly)^2) > typ.range then break end
-		end
-
+		if typ.block_path and typ:block_path(lx, ly) then break end
+		stop_radius_x, stop_radius_y = lx, ly
+		
 		-- Deam damage: beam
 		if typ.line then addGrid(lx, ly) end
 
@@ -75,17 +74,17 @@ function _M:project(t, x, y, damtype, dam, particles)
 	if not lx and not ly then lx, ly = x, y end
 
 	if typ.ball and typ.ball > 0 then
-		core.fov.calc_circle(lx, ly, typ.ball, function(_, px, py)
+		core.fov.calc_circle(stop_radius_x, stop_radius_y, typ.ball, function(_, px, py)
 			-- Deal damage: ball
 			addGrid(px, py)
-			if not typ.no_restrict and game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") then return true end
+			if typ.block_radius and typ:block_radius(px, py) then return true end
 		end, function()end, nil)
 		addGrid(lx, ly)
 	elseif typ.cone and typ.cone > 0 then
-		core.fov.calc_beam(lx, ly, typ.cone, initial_dir, typ.cone_angle, function(_, px, py)
+		core.fov.calc_beam(stop_radius_x, stop_radius_y, typ.cone, initial_dir, typ.cone_angle, function(_, px, py)
 			-- Deal damage: cone
 			addGrid(px, py)
-			if not typ.no_restrict and game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") then return true end
+			if typ.block_radius and typ:block_radius(px, py) then return true end
 		end, function()end, nil)
 		addGrid(lx, ly)
 	else
@@ -138,17 +137,14 @@ end
 -- @param y target coords
 function _M:canProject(t, x, y)
 	local typ = Target:getType(t)
+	typ.source_actor = self
 
 	-- Stop at range or on block
 	local lx, ly = x, y
 	local l = line.new(self.x, self.y, x, y)
 	lx, ly = l()
 	while lx and ly do
-		if not typ.no_restrict then
-			if typ.stop_block and game.level.map:checkAllEntities(lx, ly, "block_move") then break
-			elseif game.level.map:checkEntity(lx, ly, Map.TERRAIN, "block_move") then break end
-			if typ.range and math.sqrt((self.x-lx)^2 + (self.y-ly)^2) > typ.range then break end
-		end
+		if typ.block_path and typ:block_path(lx, ly) then break end
 
 		lx, ly = l()
 	end
@@ -178,6 +174,7 @@ function _M:projectile(t, x, y, damtype, dam, particles)
 
 --	if type(dam) == "number" and dam < 0 then return end
 	local typ = Target:getType(t)
+	typ.source_actor = self
 
 	local proj = require(self.projectile_class):makeProject(self, t.display, {x=x, y=y, start_x = t.x or self.x, start_y = t.y or self.y, damtype=damtype, tg=t, typ=typ, dam=dam, particles=particles})
 	game.zone:addEntity(game.level, proj, "projectile", self.x, self.y)
@@ -193,11 +190,7 @@ function _M:projectDoMove(typ, tgtx, tgty, x, y, srcx, srcy)
 	if lx and ly then lx, ly = l() end
 
 	if lx and ly then
-		if not typ.no_restrict then
-			if typ.stop_block and game.level.map:checkAllEntities(lx, ly, "block_move") then return lx, ly, false, true
-			elseif game.level.map:checkEntity(lx, ly, Map.TERRAIN, "block_move") then return lx, ly, false, true end
-			if typ.range and math.sqrt((srcx-lx)^2 + (srcy-ly)^2) > typ.range then return lx, ly, false, true end
-		end
+		if typ.block_path and typ:block_path(lx, ly) then return lx, ly, false, true end
 
 		-- End of the map
 		if lx < 0 or lx >= game.level.map.w or ly < 0 or ly >= game.level.map.h then return lx, ly, false, true end
@@ -251,7 +244,7 @@ function _M:projectDoStop(typ, tg, damtype, dam, particles, lx, ly, tmp)
 		core.fov.calc_circle(lx, ly, typ.ball, function(_, px, py)
 			-- Deal damage: ball
 			addGrid(px, py)
-			if not typ.no_restrict and game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") then return true end
+			if typ.block_radius and typ:block_radius(lx, ly) then return true end
 		end, function()end, nil)
 		addGrid(lx, ly)
 	elseif typ.cone and typ.cone > 0 then
@@ -259,7 +252,7 @@ function _M:projectDoStop(typ, tg, damtype, dam, particles, lx, ly, tmp)
 		core.fov.calc_beam(lx, ly, typ.cone, initial_dir, typ.cone_angle, function(_, px, py)
 			-- Deal damage: cone
 			addGrid(px, py)
-			if not typ.no_restrict and game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") then return true end
+			if typ.block_radius and typ:block_radius(lx, ly) then return true end
 		end, function()end, nil)
 		addGrid(lx, ly)
 	else
diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua
index 599b0db21342928a853676906f9e4a3fd7529f35..852978ea328c7eadc8dbca8bc0e7bc3eba2bbe3c 100644
--- a/game/engines/default/engine/utils.lua
+++ b/game/engines/default/engine/utils.lua
@@ -101,12 +101,25 @@ function table.reverse(t)
 	for i, e in ipairs(t) do tt[e] = i end
 	return tt
 end
+
 function table.listify(t)
 	local tt = {}
 	for k, e in pairs(t) do tt[#tt+1] = {k, e} end
 	return tt
 end
 
+function table.update(dst, src, deep)
+	for k, e in pairs(src) do
+		if deep and dst[k] and type(e) == "table" and type(dst[k]) == "table" and not e.__CLASSNAME then
+			table.update(dst[k], e, true)
+		elseif deep and not dst[k] and type(e) == "table" and not e.__CLASSNAME then
+			dst[k] = table.clone(e, true)
+		elseif not dst[k] then
+			dst[k] = e
+		end
+	end
+end
+
 function string.ordinal(number)
 	local suffix = "th"
 	number = tonumber(number)
diff --git a/game/modules/tome/data/talents/spells/conveyance.lua b/game/modules/tome/data/talents/spells/conveyance.lua
index 1304d01171b1037b05c71d209e10e3dbeb0cfdc5..9c0fc021762b3537cded44489fe91506b591df15 100644
--- a/game/modules/tome/data/talents/spells/conveyance.lua
+++ b/game/modules/tome/data/talents/spells/conveyance.lua
@@ -54,7 +54,7 @@ newTalent{
 
 		local x, y = self.x, self.y
 		if self:getTalentLevel(t) >= 4 then
-			local tg = {type="ball", nolock=true, no_restrict=true, nowarning=true, range=10 + self:combatSpellpower(0.1), radius=7 - self:getTalentLevel(t)}
+			local tg = {type="ball", nolock=true, pass_terrain=true, nowarning=true, range=10 + self:combatSpellpower(0.1), radius=7 - self:getTalentLevel(t)}
 			x, y = self:getTarget(tg)
 			if not x then return nil end
 			-- Target code doesnot restrict the target coordinates to the range, it lets the poject function do it
@@ -123,7 +123,7 @@ newTalent{
 
 		local x, y = self.x, self.y
 		if self:getTalentLevel(t) >= 4 then
-			local tg = {type="ball", nolock=true, no_restrict=true, nowarning=true, range=100 + self:combatSpellpower(1), radius=20 - self:getTalentLevel(t)}
+			local tg = {type="ball", nolock=true, pass_terrain=true, nowarning=true, range=100 + self:combatSpellpower(1), radius=20 - self:getTalentLevel(t)}
 			x, y = self:getTarget(tg)
 			if not x then return nil end
 			-- Target code doesnot restrict the target coordinates to the range, it lets the poject function do it