diff --git a/game/engines/default/engine/Module.lua b/game/engines/default/engine/Module.lua
index 79dfad26e984c99ab320e2d928d1066e42e74c69..182c2e41b308adaecf470470d8af5414d4042cf1 100644
--- a/game/engines/default/engine/Module.lua
+++ b/game/engines/default/engine/Module.lua
@@ -383,6 +383,10 @@ function _M:instanciate(mod, name, new_game, no_reboot)
 	-- Turn based by default
 	core.game.setRealtime(0)
 
+	-- FOV Shape
+	core.fov.set_vision_shape("circle")
+	core.fov.set_permissiveness("square")
+
 	-- Init the module directories
 	mod.load("setup")
 
diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua
index 7337ac5b764391543e95f5f95b8e3e2aaa625bc3..8860843fa1d59358747326ce55af092ff55ce78a 100644
--- a/game/engines/default/engine/utils.lua
+++ b/game/engines/default/engine/utils.lua
@@ -1106,6 +1106,7 @@ end
 -- "firstpeek" is the least permissive setting that allows @ to see r below:
 -- @##
 -- ..r
+-- Default is "square"
 function core.fov.set_permissiveness(val)
 	val = type(val) == "string" and (string.lower(val) == "square" and 0.0 or
 						string.lower(val) == "diamond" and 0.5 or
@@ -1116,6 +1117,27 @@ function core.fov.set_permissiveness(val)
 	if type(val) ~= "number" then return end
 	val = util.bound(val, 0.0, 0.5)
 	core.fov.set_permissiveness_base(val)
+	return 2*val
+end
+
+--- Sets the vision shape or distance metric for field of vision, talent ranges, AoEs, etc.
+-- @param should be a string: circle, circle_round (same as circle), circle_floor, circle_ceil, circle_plus1, octagon, diamond, square.
+-- See "src/fov/fov.h" to see how each shape calculates distance and height.
+-- "circle_round" is aesthetically pleasing, "octagon" is a traditional roguelike FoV shape, and "circle_plus1" is similar to both "circle_round" and "octagon"
+-- Default is "circle_round"
+function core.fov.set_vision_shape(val)
+	sval = type(val) == "string" and string.lower(val)
+	val = sval and ((sval == "circle" or sval == "circle_round") and 0 or
+				sval == "circle_floor" and 1 or
+				sval == "circle_ceil" and 2 or
+				sval == "circle_plus1" and 3 or
+				sval == "octagon" and 4 or
+				sval == "diamond" and 5 or
+				sval == "square" and 6) or
+			type(tonumber(val)) == "number" and tonumber(val)
+
+	if type(val) ~= "number" then return end
+	core.fov.set_vision_shape_base(val)
 	return val
 end
 
diff --git a/src/fov.c b/src/fov.c
index 82115d49cabf7ead410dd2d9fd1a7613c511cf56..ee13492a0775e076392304d492964f5cc88281f9 100644
--- a/src/fov.c
+++ b/src/fov.c
@@ -43,6 +43,7 @@
  ******************************************************************/
 
 static const char FOV_PERMISSIVE_KEY = 'k';
+static const char FOV_VISION_SHAPE_KEY = 'k';
 
 struct lua_fovcache
 {
@@ -83,23 +84,43 @@ static int lua_fov_get_permissiveness(lua_State *L)
 		lua_settable(L, LUA_REGISTRYINDEX);
 	}
 }
