diff --git a/game/engines/default/engine/Map.lua b/game/engines/default/engine/Map.lua index a9f0de62f266efe780016ce5570651a52ca81978..1a023587317d8c92aa82edc438f83a89d04c6932 100644 --- a/game/engines/default/engine/Map.lua +++ b/game/engines/default/engine/Map.lua @@ -882,7 +882,7 @@ function _M:addEffect(src, x, y, duration, damtype, dam, radius, dir, angle, ove -- Handle any angle if type(dir) == "table" then - grids = core.fov.beam_any_angle_grids(x, y, radius, dir.delta_x, dir.delta_y, angle, true) + grids = core.fov.beam_any_angle_grids(x, y, radius, angle, dir.source_x or src.x or x, dir.source_y or src.y or y, dir.delta_x, dir.delta_y, true) -- Handle balls elseif dir == 5 then grids = core.fov.circle_grids(x, y, radius, true) @@ -959,7 +959,7 @@ function _M:processEffects() table.insert(todel, i) elseif e.update_fct then if e:update_fct() then - if type(dir) == "table" then e.grids = core.fov.beam_any_angle_grids(e.x, e.y, e.radius, e.dir.delta_x, e.dir.delta_y, e.angle, true) + if type(dir) == "table" then e.grids = core.fov.beam_any_angle_grids(e.x, e.y, e.radius, e.angle, e.dir.source_x or e.src.x or e.x, e.dir.source_y or e.src.y or e.y, e.dir.delta_x, e.dir.delta_y, true) elseif e.dir == 5 then e.grids = core.fov.circle_grids(e.x, e.y, e.radius, true) else e.grids = core.fov.beam_grids(e.x, e.y, e.radius, e.dir, e.angle, true) end if e.particles then diff --git a/game/engines/default/engine/Projectile.lua b/game/engines/default/engine/Projectile.lua index 000ae27da5a6e8f08b9e4b6d2ecb928deab00b61..7bf5ac002a5d530106467598dd53fccd59484a5a 100644 --- a/game/engines/default/engine/Projectile.lua +++ b/game/engines/default/engine/Projectile.lua @@ -259,7 +259,7 @@ function _M:makeProject(src, display, def, do_move, do_act, do_stop) travel_particle_args = display.particle_args, trail_particle = display.trail, src = src, - src_x = def.start_x or src.x, src_y = def.start_y or src.y, + start_x = def.start_x or src.x, start_y = def.start_y or src.y, project = {def=def}, energy = {mod=speed}, tmp_proj = {}, diff --git a/game/engines/default/engine/Target.lua b/game/engines/default/engine/Target.lua index 18e1d91ddee6fb42a886501f1fc257615bf71b4c..e9bbd9bd59b1355d41bada71da9dd9a0a9b70bfa 100644 --- a/game/engines/default/engine/Target.lua +++ b/game/engines/default/engine/Target.lua @@ -73,6 +73,9 @@ function _M:display(dispx, dispy) self.target.x = self.target.x or self.source_actor.x self.target.y = self.target.y or self.source_actor.y + self.target_type.start_x = self.target_type.start_x or self.target_type.x or self.target_type.source_actor and self.target_type.source_actor.x or self.x + self.target_type.start_y = self.target_type.start_y or self.target_type.y or self.target_type.source_actor and self.target_type.source_actor.y or self.y + local ox, oy = self.display_x, self.display_y local sx, sy = game.level.map._map:getScroll() sx = sx + game.level.map.display_x @@ -87,9 +90,9 @@ function _M:display(dispx, dispy) local s = self.sb local l if self.target_type.source_actor.lineFOV then - l = self.target_type.source_actor:lineFOV(self.target.x, self.target.y) + l = self.target_type.source_actor:lineFOV(self.target.x, self.target.y, nil, nil, self.target_type.start_x, self.target_type.start_y) else - l = core.fov.line(self.source_actor.x, self.source_actor.y, self.target.x, self.target.y) + l = core.fov.line(self.target_type.start_x, self.target_type.start_y, self.target.x, self.target.y) end local block_corner = self.target_type.block_path and function(_, bx, by) local b, h, hr = self.target_type:block_path(bx, by, true) ; return b and h and not hr end or function(_, bx, by) return false end @@ -97,8 +100,8 @@ function _M:display(dispx, dispy) l:set_corner_block(block_corner) local lx, ly, blocked_corner_x, blocked_corner_y = l:step() - local stop_x, stop_y = self.source_actor.x, self.source_actor.y - local stop_radius_x, stop_radius_y = self.source_actor.x, self.source_actor.y + local stop_x, stop_y = self.target_type.start_x, self.target_type.start_y + local stop_radius_x, stop_radius_y = self.target_type.start_x, self.target_type.start_y local stopped = false local block, hit, hit_radius @@ -108,7 +111,7 @@ function _M:display(dispx, dispy) hit = true hit_radius = false stopped = true - if self.target_type.min_range and core.fov.distance(self.source_actor.x, self.source_actor.y, lx, ly) < self.target_type.min_range then + if self.target_type.min_range and core.fov.distance(self.target_type.start_x, self.target_type.start_y, lx, ly) < self.target_type.min_range then s = self.sr end if game.level.map:isBound(blocked_corner_x, blocked_corner_y) then @@ -136,7 +139,7 @@ function _M:display(dispx, dispy) end if self.target_type.min_range then -- Check if we should be "red" - if core.fov.distance(self.source_actor.x, self.source_actor.y, lx, ly) < self.target_type.min_range then + if core.fov.distance(self.target_type.start_x, self.target_type.start_y, lx, ly) < self.target_type.min_range then s = self.sr -- Check if we were only "red" because of minimum distance elseif s == self.sr then @@ -199,9 +202,40 @@ function _M:display(dispx, dispy) game.level.map.w, game.level.map.h, self.target_type.cone, - self.target.x - self.source_actor.x, - self.target.y - self.source_actor.y, self.target_type.cone_angle, + self.target_type.start_x, + self.target_type.start_y, + self.target.x - self.target_type.start_x, + self.target.y - self.target_type.start_y, + function(_, px, py) + if self.target_type.block_radius and self.target_type:block_radius(px, py, true) then return true end + end, + function(_, px, py) + if not self.target_type.no_restrict and not game.level.map.remembers(px, py) and not game.level.map.seens(px, py) then + self.syg:toScreen(self.display_x + (px - game.level.map.mx) * self.tile_w * Map.zoom, + self.display_y + (py - game.level.map.my + util.hexOffset(px)) * self.tile_h * Map.zoom, + self.tile_w * Map.zoom, + self.tile_h * Map.zoom) + else + self.sg:toScreen(self.display_x + (px - game.level.map.mx) * self.tile_w * Map.zoom, + self.display_y + (py - game.level.map.my + util.hexOffset(px)) * self.tile_h * Map.zoom, + self.tile_w * Map.zoom, + self.tile_h * Map.zoom) + end + end, + nil) + elseif self.target_type.wall and self.target_type.wall > 0 then + core.fov.calc_wall( + stop_radius_x, + stop_radius_y, + game.level.map.w, + game.level.map.h, + self.target_type.wall, + self.target_type.halfmax_spots, + self.target_type.start_x, + self.target_type.start_y, + self.target.x - self.target_type.start_x, + self.target.y - self.target_type.start_y, function(_, px, py) if self.target_type.block_radius and self.target_type:block_radius(px, py, true) then return true end end, @@ -308,6 +342,14 @@ function _M:getType(t) target_type.cone_angle = t.cone_angle or 55 target_type.selffire = false end + if t.type:find("wall") then + if util.isHex() then + --with a hex grid, a wall should only be defined by the number of spots + t.halfmax_spots = t.halflength + t.halflength = 2*t.halflength + end + target_type.wall = t.halflength + end if t.type:find("bolt") then target_type.stop_block = true elseif t.type:find("beam") then diff --git a/game/engines/default/engine/interface/ActorProject.lua b/game/engines/default/engine/interface/ActorProject.lua index c46ac1807a7653af04b073c7b738f510f883db7b..90c1bd0e073cc0271ed1d8a6239bc35ad8d49fdf 100644 --- a/game/engines/default/engine/interface/ActorProject.lua +++ b/game/engines/default/engine/interface/ActorProject.lua @@ -49,6 +49,8 @@ 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 + typ.start_x = typ.start_x or typ.x or typ.source_actor and typ.source_actor.x or self.x + typ.start_y = typ.start_y or typ.y or typ.source_actor and typ.source_actor.y or self.x local grids = {} local function addGrid(x, y) @@ -56,16 +58,14 @@ function _M:project(t, x, y, damtype, dam, particles) grids[x][y] = true end - local srcx, srcy = t.x or self.x, t.y or self.y - -- Stop at range or on block - local stop_x, stop_y = srcx, srcy - local stop_radius_x, stop_radius_y = srcx, srcy + local stop_x, stop_y = typ.start_x, typ.start_y + local stop_radius_x, stop_radius_y = typ.start_x, typ.start_y local l, is_corner_blocked if typ.source_actor.lineFOV then - l = typ.source_actor:lineFOV(x, y, nil, nil, srcx, srcy) + l = typ.source_actor:lineFOV(x, y, nil, nil, typ.start_x, typ.start_y) else - l = core.fov.line(srcx, srcy, x, y) + l = core.fov.line(typ.start_x, typ.start_y, x, y) end local block_corner = typ.block_path and function(_, bx, by) local b, h, hr = typ:block_path(bx, by, true) ; return b and h and not hr end or function(_, bx, by) return false end @@ -134,9 +134,11 @@ function _M:project(t, x, y, damtype, dam, particles) game.level.map.w, game.level.map.h, typ.cone, - x - self.x, - y - self.y, typ.cone_angle, + typ.start_x, + typ.start_y, + x - typ.start_x, + y - typ.start_y, function(_, px, py) if typ.block_radius and typ:block_radius(px, py) then return true end end, @@ -145,13 +147,32 @@ function _M:project(t, x, y, damtype, dam, particles) end, nil) addGrid(stop_x, stop_y) + elseif typ.wall and typ.wall > 0 then + core.fov.calc_wall( + stop_radius_x, + stop_radius_y, + game.level.map.w, + game.level.map.h, + typ.wall, + typ.halfmax_spots, + typ.start_x, + typ.start_y, + x - typ.start_x, + y - typ.start_y, + function(_, px, py) + if typ.block_radius and typ:block_radius(px, py) then return true end + end, + function(_, px, py) + addGrid(px, py) + end, + nil) else -- Deal damage: single addGrid(stop_x, stop_y) end -- Check for minimum range - if typ.min_range and core.fov.distance(self.x, self.y, stop_x, stop_y) < typ.min_range then + if typ.min_range and core.fov.distance(typ.start_x, typ.start_y, stop_x, stop_y) < typ.min_range then return end @@ -198,17 +219,17 @@ end function _M:canProject(t, x, y) local typ = Target:getType(t) typ.source_actor = self - typ.start_x = self.x - typ.start_y = self.y + typ.start_x = typ.start_x or typ.x or typ.source_actor and typ.source_actor.x or self.x + typ.start_y = typ.start_y or typ.y or typ.source_actor and typ.source_actor.y or self.x -- Stop at range or on block - local stop_x, stop_y = self.x, self.y - local stop_radius_x, stop_radius_y = self.x, self.y + local stop_x, stop_y = typ.start_x, typ.start_y + local stop_radius_x, stop_radius_y = typ.start_x, typ.start_y local l, is_corner_blocked if typ.source_actor.lineFOV then - l = typ.source_actor:lineFOV(x, y) + l = typ.source_actor:lineFOV(x, y, nil, nil, typ.start_x, typ.start_y) else - l = core.fov.line(self.x, self.y, x, y) + l = core.fov.line(typ.start_x, typ.start_y, x, y) end local block_corner = typ.block_path and function(_, bx, by) local b, h, hr = typ:block_path(bx, by, true) ; return b and h and not hr end or function(_, bx, by) return false end @@ -243,7 +264,7 @@ function _M:canProject(t, x, y) end -- Check for minimum range - if typ.min_range and core.fov.distance(self.x, self.y, stop_x, stop_y) < typ.min_range then + if typ.min_range and core.fov.distance(typ.start_x, typ.start_y, stop_x, stop_y) < typ.min_range then return end @@ -270,20 +291,20 @@ 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 - typ.start_x = t.x or self.x - typ.start_y = t.y or self.y + typ.start_x = typ.start_x or typ.x or typ.source_actor and typ.source_actor.x or self.x + typ.start_y = typ.start_y or typ.y or typ.source_actor and typ.source_actor.y or self.x if self.lineFOV then - typ.line_function = self:lineFOV(x, y, nil, nil, t.x, t.y) + typ.line_function = self:lineFOV(x, y, nil, nil, typ.start_x, typ.start_y) else - typ.line_function = core.fov.line(t.x or self.x, t.y or self.y, x, y) + typ.line_function = core.fov.line(typ.start_x, typ.start_y, x, y) end local block_corner = typ.block_path and function(_, bx, by) local b, h, hr = typ:block_path(bx, by, true) ; return b and h and not hr end or function(_, bx, by) return false end typ.line_function:set_corner_block(block_corner) - 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", t.x or self.x, t.y or self.y) + local proj = require(self.projectile_class):makeProject(self, t.display, {x=x, y=y, start_x=typ.start_x, start_y=typ.start_y, damtype=damtype, tg=t, typ=typ, dam=dam, particles=particles}) + game.zone:addEntity(game.level, proj, "projectile", typ.start_x, typ.start_y) end -- @param typ a target type table @@ -386,9 +407,11 @@ function _M:projectDoStop(typ, tg, damtype, dam, particles, lx, ly, tmp, rx, ry) game.level.map.w, game.level.map.h, typ.cone, - lx - typ.source_actor.x, - ly - typ.source_actor.y, typ.cone_angle, + typ.start_x, + typ.start_y, + lx - typ.start_x, + ly - typ.start_y, function(_, px, py) if typ.block_radius and typ:block_radius(px, py) then return true end end, @@ -398,6 +421,26 @@ function _M:projectDoStop(typ, tg, damtype, dam, particles, lx, ly, tmp, rx, ry) end, nil) addGrid(rx, ry) + elseif typ.wall and typ.wall > 0 then + core.fov.calc_wall( + rx, + rx, + game.level.map.w, + game.level.map.h, + typ.wall, + typ.halfmax_spots, + typ.start_x, + typ.start_y, + lx - typ.start_x, + ly - typ.start_y, + function(_, px, py) + if typ.block_radius and typ:block_radius(px, py) then return true end + end, + function(_, px, py) + -- Deal damage: wall + addGrid(px, py) + end, + nil) else -- Deal damage: single addGrid(lx, ly) diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua index e797858b48ca54b7d015955d4ba19aa2cc8c8c93..09e8d87ea4cb458384f15c4b9f8fc9f5ec9d079a 100644 --- a/game/engines/default/engine/utils.lua +++ b/game/engines/default/engine/utils.lua @@ -1347,17 +1347,125 @@ function util.loadfilemods(file, env) return prev end +-- if these functions are ever desired elsewhere, don't be shy to make these accessible beyond utils.lua +local function deltaCoordsToReal(dx, dy, source_x, source_y) + if util.isHex() then + dy = dy + (math.floor(math.abs(dx) + 0.5) % 2) * (0.5 - math.floor(source_x) % 2) + dx = dx * math.sqrt(3) / 2 + end + return dx, dy +end + +local function deltaRealToCoords(dx, dy, source_x, source_y) + if util.isHex() then + dx = dx < 0 and math.ceil(dx * 2 / math.sqrt(3) - 0.5) or math.floor(dx * 2 / math.sqrt(3) + 0.5) + dy = dy - (math.floor(math.abs(dx) + 0.5) % 2) * (0.5 - math.floor(source_x) % 2) + end + return source_x + dx, source_y + dy +end + +function core.fov.calc_wall(x, y, w, h, halflength, halfmax_spots, source_x, source_y, delta_x, delta_y, block, apply) + apply(_, x, y) + delta_x, delta_y = deltaCoordsToReal(delta_x, delta_y, source_x, source_y) + + local angle = math.atan2(delta_y, delta_x) + math.pi / 2 + + local dx, dy = math.cos(angle) * halflength, math.sin(angle) * halflength + local adx, ady = math.abs(dx), math.abs(dy) + + local x1, y1 = deltaRealToCoords( dx, dy, x, y) + local x2, y2 = deltaRealToCoords(-dx, -dy, x, y) + + local spots = 1 + local wall_block_corner = function(_, bx, by) + if halfmax_spots and spots > halfmax_spots or math.floor(core.fov.distance(x2, y2, bx, by, true) - 0.25) > 2*halflength then return true end + apply(_, bx, by) + spots = spots + 1 + return block(_, bx, by) + end + + local l = core.fov.line(x+0.5, y+0.5, x1+0.5, y1+0.5, function(_, bx, by) return true end) + l:set_corner_block(wall_block_corner) + -- use the correct tangent (not approximate) and round corner tie-breakers toward the player (via wiggles!) + if adx < ady then + l:change_step(dx/ady, dy/ady) + if delta_y < 0 then l:wiggle(true) else l:wiggle() end + else + l:change_step(dx/adx, dy/adx) + if delta_x < 0 then l:wiggle(true) else l:wiggle() end + end + while true do + local lx, ly, is_corner_blocked = l:step(true) + if not lx or is_corner_blocked or halfmax_spots and spots > halfmax_spots or math.floor(core.fov.distance(x2, y2, lx, ly, true) + 0.25) > 2*halflength then break end + apply(_, lx, ly) + spots = spots + 1 + if block(_, lx, ly) then break end + end + + spots = 1 + wall_block_corner = function(_, bx, by) + if halfmax_spots and spots > halfmax_spots or math.floor(core.fov.distance(x1, y1, bx, by, true) - 0.25) > 2*halflength then return true end + apply(_, bx, by) + spots = spots + 1 + return block(_, bx, by) + end + + local l = core.fov.line(x+0.5, y+0.5, x2+0.5, y2+0.5, function(_, bx, by) return true end) + l:set_corner_block(wall_block_corner) + -- use the correct tangent (not approximate) and round corner tie-breakers toward the player (via wiggles!) + if adx < ady then + l:change_step(-dx/ady, -dy/ady) + if delta_y < 0 then l:wiggle(true) else l:wiggle() end + else + l:change_step(-dx/adx, -dy/adx) + if delta_x < 0 then l:wiggle(true) else l:wiggle() end + end + while true do + local lx, ly, is_corner_blocked = l:step(true) + if not lx or is_corner_blocked or halfmax_spots and spots > halfmax_spots or math.floor(core.fov.distance(x1, y1, lx, ly, true) + 0.25) > 2*halflength then break end + apply(_, lx, ly) + spots = spots + 1 + if block(_, lx, ly) then break end + end +end + +function core.fov.wall_grids(x, y, halflength, halfmax_spots, source_x, source_y, delta_x, delta_y, block) + if not x or not y then return {} end + local grids = {} + core.fov.calc_wall(x, y, game.level.map.w, game.level.map.h, halflength, halfmax_spots, source_x, source_y, delta_x, delta_y, + function(_, lx, ly) + if type(block) == "function" then + return block(_, lx, ly) + elseif block and game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") then return true end + end, + function(_, lx, ly) + if not grids[lx] then grids[lx] = {} end + grids[lx][ly] = true + end, + nil) + + -- point of origin + if not grids[x] then grids[x] = {} end + grids[x][y] = true + + return grids +end + function core.fov.circle_grids(x, y, radius, block) if not x or not y then return {} end if radius == 0 then return {[x]={[y]=true}} end local grids = {} - core.fov.calc_circle(x, y, game.level.map.w, game.level.map.h, radius, function(_, lx, ly) - if block and game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") then return true end - end, - function(_, lx, ly) - if not grids[lx] then grids[lx] = {} end - grids[lx][ly] = true - end, nil) + core.fov.calc_circle(x, y, game.level.map.w, game.level.map.h, radius, + function(_, lx, ly) + if type(block) == "function" then + return block(_, lx, ly) + elseif block and game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") then return true end + end, + function(_, lx, ly) + if not grids[lx] then grids[lx] = {} end + grids[lx][ly] = true + end, + nil) -- point of origin if not grids[x] then grids[x] = {} end @@ -1370,13 +1478,17 @@ function core.fov.beam_grids(x, y, radius, dir, angle, block) if not x or not y then return {} end if radius == 0 then return {[x]={[y]=true}} end local grids = {} - core.fov.calc_beam(x, y, game.level.map.w, game.level.map.h, radius, dir, angle, function(_, lx, ly) - if block and game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") then return true end - end, - function(_, lx, ly) - if not grids[lx] then grids[lx] = {} end - grids[lx][ly] = true - end, nil) + core.fov.calc_beam(x, y, game.level.map.w, game.level.map.h, radius, dir, angle, + function(_, lx, ly) + if type(block) == "function" then + return block(_, lx, ly) + elseif block and game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") then return true end + end, + function(_, lx, ly) + if not grids[lx] then grids[lx] = {} end + grids[lx][ly] = true + end, + nil) -- point of origin if not grids[x] then grids[x] = {} end @@ -1385,17 +1497,21 @@ function core.fov.beam_grids(x, y, radius, dir, angle, block) return grids end -function core.fov.beam_any_angle_grids(x, y, radius, delta_x, delta_y, angle, block) +function core.fov.beam_any_angle_grids(x, y, radius, angle, source_x, source_y, delta_x, delta_y, block) if not x or not y then return {} end if radius == 0 then return {[x]={[y]=true}} end local grids = {} - core.fov.calc_beam_any_angle(x, y, game.level.map.w, game.level.map.h, radius, delta_x, delta_y, angle, function(_, lx, ly) - if block and game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") then return true end - end, - function(_, lx, ly) - if not grids[lx] then grids[lx] = {} end - grids[lx][ly] = true - end, nil) + core.fov.calc_beam_any_angle(x, y, game.level.map.w, game.level.map.h, radius, angle, source_x, source_y, delta_x, delta_y, + function(_, lx, ly) + if type(block) == "function" then + return block(_, lx, ly) + elseif block and game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move") then return true end + end, + function(_, lx, ly) + if not grids[lx] then grids[lx] = {} end + grids[lx][ly] = true + end, + nil) -- point of origin if not grids[x] then grids[x] = {} end diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 5b89f674384afb66082b8ac02e70b0461d367f7f..ca7a356a4e8eed2c0e079a42bb7773762e641ef2 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -3508,7 +3508,7 @@ function _M:on_projectile_target(x, y, p) if self:isTalentActive(self.T_GLOOM) and self:knowTalent(self.T_SANCTUARY) then -- mark temp table with the sanctuary damage change (to lower using tmp from DamageType:project) local t = self:getTalentFromId(self.T_GLOOM) - if core.fov.distance(self.x, self.y, p.src_x, p.src_y) > self:getTalentRange(t) then + if core.fov.distance(self.x, self.y, p.start_x, p.start_y) > self:getTalentRange(t) then t = self:getTalentFromId(self.T_SANCTUARY) p.tmp_proj.sanctuaryDamageChange = t.getDamageChange(self, t) print("Sanctuary marking reduced damage on projectile:", p.tmp_proj.sanctuaryDamageChange) diff --git a/game/modules/tome/data/general/objects/mindstars.lua b/game/modules/tome/data/general/objects/mindstars.lua index bcb8ad6b73d2435d602dc37c8fa5eb674216e3ea..f9b39c3fcd56652bb284693e5c63a63a48d8de4c 100644 --- a/game/modules/tome/data/general/objects/mindstars.lua +++ b/game/modules/tome/data/general/objects/mindstars.lua @@ -36,7 +36,7 @@ newEntity{ sound = {"actions/melee", pitch=0.6, vol=1.2}, sound_miss = {"actions/melee", pitch=0.6, vol=1.2}, damtype = resolvers.rngtable{DamageType.NATURE, DamageType.MIND}, }, - desc = [[Mindstars are natural products. Natural gems covered in living matter, they are used to focus the mental powers of all nature defenders.]], + desc = [[Mindstars are natural products. Natural gems covered in living matter, they are used to focus the mental powers of all nature defenders and psionics.]], egos = "/data/general/objects/egos/mindstars.lua", egos_chance = { prefix=resolvers.mbonus(40, 5), suffix=resolvers.mbonus(40, 5) }, } diff --git a/game/modules/tome/data/talents/gifts/cold-drake.lua b/game/modules/tome/data/talents/gifts/cold-drake.lua index 834bd6f188c90bce0c9797c65943e61b11663786..118f5fcdd8fa68e0fb54ee350479ba7e3d17777b 100644 --- a/game/modules/tome/data/talents/gifts/cold-drake.lua +++ b/game/modules/tome/data/talents/gifts/cold-drake.lua @@ -93,10 +93,14 @@ newTalent{ on_learn = function(self, t) self.resists[DamageType.COLD] = (self.resists[DamageType.COLD] or 0) + 1 end, on_unlearn = function(self, t) self.resists[DamageType.COLD] = (self.resists[DamageType.COLD] or 0) - 1 end, action = function(self, t) - local tg = {type="bolt", range=self:getTalentRange(t), nolock=true, talent=t} + local halflength = 1 + math.floor(self:getTalentLevel(t) / 2) + local block = function(_, lx, ly) + return game.level.map:checkAllEntities(lx, ly, "block_move") + end + local tg = {type="wall", range=self:getTalentRange(t), halflength=halflength, talent=t, halfmax_spots=halflength+1, block_radius=block} local x, y = self:getTarget(tg) if not x or not y then return nil end - local _ _, x, y = self:canProject(tg, x, y) + local _ _, _, _, x, y = self:canProject(tg, x, y) if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then return nil end local addwall = function(x, y) @@ -132,51 +136,7 @@ newTalent{ game.level.map(x, y, Map.TRAP, e) end - local size = 1 + math.floor(self:getTalentLevel(t) / 2) - local angle = math.atan2(y - self.y, x - self.x) + math.pi / 2 - local x1, y1 = x + math.cos(angle) * size, y + math.sin(angle) * size - local x2, y2 = x - math.cos(angle) * size, y - math.sin(angle) * size - - local dx1, dy1 = math.abs(x1 - x), math.abs(y1 - y) - local dx2, dy2 = math.abs(x2 - x), math.abs(y2 - y) - local block_corner = function(_, bx, by) - if game.level.map:checkAllEntities(bx, by, "block_move") then return true - else addwall(bx, by) ; return false end - end - - local l = core.fov.line(x, y, x1, y1, function(_, bx, by) return true end) - l:set_corner_block(block_corner) - -- use the correct tangent (not approximate) and round corner tie-breakers toward the player (via wiggles!) - if dx1 < dy1 then - l:change_step((x1-x)/dy1, (y1-y)/dy1) - if y < self.y then l:wiggle(true) else l:wiggle() end - else - l:change_step((x1-x)/dx1, (y1-y)/dx1) - if x < self.x then l:wiggle(true) else l:wiggle() end - end - while true do - local lx, ly, is_corner_blocked = l:step() - if not lx or is_corner_blocked or game.level.map:checkAllEntities(lx, ly, "block_move") then break end - addwall(lx, ly) - end - - local l = core.fov.line(x, y, x2, y2, function(_, bx, by) return true end) - l:set_corner_block(block_corner) - -- use the correct tangent (not approximate) and round corner tie-breakers toward the player (via wiggles!) - if dx2 < dy2 then - l:change_step((x2-x)/dy2, (y2-y)/dy2) - if y < self.y then l:wiggle(true) else l:wiggle() end - else - l:change_step((x2-x)/dx2, (y2-y)/dx2) - if x < self.x then l:wiggle(true) else l:wiggle() end - end - while true do - local lx, ly, is_corner_blocked = l:step() - if not lx or is_corner_blocked or game.level.map:checkAllEntities(lx, ly, "block_move") then break end - addwall(lx, ly) - end - - if not game.level.map:checkAllEntities(x, y, "block_move") then addwall(x, y) end + self:project(tg, x, y, addwall) game.level.map:redisplay() return true diff --git a/game/modules/tome/data/talents/gifts/summon-distance.lua b/game/modules/tome/data/talents/gifts/summon-distance.lua index 1324037d5b3b123a8fd1a2cb384bdc1d4454c1b8..93b66bf17c8e162ca489419abecd15a7fe595172 100644 --- a/game/modules/tome/data/talents/gifts/summon-distance.lua +++ b/game/modules/tome/data/talents/gifts/summon-distance.lua @@ -90,7 +90,7 @@ newTalent{ local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.ACID, self:combatTalentStatDamage(t, "wil", 30, 430)) + self:project(tg, x, y, DamageType.ACID, self:mindCrit(self:combatTalentStatDamage(t, "wil", 30, 430))) game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_acid", {radius=tg.radius, tx=x-self.x, ty=y-self.y}) game:playSoundNear(self, "talents/breath") return true @@ -121,7 +121,7 @@ newTalent{ local x, y = self:getTarget(tg) if not x or not y then return nil end local dam = self:combatTalentStatDamage(t, "wil", 30, 500) - self:project(tg, x, y, DamageType.LIGHTNING, rng.avg(dam / 3, dam, 3)) + self:project(tg, x, y, DamageType.LIGHTNING, self:mindCrit(rng.avg(dam / 3, dam, 3))) game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_lightning", {radius=tg.radius, tx=x-self.x, ty=y-self.y}) game:playSoundNear(self, "talents/lightning") return true @@ -155,7 +155,7 @@ newTalent{ local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.POISON, self:combatTalentStatDamage(t, "wil", 30, 460)) + self:project(tg, x, y, DamageType.POISON, self:mindCrit(self:combatTalentStatDamage(t, "wil", 30, 460))) game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_slime", {radius=tg.radius, tx=x-self.x, ty=y-self.y}) game:playSoundNear(self, "talents/breath") return true diff --git a/src/fov.c b/src/fov.c index fae425e04a381fe4318c65d4cf55616290751786..ff51c7702fd0f5a52362143f47a7641cc6239d8f 100644 --- a/src/fov.c +++ b/src/fov.c @@ -36,6 +36,9 @@ #include <math.h> #include <time.h> +#define SQRT_3 1.73205080756887729353 +#define INV_SQRT_3 0.577350269189625764509 +#define SQRT_3_2 0.866025403784438646764 #define INV_SQRT_3_2 1.15470053837925152902 /****************************************************************** @@ -344,13 +347,15 @@ static int lua_fov_calc_beam_any_angle(lua_State *L) int w = luaL_checknumber(L, 3); int h = luaL_checknumber(L, 4); int radius = luaL_checknumber(L, 5); - float dx = luaL_checknumber(L, 6); - float dy = luaL_checknumber(L, 7); - float beam_angle = luaL_checknumber(L, 8); + float beam_angle = luaL_checknumber(L, 6); + int sx = luaL_checknumber(L, 7); + int sy = luaL_checknumber(L, 8); + float dx = luaL_checknumber(L, 9); + float dy = luaL_checknumber(L, 10); struct lua_fov fov; - if (lua_isuserdata(L, 11)) + if (lua_isuserdata(L, 13)) { - fov.cache = (struct lua_fovcache*)auxiliar_checkclass(L, "fov{cache}", 10); + fov.cache = (struct lua_fovcache*)auxiliar_checkclass(L, "fov{cache}", 12); fov.cache_ref = luaL_ref(L, LUA_REGISTRYINDEX); } else @@ -374,7 +379,7 @@ static int lua_fov_calc_beam_any_angle(lua_State *L) 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); + fov_beam_any_angle(&(fov.fov_settings), &fov, NULL, x, y, radius, sx, sy, dx, dy, beam_angle); map_seen(&fov, x, y, 0, 0, radius, NULL); fov_settings_free(&(fov.fov_settings)); @@ -1058,9 +1063,18 @@ static int lua_hex_fov_line_change_step(lua_State *L) hex_fov_line_data *line = &(lua_line->line); float step_x = lua_tonumber(L, 2); float step_y = lua_tonumber(L, 3); + float ax = fabs(step_x); + float ay = fabs(step_y); + + // lines are a little weird in hex, so lets enforce unit step sizes + if (SQRT_3*ay < ax) { + line->step_x = SQRT_3_2 * step_x / ax; + line->step_y = SQRT_3_2 * step_y / ax; + } else { + line->step_x = step_x / (INV_SQRT_3*ax + ay); + line->step_y = step_y / (INV_SQRT_3*ax + ay); + } - line->step_x = step_x; - line->step_y = step_y; return 0; } diff --git a/src/fov/fov.c b/src/fov/fov.c index 64169ad0423d64f7fa01f7058cf61ee298f39286..2c807a78e3fa087081b0c644a276910f522622a3 100644 --- a/src/fov/fov.c +++ b/src/fov/fov.c @@ -716,7 +716,7 @@ void fov_beam(fov_settings_type *settings, void *map, void *source, }}}}}}}} void fov_beam_any_angle(fov_settings_type *settings, void *map, void *source, - int source_x, int source_y, unsigned radius, + int source_x, int source_y, unsigned radius, int sx, int sy, float dx, float dy, float beam_angle) { /* Note: angle_begin and angle_end are misnomers, since FoV calculation uses slopes, not angles. @@ -749,7 +749,7 @@ void fov_beam_any_angle(fov_settings_type *settings, void *map, void *source, /* time for some slightly odd conventions. We're assuming that dx and dy are still in coordinate space so * that "source_x + dx" gives the target tile coordinate. dx, dy are floats, so we have sub-tile resolution. * We will then calculate the "real space" x's and y's to allow beam-casting at any angle. */ - dy += (float)(((int)(abs(dx) + 0.5f)) & 1) * (0.5f - (float)(source_x & 1)); + dy += (float)(((int)(abs(dx) + 0.5f)) & 1) * (0.5f - (float)(sx & 1)); dx *= SQRT_3_2; } diff --git a/src/fov/fov.h b/src/fov/fov.h index c074e4a970db94f7faa43514c15e408221fb3256..39d0810db491376bdef4139e4a1d83d55eef4a88 100644 --- a/src/fov/fov.h +++ b/src/fov/fov.h @@ -343,7 +343,7 @@ void fov_beam(fov_settings_type *settings, void *map, void *source, * \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, + int source_x, int source_y, unsigned radius, int sx, int sy, float dx, float dy, float beam_angle );