diff --git a/game/engines/default/engine/Actor.lua b/game/engines/default/engine/Actor.lua
index 6a0d91b1de419fecac20fabf8c0ee7e859726239..a59f563d7e77604b285d70a8a3b6dab03715e3e1 100644
--- a/game/engines/default/engine/Actor.lua
+++ b/game/engines/default/engine/Actor.lua
@@ -268,7 +268,7 @@ end
 function _M:canMove(x, y, terrain_only)
 	if not game.level.map:isBound(x, y) then return false end
 	if terrain_only then
-		return not game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move")
+		return not game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move", self)
 	else
 		return not game.level.map:checkAllEntities(x, y, "block_move", self)
 	end
diff --git a/game/engines/default/engine/Target.lua b/game/engines/default/engine/Target.lua
index dbcfea6e908b17b6f90281cc333ec1f93c142b4c..31b630d3eeb20857b62e6f5b4c15166d212a9691 100644
--- a/game/engines/default/engine/Target.lua
+++ b/game/engines/default/engine/Target.lua
@@ -343,7 +343,7 @@ function _M:realDisplay(dispx, dispy, display_highlight)
 			self.target_type.display_update_min_range(self, d)
 		end
 
-		self.target_type.display_line_step(self, d)
+		if self.target_type.display_line_step then self.target_type.display_line_step(self, d) end
 
 		if d.block then self.target_type.display_on_block(self, d) end
 
@@ -429,8 +429,18 @@ function _M:realDisplay(dispx, dispy, display_highlight)
 	self:triggerHook(d)
 end
 
+--- Determine if a grid blocks projection along a path based on targeting table parameters
+-- @see Target:getType(t) below
+-- @param typ = updated targeting table (from Target:getType)
+-- @param lx, ly = grid coordinates
+-- @param for_highlights [type=boolean] highlight grids for player targeting mode
+-- @return[1] [type=boolean] grid blocks the projection
+-- @return[2] [type=boolean] grid may be hit by the projection
+-- @return[2] "unknown" (with for_highlights) if the grid is unknown
+-- @return[3] [type=boolean] grid blocks the projection, path around not allowed (corner blocked)
 _M.defaults.block_path = function(typ, lx, ly, for_highlights)
-	if not game.level.map:isBound(lx, ly) then
+	local map = game.level.map
+	if not map:isBound(lx, ly) then
 		return true, false, false
 	elseif not typ.no_restrict then
 		if typ.range and typ.start_x then
@@ -440,37 +450,51 @@ _M.defaults.block_path = function(typ, lx, ly, for_highlights)
 			local dist = core.fov.distance(typ.source_actor.x, typ.source_actor.y, lx, ly)
 			if dist > typ.range then return true, false, false end
 		end
-		local is_known = game.level.map.remembers(lx, ly) or game.level.map.seens(lx, ly)
+		local is_known = map.remembers(lx, ly) or map.seens(lx, ly)
 		if typ.requires_knowledge and not is_known then
 			return true, false, false
 		end
-		if not typ.pass_terrain and game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") and not game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "pass_projectile") then
-			if for_highlights and not is_known then
-				return false, "unknown", true
-			else
-				return true, true, false
+		local trn_block, trn_pass
+		if not typ.pass_terrain then -- check terrain
+			trn_block = map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") or false
+			if trn_block then 
+				trn_pass = map:checkEntity(lx, ly, engine.Map.TERRAIN, "pass_projectile") or false
+				if not trn_pass then -- blocked by terrain
+					if for_highlights and not is_known then
+						return false, "unknown", true
+					else
+						return true, true, false
+					end
+				end
 			end
-		-- If we explode due to something other than terrain, then we should explode ON the tile, not before it
-		elseif typ.stop_block then
-			local nb = game.level.map:checkAllEntitiesCount(lx, ly, "block_move")
-			-- Reduce for pass_projectile or pass_terrain, which was handled above
-			if game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") and (typ.pass_terrain or game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "pass_projectile")) then
+		end
+		-- If the projection is blocked by something other than terrain, the grid should be hit
+		if typ.stop_block then -- check all entities
+			 -- get #blocking entities and subtract for each entity that can be explicitly passed through
+			local nb = map:checkAllEntitiesCount(lx, ly, "block_move")
+			if nb > 0 then -- decrement for passable terrain
+				if trn_block == nil then trn_block = map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") end
+				if trn_block and (typ.pass_terrain or (trn_pass == nil and map:checkEntity(lx, ly, engine.Map.TERRAIN, "pass_projectile") or trn_pass)) then
 				nb = nb - 1