+
+static int lua_fov_set_vision_shape(lua_State *L)
+{
+	int val = luaL_checknumber(L, 1);
+	lua_pushlightuserdata(L, (void *)&FOV_VISION_SHAPE_KEY); // push address as guaranteed unique key
+	lua_pushnumber(L, val);
+	lua_settable(L, LUA_REGISTRYINDEX);
+	return 0;
+}
+
+static int lua_fov_get_vision_shape(lua_State *L)
+{
+	lua_pushlightuserdata(L, (void *)&FOV_VISION_SHAPE_KEY); // push address as guaranteed unique key
+	lua_gettable(L, LUA_REGISTRYINDEX);  /* retrieve value */
+	if (lua_isnil(L, -1)) {
+		lua_pop(L, 1); // remove nil
+		lua_pushlightuserdata(L, (void *)&FOV_VISION_SHAPE_KEY); // push address as guaranteed unique key
+		lua_pushnumber(L, FOV_SHAPE_CIRCLE_ROUND);
+		lua_settable(L, LUA_REGISTRYINDEX);
+	}
+}
+
 static void map_seen(void *m, int x, int y, int dx, int dy, int radius, void *src)
 {
 	struct lua_fov *fov = (struct lua_fov *)m;
 	if (x < 0 || y < 0 || x >= fov->w || y >= fov->h) return;
-	if (dx*dx + dy*dy <= radius*radius + radius) // <-- use shape of FoV.  Also, is this check really necessary? TODO: verify and delete if unnecessary
-	{
-		// circular view - can be changed if you like
-		lua_rawgeti(fov->L, LUA_REGISTRYINDEX, fov->apply_ref);
-		if (fov->cache) lua_rawgeti(fov->L, LUA_REGISTRYINDEX, fov->cache_ref);
-		else lua_pushnil(fov->L);
-		lua_pushnumber(fov->L, x);
-		lua_pushnumber(fov->L, y);
-		lua_pushnumber(fov->L, dx);
-		lua_pushnumber(fov->L, dy);
-		lua_pushnumber(fov->L, dx*dx + dy*dy);
-		lua_call(fov->L, 6, 0);
-	}
+
+	// circular view - can be changed if you like
+	lua_rawgeti(fov->L, LUA_REGISTRYINDEX, fov->apply_ref);
+	if (fov->cache) lua_rawgeti(fov->L, LUA_REGISTRYINDEX, fov->cache_ref);
+	else lua_pushnil(fov->L);
+	lua_pushnumber(fov->L, x);
+	lua_pushnumber(fov->L, y);
+	lua_pushnumber(fov->L, dx);
+	lua_pushnumber(fov->L, dy);
+	lua_pushnumber(fov->L, dx*dx + dy*dy);
+	lua_call(fov->L, 6, 0);
 }
 
 static bool map_opaque(void *m, int x, int y)
@@ -155,6 +176,8 @@ static int lua_fov_calc_circle(lua_State *L)
 	fov_settings_set_apply_lighting_function(&(fov.fov_settings), map_seen);
 	lua_fov_get_permissiveness(L);
 	fov.fov_settings.permissiveness = luaL_checknumber(L, -1);
+	lua_fov_get_vision_shape(L);
+	fov.fov_settings.shape = luaL_checknumber(L, -1);
 
 	fov_circle(&(fov.fov_settings), &fov, NULL, x, y, radius);
 	map_seen(&fov, x, y, 0, 0, radius, NULL);
@@ -213,6 +236,8 @@ static int lua_fov_calc_beam(lua_State *L)
 	fov_settings_set_apply_lighting_function(&(fov.fov_settings), map_seen);
 	lua_fov_get_permissiveness(L);
 	fov.fov_settings.permissiveness = luaL_checknumber(L, -1);
+	lua_fov_get_vision_shape(L);
+	fov.fov_settings.shape = luaL_checknumber(L, -1);
 
 	fov_beam(&(fov.fov_settings), &fov, NULL, x, y, radius, dir, angle);
 	map_seen(&fov, x, y, 0, 0, radius, NULL);
@@ -259,6 +284,8 @@ static int lua_fov_calc_beam_any_angle(lua_State *L)
 	fov_settings_set_apply_lighting_function(&(fov.fov_settings), map_seen);
 	lua_fov_get_permissiveness(L);
 	fov.fov_settings.permissiveness = luaL_checknumber(L, -1);
+	lua_fov_get_vision_shape(L);
+	fov.fov_settings.shape = luaL_checknumber(L, -1);
 
 	fov_beam_any_angle(&(fov.fov_settings), &fov, NULL, x, y, radius, dx, dy, beam_angle);
 	map_seen(&fov, x, y, 0, 0, radius, NULL);
@@ -277,9 +304,49 @@ static int lua_distance(lua_State *L)
 	double y1 = luaL_checknumber(L, 2);
 	double x2 = luaL_checknumber(L, 3);
 	double y2 = luaL_checknumber(L, 4);
