diff --git a/game/engines/default/engine/interface/ActorFOV.lua b/game/engines/default/engine/interface/ActorFOV.lua
index 62388ce430507d2b234ffe2ebc0e11630f1186a8..6cfe58c72f4265974a42996dd8d4a75921c6c550 100644
--- a/game/engines/default/engine/interface/ActorFOV.lua
+++ b/game/engines/default/engine/interface/ActorFOV.lua
@@ -129,6 +129,70 @@ function _M:computeFOV(radius, block, apply, force, no_store, cache)
 	end
 end
 
+--- Computes actor's FOV as a beam
+-- @param radius the FOV radius, defaults to 20
+-- @param dir the FOV beam direction (1,2,3,4,6,7,8,9)
+-- @param angle the FOV beam spread
+-- @param block the property to look for FOV blocking, defaults to "block_sight"
+-- @param apply an apply function that will be called on each seen grids, defaults to nil
+-- @param force set to true to force a regeneration even if we did not move
+-- @param no_store do not store FOV informations
+-- @param cache if true it will use the cache given by the map, for the map actor. It can be used for other actors is they have the same block settings
+function _M:computeFOVBeam(radius, dir, angle, block, apply, force, no_store, cache)
+	-- If we did not move, do not update
+	if not force and self.fov_last_x == self.x and self.fov_last_y == self.y and self.fov_computed then return end
+	radius = radius or 20
+	dir = dir or 8
+	angle = angle or 45
+	block = block or "block_sight"
+
+	-- Simple FOV compute no storage
+	if no_store and apply then
+		local map = game.level.map
+		core.fov.calc_beam(self.x, self.y, map.w, map.h, radius, dir, angle, function(_, x, y)
+			if map:checkAllEntities(x, y, block, self) then return true end
+		end, function(_, x, y, dx, dy, sqdist)
+			apply(x, y, dx, dy, sqdist)
+		end, cache and game.level.map._fovcache[block])
+
+	elseif not no_store then
+		local fov = {actors={}, actors_dist={}}
+		setmetatable(fov.actors, {__mode='k'})
+		setmetatable(fov.actors_dist, {__mode='v'})
+
+		local map = game.level.map
+		core.fov.calc_beam(self.x, self.y, map.w, map.h, radius, dir, angle, function(_, x, y)
+			if map:checkAllEntities(x, y, block, self) then return true end
+		end, function(_, x, y, dx, dy, sqdist)
+			if apply then apply(x, y, dx, dy, sqdist) end
+
+			if self.__do_distance_map then self.distance_map[x + y * game.level.map.w] = game.turn + radius - math.sqrt(sqdist) end
+
+			-- Note actors
+			local a = map(x, y, Map.ACTOR)
+			if a and a ~= self and not a.dead then
+				local t = {x=x,y=y, dx=dx, dy=dy, sqdist=sqdist}
+				fov.actors[a] = t
+				fov.actors_dist[#fov.actors_dist+1] = a
+				a.__sqdist = sqdist
+				a:check("seen_by", self)
+				a:updateFOV(self, t.sqdist)
+			end
+		end, cache and game.level.map._fovcache[block])
+
+		-- Sort actors by distance (squared but we do not care)
+		table.sort(fov.actors_dist, "__sqdist")
+--		print("Computed FOV for", self.uid, self.name, ":: seen ", #fov.actors_dist, "actors closeby")
+
+		self.fov = fov
+		self.fov_last_x = self.x
+		self.fov_last_y = self.y
+		self.fov_last_turn = game.turn
+		self.fov_last_change = game.turn
+		self.fov_computed = true
+	end
+end
+
 --- Update our fov to include the given actor at the given dist
 -- @param a the actor to include
 -- @param sqdist the squared distance to that actor
diff --git a/game/modules/angband/class/Player.lua b/game/modules/angband/class/Player.lua
index 68e30ce3cce043e4360356c3710968b6aa85cc58..944e4245e27f64e71bf821a75e807d1faa99d87a 100644
--- a/game/modules/angband/class/Player.lua
+++ b/game/modules/angband/class/Player.lua
@@ -388,7 +388,7 @@ end
 local function spotHostiles(self)
 	local seen = false
 	-- Check for visible monsters, only see LOS actors, so telepathy wont prevent resting
-	core.fov.calc_circle(self.x, self.y, game.level.w, game.level.h, 20, function(_, x, y) return game.level.map:opaque(x, y) end, function(_, x, y)
+	core.fov.calc_circle(self.x, self.y, game.level.map.w, game.level.map.h, 20, function(_, x, y) return game.level.map:opaque(x, y) end, function(_, x, y)
 		local actor = game.level.map(x, y, game.level.map.ACTOR)
 		if actor and self:reactionToward(actor) < 0 and self:canSee(actor) and game.level.map.seens(x, y) then seen = true end
 	end, nil)
diff --git a/game/modules/example/class/Player.lua b/game/modules/example/class/Player.lua
index 26b2e262bdc7c274882f94c20bd0dc11240bb06d..b3e55e7418c336f849249f6cd2ac22d1ef1d5da3 100644
--- a/game/modules/example/class/Player.lua
+++ b/game/modules/example/class/Player.lua
@@ -154,7 +154,7 @@ end
 local function spotHostiles(self)
 	local seen = false
 	-- Check for visible monsters, only see LOS actors, so telepathy wont prevent resting
-	core.fov.calc_circle(self.x, self.y, game.level.w, game.level.h, 20, function(_, x, y) return game.level.map:opaque(x, y) end, function(_, x, y)
+	core.fov.calc_circle(self.x, self.y, game.level.map.w, game.level.map.h, 20, function(_, x, y) return game.level.map:opaque(x, y) end, function(_, x, y)
 		local actor = game.level.map(x, y, game.level.map.ACTOR)
 		if actor and self:reactionToward(actor) < 0 and self:canSee(actor) and game.level.map.seens(x, y) then seen = true end
 	end, nil)
diff --git a/game/modules/example_realtime/class/Player.lua b/game/modules/example_realtime/class/Player.lua
index 44241450c6675879fa3611170288742806f56a41..fa3d78301c0c80d34c7a766ded77f1c2cabdf442 100644
--- a/game/modules/example_realtime/class/Player.lua
+++ b/game/modules/example_realtime/class/Player.lua
@@ -153,7 +153,7 @@ end
 local function spotHostiles(self)
 	local seen = false
 	-- Check for visible monsters, only see LOS actors, so telepathy wont prevent resting
-	core.fov.calc_circle(self.x, self.y, game.level.w, game.level.h, 20, function(_, x, y) return game.level.map:opaque(x, y) end, function(_, x, y)
+	core.fov.calc_circle(self.x, self.y, game.level.map.w, game.level.map.h, 20, function(_, x, y) return game.level.map:opaque(x, y) end, function(_, x, y)
 		local actor = game.level.map(x, y, game.level.map.ACTOR)
 		if actor and self:reactionToward(actor) < 0 and self:canSee(actor) and game.level.map.seens(x, y) then seen = true end
 	end, nil)
diff --git a/game/modules/tome/data/general/objects/boss-artifacts.lua b/game/modules/tome/data/general/objects/boss-artifacts.lua
index 8ffdd2e731d8f6a8d0b19775cae52c5e3974fc43..5993676fc7e42b0fa242e96b5d27c40a35e71fbd 100644
--- a/game/modules/tome/data/general/objects/boss-artifacts.lua
+++ b/game/modules/tome/data/general/objects/boss-artifacts.lua
@@ -536,7 +536,7 @@ newEntity{ define_as = "RUNED_SKULL",
 
 newEntity{ base = "BASE_GREATMAUL",
 	define_as = "GREATMAUL_BILL_TRUNK",
-	unided_name = "tree trunk",
+	unided_name = "tree trunk", image = "object/artifact/bill_treestump.png",
 	name = "Bill's Tree Trunk", unique=true,
 	desc = [[This is a big, nasty-looking tree trunk that Bill was using as a weapon. It could still serve this purpose, should you be strong enough to wield it!]],
 	require = { stat = { str=25 }, },
diff --git a/game/modules/tome/data/gfx/shockbolt/npc/troll_bill.png b/game/modules/tome/data/gfx/shockbolt/npc/troll_bill.png
new file mode 100644
index 0000000000000000000000000000000000000000..b370d4a5a6de3065c8c0bf528789430cfb15c0ff
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/npc/troll_bill.png differ
diff --git a/game/modules/tome/data/gfx/shockbolt/object/artifact/bill_treestump.png b/game/modules/tome/data/gfx/shockbolt/object/artifact/bill_treestump.png
new file mode 100644
index 0000000000000000000000000000000000000000..a9f69ae4df7cc6a4a2f1670c2ea9576d6183ded4
Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/object/artifact/bill_treestump.png differ