87 lines
3.3 KiB
Python
87 lines
3.3 KiB
Python
import numpy as np
|
|
|
|
from environments.helpers import Constants as c
|
|
|
|
mult_array = np.asarray([
|
|
[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]
|
|
])
|
|
|
|
|
|
class Map(object):
|
|
# Multipliers for transforming coordinates to other octants:
|
|
|
|
def __init__(self, map_array: np.typing.ArrayLike, diamond_slope: float = 0.9):
|
|
self.data = map_array
|
|
self.width, self.height = map_array.shape
|
|
self.light = np.full_like(self.data, c.FREE_CELL.value)
|
|
self.flag = c.FREE_CELL.value
|
|
self.d_slope = diamond_slope
|
|
|
|
def blocked(self, x, y):
|
|
return (x < 0 or y < 0
|
|
or x >= self.width or y >= self.height
|
|
or self.data[x, y] == c.OCCUPIED_CELL.value)
|
|
|
|
def lit(self, x, y):
|
|
return self.light[x, y] == self.flag
|
|
|
|
def set_lit(self, x, y):
|
|
if 0 <= x < self.width and 0 <= y < self.height:
|
|
self.light[x, y] = self.flag
|
|
|
|
def _cast_light(self, cx, cy, row, start, end, radius, xx, xy, yx, yy, id):
|
|
"Recursive lightcasting function"
|
|
if start < end:
|
|
return
|
|
radius_squared = radius*radius
|
|
new_start = None
|
|
for j in range(row, radius+1):
|
|
dx, dy = -j-1, -j
|
|
blocked = False
|
|
while dx <= 0:
|
|
dx += 1
|
|
# Translate the dx, dy coordinates into map coordinates:
|
|
X, Y = cx + dx * xx + dy * xy, cy + dx * yx + dy * yy
|
|
# l_slope and r_slope store the slopes of the left and right
|
|
# extremities of the square we're considering:
|
|
l_slope, r_slope = (dx-self.d_slope)/(dy+self.d_slope), (dx+self.d_slope)/(dy-self.d_slope)
|
|
if start < r_slope:
|
|
continue
|
|
elif end > l_slope:
|
|
break
|
|
else:
|
|
# Our light beam is touching this square; light it:
|
|
if dx*dx + dy*dy < radius_squared:
|
|
self.set_lit(X, Y)
|
|
if blocked:
|
|
# we're scanning a row of blocked squares:
|
|
if self.blocked(X, Y):
|
|
new_start = r_slope
|
|
continue
|
|
else:
|
|
blocked = False
|
|
start = new_start
|
|
else:
|
|
if self.blocked(X, Y) and j < radius:
|
|
# This is a blocking square, start a child scan:
|
|
blocked = True
|
|
self._cast_light(cx, cy, j+1, start, l_slope,
|
|
radius, xx, xy, yx, yy, id+1)
|
|
new_start = r_slope
|
|
# Row is scanned; do next row unless last square was blocked:
|
|
if blocked:
|
|
break
|
|
|
|
def do_fov(self, x, y, radius):
|
|
"Calculate lit squares from the given location and radius"
|
|
self.flag += 1
|
|
for oct in range(8):
|
|
self._cast_light(x, y, 1, 1.0, 0.0, radius,
|
|
mult_array[0, oct], mult_array[1, oct],
|
|
mult_array[2, oct], mult_array[3, oct], 0)
|
|
self.light[x, y] = self.flag
|
|
return self.light
|