+	bool ret_float = lua_toboolean(L, 5);
+	double dx = fabs(x2 - x1);
+	double dy = fabs(y2 - y1);
+	double dist;
+	lua_fov_get_vision_shape(L);
+	int shape = luaL_checknumber(L, -1);
+
+	switch(shape) {
+	case FOV_SHAPE_CIRCLE_ROUND :
+		dist = sqrt(dx*dx + dy*dy) + 0.5;
+		break;
+	case FOV_SHAPE_CIRCLE_FLOOR :
+		dist = sqrt(dx*dx + dy*dy);
+		break;
+	case FOV_SHAPE_CIRCLE_CEIL :
+		if (ret_float)
+			dist = sqrt(dx*dx + dy*dy);
+		else
+			dist = ceil(sqrt(dx*dx + dy*dy));
+		break;
+	case FOV_SHAPE_CIRCLE_PLUS1 :
+		dist = sqrt(dx*dx + dy*dy);
+		if (dist > 0.5) dist = dist + 1 - 1.0/dist;
+		break;
+	case FOV_SHAPE_OCTAGON :
+		dist = (dx > dy) ? (dx + 0.5*dy) : (dy + 0.5*dx);
+		break;
+	case FOV_SHAPE_DIAMOND :
+		dist = dx + dy;
+		break;
+	case FOV_SHAPE_SQUARE :
+		dist = (dx > dy) ? dx : dy;
+		break;
+	default :
+		dist = sqrt(dx*dx + dy*dy) + 0.5;
+		break;
+	}
+
+	if (ret_float)
+		lua_pushnumber(L, dist);
+	else
+		lua_pushnumber(L, (int)dist);
 
-	// TODO: switch/case based on FoV shape.  Use rounded circle for now
-	lua_pushnumber(L, (int)(sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) + 0.5f));
 	return 1;
 }
 