+				end
 			end
-			-- Reduce the nb blocking for friendlies
-			if not typ.friendlyblock and typ.source_actor and typ.source_actor.reactionToward then
-				local a = game.level.map(lx, ly, engine.Map.ACTOR)
-				if a and typ.source_actor:reactionToward(a) >= 0 then
-					nb = nb - 1
+			if nb > 0 and (typ.friendlyblock ~= nil or not typ.actorblock) then -- decrement for passable actors
+				local a = map(lx, ly, engine.Map.ACTOR)
+				if a then -- friendly block controls if specified
+					if typ.friendlyblock ~= nil and typ.source_actor and typ.source_actor.reactionToward and typ.source_actor:reactionToward(a) >= 0 then
+						if not typ.friendlyblock then nb = nb - 1 end
+					elseif not typ.actorblock then 
+						nb = nb - 1
+					end
 				end
 			end
 			if nb > 0 then
 				if for_highlights then
-					-- Targeting highlight should be yellow if we don't know what we're firing through
+					-- Targeting highlight should be yellow if the grid is not known
 					if not is_known then
 						return false, "unknown", true
 					-- Don't show the path as blocked if it's blocked by an actor we can't see
-					elseif nb == 1 and typ.source_actor and typ.source_actor.canSee and not typ.source_actor:canSee(game.level.map(lx, ly, engine.Map.ACTOR)) then
+					elseif nb == 1 and typ.source_actor and typ.source_actor.canSee and not typ.source_actor:canSee(map(lx, ly, engine.Map.ACTOR)) then
 						return false, true, true
 					end
 				end
@@ -481,15 +505,56 @@ _M.defaults.block_path = function(typ, lx, ly, for_highlights)
 			return false, "unknown", true
 		end
 	end
-	-- If we don't block the path, then the explode point should be here
+	-- Projection not blocked, grid is hit
 	return false, true, true
 end
 
+--- Determine if a grid blocks projection based on targeting table parameters (radius test)
+-- @see Target:getType(t) below
+-- @param typ = updated targeting table (from Target:getType)
+-- @param lx, ly = grid coordinates
+-- @param for_highlights [type=boolean] highlight grids for player targeting mode
+-- @return[1] [type=boolean] grid blocks the projection
 _M.defaults.block_radius = function(typ, lx, ly, for_highlights)
-	return not typ.no_restrict and
-		game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") and
-		not game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "pass_projectile") and
-		not (for_highlights and not (game.level.map.remembers(lx, ly) or game.level.map.seens(lx, ly)))
+	local map = game.level.map
+	if not map:isBound(lx, ly) then return true end
+	if typ.no_restrict then return end
+	if typ.requires_knowledge and not (map.remembers(lx, ly) or map.seens(lx, ly)) then return true end
+
+	local blocked, trn_block, trn_pass
+	if not typ.pass_terrain then -- check terrain
+		trn_block = map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") or false
+		if trn_block then 
+			trn_pass = map:checkEntity(lx, ly, engine.Map.TERRAIN, "pass_projectile") or false
+			if not trn_pass then blocked = true end -- blocked by terrain
+		end
+	end
+	
+	if not blocked and typ.stop_block then -- check all entities
+		 -- get #blocking entities and subtract for each entity that can be explicitly passed through
+		local nb = map:checkAllEntitiesCount(lx, ly, "block_move")
+		if nb > 0 then -- decrement for passable terrain
+			if trn_block == nil then trn_block = map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") end
+			if trn_block and (typ.pass_terrain or (trn_pass == nil and map:checkEntity(lx, ly, engine.Map.TERRAIN, "pass_projectile") or trn_pass)) then
+				nb = nb - 1
+			end
+		end
+		if nb > 0 and (typ.friendlyblock ~= nil or not typ.actorblock) then -- decrement for passable actors
+			local a = map(lx, ly, engine.Map.ACTOR)
+			if a then -- friendly block controls if specified
+				if typ.friendlyblock ~= nil and typ.source_actor and typ.source_actor.reactionToward and typ.source_actor:reactionToward(a) >= 0 then
+					if not typ.friendlyblock then nb = nb - 1 end
+				elseif not typ.actorblock then 
+					nb = nb - 1
+				end
+			end
+		end
+		if nb > 0 then
+			blocked = true
+		end
+	end
+	-- treat unknown grids as non-blocking for player targeting highlights
+	if blocked and not (for_highlights and not (map.remembers(lx, ly) or map.seens(lx, ly))) then return true end
 end
 
 --- targeting type strings -> modification function.
