Index: src/fov/fov.h =================================================================== --- src/fov/fov.h (revision 2889) +++ src/fov/fov.h (working copy) @@ -238,6 +238,28 @@ fov_direction_type direction, float angle ); +/** + * Calculate a field of view from source at (x,y), pointing + * in the given direction (in degrees) and with the given angle. The larger + * the angle, the wider, "less focused" the beam. Each side of the + * line pointing in the direction from the source will be half the + * angle given such that the angle specified will be represented on + * the raster. + * + * \param settings Pointer to data structure containing settings. + * \param map Pointer to map data structure to be passed to callbacks. + * \param source Pointer to data structure holding source of light. + * \param source_x x-axis coordinate from which to start. + * \param source_y y-axis coordinate from which to start. + * \param radius Euclidean distance from (x,y) after which to stop. + * \param dir_angle Direction angle, in degrees. + * \param beam_angle The angle at the base of the beam of light, in degrees. + */ +void fov_beam_any_angle(fov_settings_type *settings, void *map, void *source, + int source_x, int source_y, unsigned radius, + float dir_angle, float beam_angle +); + #ifdef __cplusplus } /* extern "C" */ #endif Index: src/fov/fov.c =================================================================== --- src/fov/fov.c (revision 2889) +++ src/fov/fov.c (working copy) @@ -435,3 +435,131 @@ BEAM_DIRECTION_DIAG(FOV_SOUTHEAST, ppy, ppn, pmy, pmn, mpn, mpy, mmn, mmy); BEAM_DIRECTION_DIAG(FOV_SOUTHWEST, pmy, mpn, ppy, mmn, ppn, mmy, pmn, mpy); } + +#define BEAM_ANY_DIRECTION(offset, p1, p2, p3, p4, p5, p6, p7, p8) \ +angle_begin -= offset; \ +angle_end -= offset; \ +start_slope = angle_begin; \ +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) { \ +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 > 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 > 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 > 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 > 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 > 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 > FLT_EPSILON) { \ +start_slope = betweenf(8.0f - angle_end, 0.0f, 1.0f); \ +fov_octant_##p8(&data, 1, start_slope, 1.0f, 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; \ +fov_octant_##p1(&data, 1, start_slope, end_slope, true, true); \ +\ +if (angle_end - 1.0 > 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 > 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 > 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 > 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 > 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 > 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 > FLT_EPSILON) { \ +end_slope = betweenf(angle_end - 7.0f, 0.0f, 1.0f); \ +fov_octant_##p8(&data, 1, 0.0f, end_slope, false, false); \ +}}}}}}} + +void fov_beam_any_angle(fov_settings_type *settings, void *map, void *source, + int source_x, int source_y, unsigned radius, + float dir_angle, float beam_angle) { + + fov_private_data_type data; + float start_slope, end_slope, angle_begin, angle_end; + + data.settings = settings; + data.map = map; + data.source = source; + data.source_x = source_x; + data.source_y = source_y; + data.radius = radius; + + if (beam_angle <= 0.0f) { + return; + } else if (beam_angle >= 360.0f) { + _fov_circle(&data); + return; + } + + while (dir_angle >= 360.0f) { + dir_angle -= 360.0f; + } + + while (dir_angle < 0.0f) { + dir_angle += 360.0f; + } + + /* 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; + angle_end += 8.0f; + } + + if (1.0f - angle_begin > FLT_EPSILON) { + BEAM_ANY_DIRECTION(0.0f, ppn, ppy, pmy, mpn, mmn, mmy, mpy, pmn); + } else if (2.0f - angle_begin > FLT_EPSILON) { + BEAM_ANY_DIRECTION_DIAG(1.0f, ppy, pmy, mpn, mmn, mmy, mpy, pmn, ppn); + } else if (3.0f - angle_begin > FLT_EPSILON) { + BEAM_ANY_DIRECTION(2.0f, pmy, mpn, mmn, mmy, mpy, pmn, ppn, ppy); + } else if (4.0f - angle_begin > FLT_EPSILON) { + BEAM_ANY_DIRECTION_DIAG(3.0f, mpn, mmn, mmy, mpy, pmn, ppn, ppy, pmy); + } else if (5.0f - angle_begin > FLT_EPSILON) { + BEAM_ANY_DIRECTION(4.0f, mmn, mmy, mpy, pmn, ppn, ppy, pmy, mpn); + } else if (6.0f - angle_begin > FLT_EPSILON) { + BEAM_ANY_DIRECTION_DIAG(5.0f, mmy, mpy, pmn, ppn, ppy, pmy, mpn, mmn); + } else if (7.0f - angle_begin > FLT_EPSILON) { + BEAM_ANY_DIRECTION(6.0f, mpy, pmn, ppn, ppy, pmy, mpn, mmn, mmy); + } else if (8.0f - angle_begin > FLT_EPSILON) { + BEAM_ANY_DIRECTION_DIAG(7.0f, pmn, ppn, ppy, pmy, mpn, mmn, mmy, mpy); + } +} Index: src/fov.c =================================================================== --- src/fov.c (revision 2889) +++ src/fov.c (working copy) @@ -195,6 +195,48 @@ return 0; } +static int lua_fov_calc_beam_any_angle(lua_State *L) +{ + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + int w = luaL_checknumber(L, 3); + int h = luaL_checknumber(L, 4); + int radius = luaL_checknumber(L, 5); + float dir_angle = luaL_checknumber(L, 6); + float beam_angle = luaL_checknumber(L, 7); + struct lua_fov fov; + if (lua_isuserdata(L, 10)) + { + fov.cache = (struct lua_fovcache*)auxiliar_checkclass(L, "fov{cache}", 10); + fov.cache_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + else + { + lua_pop(L, 1); + fov.cache_ref = LUA_NOREF; + fov.cache = NULL; + } + fov.L = L; + fov.apply_ref = luaL_ref(L, LUA_REGISTRYINDEX); + fov.opaque_ref = luaL_ref(L, LUA_REGISTRYINDEX); + fov.cache = NULL; + fov.w = w; + fov.h = h; + + fov_settings_init(&(fov.fov_settings)); + fov_settings_set_opacity_test_function(&(fov.fov_settings), map_opaque); + fov_settings_set_apply_lighting_function(&(fov.fov_settings), map_seen); + fov_beam_any_angle(&(fov.fov_settings), &fov, NULL, x, y, radius+1, dir_angle, beam_angle); + map_seen(&fov, x, y, 0, 0, radius, NULL); + fov_settings_free(&(fov.fov_settings)); + + luaL_unref(L, LUA_REGISTRYINDEX, fov.apply_ref); + luaL_unref(L, LUA_REGISTRYINDEX, fov.opaque_ref); + if (fov.cache_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, fov.cache_ref); + + return 0; +} + static int lua_distance(lua_State *L) { double x1 = luaL_checknumber(L, 1); @@ -436,6 +478,7 @@ {"calc_default_fov", lua_fov_calc_default_fov}, {"calc_circle", lua_fov_calc_circle}, {"calc_beam", lua_fov_calc_beam}, + {"calc_beam_any_angle", lua_fov_calc_beam_any_angle}, {NULL, NULL}, }; Index: game/engines/default/engine/Target.lua =================================================================== --- game/engines/default/engine/Target.lua (revision 2889) +++ game/engines/default/engine/Target.lua (working copy) @@ -129,13 +129,14 @@ end, nil) elseif self.target_type.cone and self.target_type.cone > 0 then - core.fov.calc_beam( + local dir_angle = math.deg(math.atan2(self.target.y - self.source_actor.y, self.target.x - self.source_actor.x)) + core.fov.calc_beam_any_angle( stop_radius_x, stop_radius_y, game.level.map.w, game.level.map.h, self.target_type.cone, - initial_dir, + dir_angle, self.target_type.cone_angle, function(_, px, py) if self.target_type.block_radius and self.target_type:block_radius(px, py) then return true end Index: game/engines/default/engine/interface/ActorProject.lua =================================================================== --- game/engines/default/engine/interface/ActorProject.lua (revision 2889) +++ game/engines/default/engine/interface/ActorProject.lua (working copy) @@ -99,13 +99,14 @@ nil) addGrid(stop_x, stop_y) elseif typ.cone and typ.cone > 0 then - core.fov.calc_beam( + local dir_angle = math.deg(math.atan2(y - self.y, x - self.x)) + core.fov.calc_beam_any_angle( stop_radius_x, stop_radius_y, game.level.map.w, game.level.map.h, typ.cone, - initial_dir, + dir_angle, typ.cone_angle, function(_, px, py) if typ.block_radius and typ:block_radius(px, py) then return true end @@ -307,7 +308,8 @@ addGrid(rx, ry) elseif typ.cone and typ.cone > 0 then local initial_dir = lx and util.getDir(lx, ly, x, y) or 5 - core.fov.calc_beam( + local dir_angle = math.deg(math.atan2(ly - typ.source_actor.y, lx - typ.source_actor.x)) + core.fov.calc_beam_any_angle( rx, ry, game.level.map.w, Index: game/modules/tome/data/talents/divine/light.lua =================================================================== --- game/modules/tome/data/talents/divine/light.lua (revision 2889) +++ game/modules/tome/data/talents/divine/light.lua (working copy) @@ -57,6 +57,7 @@ getHeal = function(self, t) return self:combatTalentSpellDamage(t, 4, 40) end, getDuration = function(self, t) return self:getTalentLevel(t) + 2 end, action = function(self, t) + local tg = self:getTalentTarget(t) self:project(tg, self.x, self.y, DamageType.LITE, 1) -- Add a lasting map effect game.level.map:addEffect(self,