63 lines
1.8 KiB
Lua
63 lines
1.8 KiB
Lua
-- Recursive shadowcasting over eight octants. Returns a set of visible map
|
|
-- indices for a given origin and radius.
|
|
|
|
local Fov = {}
|
|
|
|
local mult = {
|
|
{ 1, 0, 0, -1, -1, 0, 0, 1 },
|
|
{ 0, 1, -1, 0, 0, -1, 1, 0 },
|
|
{ 0, 1, 1, 0, 0, -1, -1, 0 },
|
|
{ 1, 0, 0, 1, -1, 0, 0, -1 },
|
|
}
|
|
|
|
local function castLight(map, cx, cy, row, start, finish, radius, xx, xy, yx, yy, visible)
|
|
if start < finish then return end
|
|
local radiusSq = radius * radius
|
|
local newStart = 0.0
|
|
for j = row, radius do
|
|
local dx, dy = -j - 1, -j
|
|
local blocked = false
|
|
while dx <= 0 do
|
|
dx = dx + 1
|
|
local mapX = cx + dx * xx + dy * xy
|
|
local mapY = cy + dx * yx + dy * yy
|
|
local leftSlope = (dx - 0.5) / (dy + 0.5)
|
|
local rightSlope = (dx + 0.5) / (dy - 0.5)
|
|
if start < rightSlope then
|
|
-- cell not yet inside the current view; keep scanning
|
|
elseif finish > leftSlope then
|
|
break
|
|
else
|
|
if dx * dx + dy * dy <= radiusSq and map:inBounds(mapX, mapY) then
|
|
visible[map:index(mapX, mapY)] = true
|
|
end
|
|
if blocked then
|
|
if map:blocksSight(mapX, mapY) then
|
|
newStart = rightSlope
|
|
else
|
|
blocked = false
|
|
start = newStart
|
|
end
|
|
elseif map:blocksSight(mapX, mapY) and j < radius then
|
|
blocked = true
|
|
castLight(map, cx, cy, j + 1, start, leftSlope, radius, xx, xy, yx, yy, visible)
|
|
newStart = rightSlope
|
|
end
|
|
end
|
|
end
|
|
if blocked then break end
|
|
end
|
|
end
|
|
|
|
function Fov.compute(map, originX, originY, radius)
|
|
local visible = {}
|
|
visible[map:index(originX, originY)] = true
|
|
for octant = 1, 8 do
|
|
castLight(map, originX, originY, 1, 1.0, 0.0, radius,
|
|
mult[1][octant], mult[2][octant], mult[3][octant], mult[4][octant], visible)
|
|
end
|
|
return visible
|
|
end
|
|
|
|
return Fov
|