@@ -509,31 +574,39 @@ _M.types_def = {
 		dest.wall = src.halflength
 		end,
 	bolt = function(dest, src) dest.stop_block = true end,
-	beam = function(dest, scr) dest.line = true end,}
-
--- @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 around 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.grid_exclude = {[x1][y1]=true,...[x2][y2]=true...} Grids to exclude - for making holes in the AOE
+	beam = function(dest, scr) dest.line = true end,
+}
+
+--- Interpret a targeting table, applying default fields needed by ActorProject and realDisplay
+-- @param t = targeting table to be interpreted/updated, containing specific target parameters
+-- @param t.type = string target geometric type, populates other default variables (see below), defined types:
+-- 		hit: hit a single grid in LOS
+-- 		beam: hit all grids along a LOS path
+-- 		bolt: hit the first blocking grid along a LOS path
+-- 		ball: hit all grids in a ball around the target
+-- 		cone: hit all grids in a cone aimed at the target
+-- @param t.range = maximum range from origin to target <default: 20>
+-- @param t.min_range = minimum range from origin to target
+-- @param t.cone_angle = angle for cone AoE <default: 55°>
+-- @param t.radius = radius for ball/cone AoE
+-- @param t.grid_exclude = {[x1][y1]=true,...[x2][y2]=true...} Grids to exclude - (makes holes in AoE)
 -- @param t.act_exclude = {[uid] = true,...} exclude grids containing actor(s) with the matching uid(s)
--- @param t.selffire = boolean or % chance to project against grids with self
--- @param t.friendlyfire = boolean or % chance to project against grids with friendly Actors (based on 			Actor:reactionToward(target)>0)
--- @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.  It should return three arguments: block, hit, hit_radius
--- @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.
--- @return[1] t The target table used by ActorProject
--- @return[2] `Projectile`
--- @return[3] `GameTargeting`
--- @return[4] etc
+-- @param t.selffire = boolean or % chance to project against grids with self <default: true>
+-- @param t.friendlyfire = boolean or % chance to project against grids with friendly Actors (based on Actor:reactionToward(target)>0) <default: true>
+-- @param t.multiple = boolean t contains multiple indexed targeting tables (interpreted in place)
+-- @param t.block_path = function(typ, lx, ly, for_highlights) (default set according to t.type):
+--		Determines if/how a projection is blocked along a path
+--		returns block (grid blocks), hit (grid hit), hit_radius (grid blocks, path around disallowed)
+-- @param t.block_radius = function(typ, lx, ly, for_highlights) (default set according to t.type):
+--		Determines if a radial projection from a point is blocked
+ --Parameters interpreted by the default blocking functions:
+-- @param t.no_restrict = boolean all grids are treated as non-blocking
+-- @param t.pass_terrain = boolean pass through all terrain (Grid.pass_projectile also checked)
+-- @param t.requires_knowledge = boolean stop at unknown grids (for player)
+-- @param t.stop_block = boolean stop at first grid that has any entity (not just terrain) that blocks move
+-- @param t.actorblock (req. stop_block) = boolean stop at the first Actor <default: true>
+-- @param t.friendlyblock (req. stop_block) = boolean stop/no stop at friendly Actors (overrides actorblock)
+-- @return[1] An updated targeting table ready to be used by ActorProject
 function _M:getType(t)
 	if not t then return {} end
 
@@ -552,7 +625,7 @@ function _M:getType(t)
 		range = 20,
 		selffire = true,
 		friendlyfire = true,
-		friendlyblock = true,
+		actorblock = true,
 	}
 	for k, v in pairs(self.defaults) do target_type[k] = v end