@@ -355,101 +422,101 @@ typedef struct {
 
 static void map_default_seen(void *m, int x, int y, int dx, int dy, int radius, void *src)
 {
+	// TODO: understand how this function uses distances and use "lua_distance" (i.e., core.fov.distance) if necessary
 	default_fov *def = (default_fov*)src;
 	struct lua_fov *fov = (struct lua_fov *)m;
 	float sqdist = dx*dx + dy*dy;
 	float dist = sqrtf(sqdist);
-	if (dx*dx + dy*dy <= radius*radius + radius) // <-- use FoV shape.  Also, is this check really necessary? TODO: verify and delete if unnecessary
-	{
-		// Distance Map
-		if (def->do_dmap)
-		{
-			lua_pushnumber(fov->L, x + y * def->w);
-			lua_pushnumber(fov->L, def->turn + radius - dist);
-			lua_rawset(fov->L, STACK_DMAP);
-		}
-
-		// Apply
-		if (def->do_apply)
-		{
-			lua_pushvalue(fov->L, STACK_APPLY);
-			lua_pushnumber(fov->L, x);
-			lua_pushnumber(fov->L, y);
-			lua_pushnumber(fov->L, dx);
-			lua_pushnumber(fov->L, dy);
-			lua_pushnumber(fov->L, sqdist);
-			lua_call(fov->L, 5, 0);
-		}
+	if (x < 0 || y < 0 || x >= fov->w || y >= fov->h) return;
 
-		// Get entity
+	// Distance Map
+	if (def->do_dmap)
+	{
 		lua_pushnumber(fov->L, x + y * def->w);
-		lua_rawget(fov->L, STACK_MAP);
-		if (!lua_istable(fov->L, -1)) { lua_pop(fov->L, 1); return; }
-		lua_pushnumber(fov->L, def->entity);
-		lua_rawget(fov->L, -2);
-		if (!lua_istable(fov->L, -1)) { lua_pop(fov->L, 2); return; }
-
-		// Check if dead
-		lua_pushstring(fov->L, "dead");
-		lua_gettable(fov->L, -2);
-		if (lua_toboolean(fov->L, -1)) { lua_pop(fov->L, 3); return; }
-		lua_pop(fov->L, 1);
-
-		// Set sqdist in the actor for faster sorting
-		lua_pushstring(fov->L, "__sqdist");
-		lua_pushnumber(fov->L, sqdist);
-		lua_rawset(fov->L, -3);
+		lua_pushnumber(fov->L, def->turn + radius - dist);
+		lua_rawset(fov->L, STACK_DMAP);
+	}
 
-		// Make a table to hold data
-		lua_newtable(fov->L);
-		lua_pushstring(fov->L, "x");
+	// Apply
+	if (def->do_apply)
+	{
+		lua_pushvalue(fov->L, STACK_APPLY);
 		lua_pushnumber(fov->L, x);
-		lua_rawset(fov->L, -3);
-		lua_pushstring(fov->L, "y");
 		lua_pushnumber(fov->L, y);
-		lua_rawset(fov->L, -3);
-		lua_pushstring(fov->L, "dx");
 		lua_pushnumber(fov->L, dx);
-		lua_rawset(fov->L, -3);
-		lua_pushstring(fov->L, "dy");
 		lua_pushnumber(fov->L, dy);
-		lua_rawset(fov->L, -3);
-		lua_pushstring(fov->L, "sqdist");
 		lua_pushnumber(fov->L, sqdist);
-		lua_rawset(fov->L, -3);
-
-		// Set the actor table
-		lua_pushvalue(fov->L, -2);
-		lua_pushvalue(fov->L, -2);
-		lua_rawset(fov->L, STACK_ACTOR);
-
-		// Set the dist table
-		def->dist_idx++;
-		lua_pushnumber(fov->L, def->dist_idx);
-		lua_pushvalue(fov->L, -3);
-		lua_rawset(fov->L, STACK_DIST);
+		lua_call(fov->L, 5, 0);
+	}
 
-		// Call seen_by, if possible
-		lua_pushstring(fov->L, "updateFOV");
-		lua_gettable(fov->L, -3);
+	// Get entity
+	lua_pushnumber(fov->L, x + y * def->w);
+	lua_rawget(fov->L, STACK_MAP);
+	if (!lua_istable(fov->L, -1)) { lua_pop(fov->L, 1); return; }
+	lua_pushnumber(fov->L, def->entity);
+	lua_rawget(fov->L, -2);
+	if (!lua_istable(fov->L, -1)) { lua_pop(fov->L, 2); return; }
+
+	// Check if dead
+	lua_pushstring(fov->L, "dead");
+	lua_gettable(fov->L, -2);
+	if (lua_toboolean(fov->L, -1)) { lua_pop(fov->L, 3); return; }
+	lua_pop(fov->L, 1);
+
+	// Set sqdist in the actor for faster sorting
+	lua_pushstring(fov->L, "__sqdist");
+	lua_pushnumber(fov->L, sqdist);
+	lua_rawset(fov->L, -3);
+
+	// Make a table to hold data
+	lua_newtable(fov->L);
+	lua_pushstring(fov->L, "x");
+	lua_pushnumber(fov->L, x);
+	lua_rawset(fov->L, -3);
+	lua_pushstring(fov->L, "y");
+	lua_pushnumber(fov->L, y);
+	lua_rawset(fov->L, -3);
+	lua_pushstring(fov->L, "dx");
+	lua_pushnumber(fov->L, dx);
+	lua_rawset(fov->L, -3);
+	lua_pushstring(fov->L, "dy");
+	lua_pushnumber(fov->L, dy);
+	lua_rawset(fov->L, -3);
+	lua_pushstring(fov->L, "sqdist");
+	lua_pushnumber(fov->L, sqdist);
+	lua_rawset(fov->L, -3);
+
+	// Set the actor table
+	lua_pushvalue(fov->L, -2);
+	lua_pushvalue(fov->L, -2);
+	lua_rawset(fov->L, STACK_ACTOR);
+
+	// Set the dist table
+	def->dist_idx++;
+	lua_pushnumber(fov->L, def->dist_idx);
+	lua_pushvalue(fov->L, -3);
+	lua_rawset(fov->L, STACK_DIST);
+
+	// Call seen_by, if possible
+	lua_pushstring(fov->L, "updateFOV");
+	lua_gettable(fov->L, -3);
+	lua_pushvalue(fov->L, -3);
+	lua_pushvalue(fov->L, STACK_SELF);
+	lua_pushnumber(fov->L, sqdist);
+	lua_call(fov->L, 3, 0);
+
+	// Call seen_by, if possible
+	lua_pushstring(fov->L, "seen_by");
+	lua_gettable(fov->L, -3);
+	if (lua_isfunction(fov->L, -1))
+	{
 		lua_pushvalue(fov->L, -3);
 		lua_pushvalue(fov->L, STACK_SELF);
-		lua_pushnumber(fov->L, sqdist);
-		lua_call(fov->L, 3, 0);
-
-		// Call seen_by, if possible
-		lua_pushstring(fov->L, "seen_by");
-		lua_gettable(fov->L, -3);
-		if (lua_isfunction(fov->L, -1))
-		{
-			lua_pushvalue(fov->L, -3);
-			lua_pushvalue(fov->L, STACK_SELF);
-			lua_call(fov->L, 2, 0);
-		}
-		else lua_pop(fov->L, 1);
-
-		lua_pop(fov->L, 3);
+		lua_call(fov->L, 2, 0);
 	}
+	else lua_pop(fov->L, 1);
+
+	lua_pop(fov->L, 3);
 }
 
 static bool map_default_opaque(void *m, int x, int y)
@@ -497,6 +564,8 @@ static int lua_fov_calc_default_fov(lua_State *L)
 	fov_settings_set_apply_lighting_function(&(fov.fov_settings), map_default_seen);
 	lua_fov_get_permissiveness(L);
 	fov.fov_settings.permissiveness = luaL_checknumber(L, -1);
+	lua_fov_get_vision_shape(L);
+	fov.fov_settings.shape = luaL_checknumber(L, -1);
 
 	fov_circle(&(fov.fov_settings), &fov, &def, x, y, radius);
 	map_default_seen(&fov, x, y, 0, 0, radius, &def);
@@ -539,10 +608,13 @@ static int lua_fov_line_init(lua_State *L)
 	bool start_at_end = lua_toboolean(L, 8);
 	fov.w = w;
 	fov.h = h;
+
 	fov_settings_init(&(fov.fov_settings));
 	fov_settings_set_opacity_test_function(&(fov.fov_settings), map_opaque);
 	lua_fov_get_permissiveness(L);
 	fov.fov_settings.permissiveness = luaL_checknumber(L, -1);
+	lua_fov_get_vision_shape(L);
+	fov.fov_settings.shape = luaL_checknumber(L, -1);
 
 	fov_line_data *line = (fov_line_data*)lua_newuserdata(L, sizeof(fov_line_data));
 
@@ -715,6 +787,7 @@ static const struct luaL_reg fovlib[] =
 	{"line_base", lua_fov_line_init},
 	{"line_import", lua_fov_line_import},
 	{"set_permissiveness_base", lua_fov_set_permissiveness},
+	{"set_vision_shape_base", lua_fov_set_vision_shape},
 	{NULL, NULL},
 };
 
diff --git a/src/fov/fov.c b/src/fov/fov.c
index 04cbc79888039576f0f6773c84d24ff22d462265..037ad6540841d1f932e7b95a4ad68ab51d7c5d35 100644
--- a/src/fov/fov.c
+++ b/src/fov/fov.c
@@ -82,7 +82,7 @@ typedef struct {
 /* Options -------------------------------------------------------- */
 
 void fov_settings_init(fov_settings_type *settings) {
-    settings->shape = FOV_SHAPE_CIRCLE_PRECALCULATE;
+    settings->shape = FOV_SHAPE_CIRCLE_ROUND;
     settings->corner_peek = FOV_CORNER_NOPEEK;
     settings->opaque_apply = FOV_OPAQUE_APPLY;
     settings->opaque = NULL;
@@ -238,23 +238,32 @@ static float fov_slope(float dx, float dy) {
         }                                                                                       \
                                                                                                 \
         switch (settings->shape) {                                                              \
-        case FOV_SHAPE_CIRCLE_PRECALCULATE:                                                     \
+        case FOV_SHAPE_CIRCLE_ROUND :                                                           \
             h = height(settings, dx, data->radius);                                             \
             break;                                                                              \
-        case FOV_SHAPE_CIRCLE:                                                                  \
-            h = (unsigned)(sqrt((data->radius)*(data->radius) + data->radius - dx*dx));         \
+        case FOV_SHAPE_CIRCLE_FLOOR :                                                           \
+            h = (unsigned)(sqrt((data->radius)*(data->radius) + 2*data->radius - dx*dx));       \
+            break;                                                                              \
+        case FOV_SHAPE_CIRCLE_CEIL :                                                            \
+            h = (unsigned)(sqrt((data->radius)*(data->radius) - dx*dx));                        \
+            break;                                                                              \
+        case FOV_SHAPE_CIRCLE_PLUS1 :                                                           \
+            h = (unsigned)(sqrt((data->radius)*(data->radius) + 1 - dx*dx));                    \
             break;                                                                              \
         case FOV_SHAPE_OCTAGON:                                                                 \
-            h = (data->radius - dx)<<1;                                                         \
+            h = 2u*(data->radius - (unsigned)dx) + 1u;                                          \
             break;                                                                              \
-        default:                                                                                \
+        case FOV_SHAPE_DIAMOND :                                                                \
+            h = data->radius - (unsigned)dx;                                                    \
+            break;                                                                              \
+        case FOV_SHAPE_SQUARE :                                                                 \
             h = data->radius;                                                                   \
             break;                                                                              \
+        default :                                                                               \
+            h = (unsigned)(sqrt((data->radius)*(data->radius) + data->radius - dx*dx));         \
+            break;                                                                              \
         };                                                                                      \
         if ((unsigned)dy1 > h) {                                                                \
-            if (h == 0) {                                                                       \
-                return;                                                                         \
-            }                                                                                   \
             dy1 = (int)h;                                                                       \
         }                                                                                       \
                                                                                                 \
@@ -469,82 +478,92 @@ void fov_beam(fov_settings_type *settings, void *map, void *source,
     end_slope = betweenf(angle_end, 0.0f, 1.0f);                         \
     fov_octant_##p1(&data, 1, start_slope, end_slope, true, true);       \
                                                                          \
-    if (angle_end - 1.0 > FLT_EPSILON) {                                 \
+    if (angle_end - 1.0f > FLT_EPSILON) {                                \
         start_slope = betweenf(2.0f - angle_end, 0.0f, 1.0f);            \
         fov_octant_##p2(&data, 1, start_slope, 1.0f, true, false);       \
                                                                          \
-    if (angle_end - 2.0 > 2.0f * FLT_EPSILON) {                          \
+    if (angle_end - 2.0f > 2.0f * FLT_EPSILON) {                         \
         end_slope = betweenf(angle_end - 2.0f, 0.0f, 1.0f);              \
         fov_octant_##p3(&data, 1, 0.0f, end_slope, false, true);         \
                                                                          \
-    if (angle_end - 3.0 > 3.0f * FLT_EPSILON) {                          \
+    if (angle_end - 3.0f > 3.0f * FLT_EPSILON) {                         \
         start_slope = betweenf(4.0f - angle_end, 0.0f, 1.0f);            \
         fov_octant_##p4(&data, 1, start_slope, 1.0f, true, false);       \
                                                                          \
-    if (angle_end - 4.0 > 4.0f * FLT_EPSILON) {                          \
+    if (angle_end - 4.0f > 4.0f * FLT_EPSILON) {                         \
         end_slope = betweenf(angle_end - 4.0f, 0.0f, 1.0f);              \
         fov_octant_##p5(&data, 1, 0.0f, end_slope, false, true);         \
                                                                          \
-    if (angle_end - 5.0 > 5.0f * FLT_EPSILON) {                          \
+    if (angle_end - 5.0f > 5.0f * FLT_EPSILON) {                         \
         start_slope = betweenf(6.0f - angle_end, 0.0f, 1.0f);            \
         fov_octant_##p6(&data, 1, start_slope, 1.0f, true, false);       \
                                                                          \
-    if (angle_end - 6.0 > 6.0f * FLT_EPSILON) {                          \
+    if (angle_end - 6.0f > 6.0f * FLT_EPSILON) {                         \
         end_slope = betweenf(angle_end - 6.0f, 0.0f, 1.0f);              \
         fov_octant_##p7(&data, 1, 0.0f, end_slope, false, true);         \
                                                                          \
-    if (angle_end - 7.0 > 7.0f * FLT_EPSILON) {                          \
+    if (angle_end - 7.0f > 7.0f * FLT_EPSILON) {                         \
         start_slope = betweenf(8.0f - angle_end, 0.0f, 1.0f);            \
-        fov_octant_##p8(&data, 1, start_slope, 1.0f, false, false);      \
-    }}}}}}}
+        fov_octant_##p8(&data, 1, start_slope, 1.0f, true, false);       \
+                                                                         \
+    if (angle_end - 8.0f > 8.0f * FLT_EPSILON) {                         \
+        end_slope = betweenf(angle_end - 8.0f, 0.0f, 1.0f);              \
+        start_slope = betweenf(angle_end - 8.0f, 0.0f, 1.0f);            \
+        fov_octant_##p1(&data, 1, 0.0f, end_slope, false, false);        \
+    }}}}}}}}
 
 #define BEAM_ANY_DIRECTION_DIAG(offset, p1, p2, p3, p4, p5, p6, p7, p8)  \
     angle_begin -= offset;                                               \
     angle_end -= offset;                                                 \
-    start_slope = betweenf(1.0 - angle_end, 0.0f, 1.0f);                 \
-    end_slope = 1.0 - angle_begin;                                       \
+    start_slope = betweenf(1.0f - angle_end, 0.0f, 1.0f);                \
+    end_slope = 1.0f - angle_begin;                                      \
     fov_octant_##p1(&data, 1, start_slope, end_slope, true, true);       \
                                                                          \
-    if (angle_end - 1.0 > FLT_EPSILON) {                                 \
+    if (angle_end - 1.0f > FLT_EPSILON) {                                \
         end_slope = betweenf(angle_end - 1.0f, 0.0f, 1.0f);              \
         fov_octant_##p2(&data, 1, 0.0f, end_slope, false, true);         \
                                                                          \
-    if (angle_end - 2.0 > 2.0f * FLT_EPSILON) {                          \
+    if (angle_end - 2.0f > 2.0f * FLT_EPSILON) {                         \
         start_slope = betweenf(3.0f - angle_end, 0.0f, 1.0f);            \
         fov_octant_##p3(&data, 1, start_slope, 1.0f, true, false);       \
                                                                          \
-    if (angle_end - 3.0 > 3.0f * FLT_EPSILON) {                          \
+    if (angle_end - 3.0f > 3.0f * FLT_EPSILON) {                         \
         end_slope = betweenf(angle_end - 3.0f, 0.0f, 1.0f);              \
         fov_octant_##p4(&data, 1, 0.0f, end_slope, false, true);         \
                                                                          \
-    if (angle_end - 4.0 > 4.0f * FLT_EPSILON) {                          \
+    if (angle_end - 4.0f > 4.0f * FLT_EPSILON) {                         \
         start_slope = betweenf(5.0f - angle_end, 0.0f, 1.0f);            \
         fov_octant_##p5(&data, 1, start_slope, 1.0f, true, false);       \
                                                                          \
-    if (angle_end - 5.0 > 5.0f * FLT_EPSILON) {                          \
+    if (angle_end - 5.0f > 5.0f * FLT_EPSILON) {                         \
         end_slope = betweenf(angle_end - 5.0f, 0.0f, 1.0f);              \
         fov_octant_##p6(&data, 1, 0.0f, end_slope, false, true);         \
                                                                          \
-    if (angle_end - 6.0 > 6.0f * FLT_EPSILON) {                          \
+    if (angle_end - 6.0f > 6.0f * FLT_EPSILON) {                         \
         start_slope = betweenf(7.0f - angle_end, 0.0f, 1.0f);            \
         fov_octant_##p7(&data, 1, start_slope, 1.0f, true, false);       \
                                                                          \
-    if (angle_end - 7.0 > 7.0f * FLT_EPSILON) {                          \
+    if (angle_end - 7.0f > 7.0f * FLT_EPSILON) {                         \
         end_slope = betweenf(angle_end - 7.0f, 0.0f, 1.0f);              \
-        fov_octant_##p8(&data, 1, 0.0f, end_slope, false, false);        \
-}}}}}}}
+        fov_octant_##p8(&data, 1, 0.0f, end_slope, false, true);         \
+                                                                         \
+    if (angle_end - 8.0f > 8.0f * FLT_EPSILON) {                         \
+        start_slope = betweenf(9.0f - angle_end, 0.0f, 1.0f);            \
+        fov_octant_##p1(&data, 1, start_slope, 1.0f, false, false);      \
+}}}}}}}}
 
 void fov_beam_any_angle(fov_settings_type *settings, void *map, void *source,
                         int source_x, int source_y, unsigned radius,
                         float dx, float dy, float beam_angle) {
 
     /* Note: angle_begin and angle_end are misnomers, since FoV calculation uses slopes, not angles.
-     * Hence, it may not be clear that we implicitly use a tan(x) ~ 4/pi*x approximation
-     * for x in range (0, pi/4) radians, or (0, 45) degrees.  We can (and will) make this much
-     * more precise, which is why the function now uses parameters dx, dy instead of dir_angle.
-     * TODO: make this more precise by not using approximations */
+     * We previously used a tan(x) ~ 4/pi*x approximation * for x in range (0, pi/4) radians, or 45 degrees.
+     * We no longer use this approximation.  Angles and slopes are calculated precisely,
+     * so this function can be used for numerically precise purposes if desired.
+     */
+
     fov_private_data_type data;
-    float start_slope, end_slope, angle_begin, angle_end, dir_angle;
+    float start_slope, end_slope, angle_begin, angle_end, x_start, y_start, x_end, y_end;
 
     data.settings = settings;
     data.map = map;
@@ -560,20 +579,45 @@ void fov_beam_any_angle(fov_settings_type *settings, void *map, void *source,
         return;
     }
 
-    dir_angle = RtoD*atan2(dy, dx);
-    while (dir_angle >= 360.0f) {
-        dir_angle -= 360.0f;
+    beam_angle = 0.5f * DtoR * beam_angle;
+    x_start = cos(beam_angle)*dx + sin(beam_angle)*dy;
+    y_start = cos(beam_angle)*dy - sin(beam_angle)*dx;
+    x_end   = cos(beam_angle)*dx - sin(beam_angle)*dy;
+    y_end   = cos(beam_angle)*dy + sin(beam_angle)*dx;
+
+    if (y_start > 0.0f) {
+        if (x_start > 0.0f) {                      /* octant 1 */               /* octant 2 */
+            angle_begin = ( y_start <  x_start) ? (y_start / x_start)        : (2.0f - x_start / y_start);
+        }
+        else {                                     /* octant 3 */               /* octant 4 */
+            angle_begin = (-x_start <  y_start) ? (2.0f - x_start / y_start) : (4.0f + y_start / x_start);
+        }
+    } else {
+        if (x_start < 0.0f) {                      /* octant 5 */               /* octant 6 */
+            angle_begin = (-y_start < -x_start) ? (4.0f + y_start / x_start) : (6.0f - x_start / y_start);
+        }
+        else {                                     /* octant 7 */               /* octant 8 */
+            angle_begin = ( x_start < -y_start) ? (6.0f - x_start / y_start) : (8.0f + y_start / x_start);
+        }
     }
 
-    while (dir_angle < 0.0f) {
-        dir_angle += 360.0f;
+    if (y_end > 0.0f) {
+        if (x_end > 0.0f) {                  /* octant 1 */           /* octant 2 */
+            angle_end = ( y_end <  x_end) ? (y_end / x_end)        : (2.0f - x_end / y_end);
+        }
+        else {                               /* octant 3 */           /* octant 4 */
+            angle_end = (-x_end <  y_end) ? (2.0f - x_end / y_end) : (4.0f + y_end / x_end);
+        }
+    } else {
+        if (x_end < 0.0f) {                  /* octant 5 */           /* octant 6 */
+            angle_end = (-y_end < -x_end) ? (4.0f + y_end / x_end) : (6.0f - x_end / y_end);
+        }
+        else {                               /* octant 7 */           /* octant 8 */
+            angle_end = ( x_end < -y_end) ? (6.0f - x_end / y_end) : (8.0f + y_end / x_end);
+        }
     }
 
-    /* Calculate the angles as a percentage of 45 degrees */
-    angle_begin = (dir_angle - 0.5*beam_angle) / 45.0f;
-    angle_end = (dir_angle + 0.5*beam_angle) / 45.0f;
-    if (angle_begin < 0.0f) {
-        angle_begin += 8.0f;
+    if (angle_end < angle_begin) {
         angle_end += 8.0f;
     }
 
diff --git a/src/fov/fov.h b/src/fov/fov.h
index 8c3ab0b8c8f8f2aeba4555449fd7d56e35be4aa7..fe614767ff9baef46951cc5c7688dc27d41eaa82 100644
--- a/src/fov/fov.h
+++ b/src/fov/fov.h
@@ -55,12 +55,15 @@ typedef enum {
     FOV_SOUTHEAST
 } fov_direction_type;
 
-/** Values for the shape setting. */
+/** Values for the shape setting.          Distance                       Y (given x, radius r)   Square distance check   */
 typedef enum {
-    FOV_SHAPE_CIRCLE_PRECALCULATE,
-    FOV_SHAPE_SQUARE,
-    FOV_SHAPE_CIRCLE,
-    FOV_SHAPE_OCTAGON
+    FOV_SHAPE_CIRCLE_ROUND,             /* floor(sqrt(x^2 + y^2) + 0.5)   sqrt(r^2 + r - x^2)     x^2 + y^2 <= r^2 + r    */
+    FOV_SHAPE_CIRCLE_FLOOR,             /* floor(sqrt(x^2 + y^2))         sqrt(r^2 + 2*r - x^2)   x^2 + y^2 <= r^2 + 2*r  */
+    FOV_SHAPE_CIRCLE_CEIL,              /* ceil(sqrt(x^2 + y^2))          sqrt(r^2 - x^2)         x^2 + y^2 <= r^2        */
+    FOV_SHAPE_CIRCLE_PLUS1,             /* floor(d + 1 - 1.0/d)           sqrt(r^2 + 1 - x^2)     x^2 + y^2 <= r^2 + 1    */
+    FOV_SHAPE_OCTAGON,                  /* max(x, y) + min(x, y)/2        2*(r - x) + 1                                   */
+    FOV_SHAPE_DIAMOND,                  /* x + y                          r - x                                           */
+    FOV_SHAPE_SQUARE                    /* max(x, y)                      r                                               */
 } fov_shape_type;
 
 /** Values for the corner peek setting. */