First implementation of visibility, not tested, nor used for now
This commit is contained in:
parent
53cb6a89ae
commit
1cf5e7bd8b
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
env/
|
env/
|
||||||
venv/
|
venv/
|
||||||
|
local/
|
||||||
|
|
||||||
.coverage
|
.coverage
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from math import sqrt
|
from math import sqrt
|
||||||
from random import choice, randint
|
from random import choice, randint
|
||||||
from typing import List, Optional
|
from typing import List, Optional, Union, Tuple
|
||||||
|
|
||||||
from .display.texturepack import TexturePack
|
from .display.texturepack import TexturePack
|
||||||
from .translations import gettext as _
|
from .translations import gettext as _
|
||||||
|
@ -30,6 +30,28 @@ class Logs:
|
||||||
self.messages = []
|
self.messages = []
|
||||||
|
|
||||||
|
|
||||||
|
class Slope():
|
||||||
|
X: int
|
||||||
|
Y: int
|
||||||
|
|
||||||
|
def __init__(self, y: int, x: int) -> None:
|
||||||
|
self.Y = y
|
||||||
|
self.X = x
|
||||||
|
|
||||||
|
def compare(self, other: Union[Tuple[int, int], "Slope"]) -> int:
|
||||||
|
if isinstance(other, Slope):
|
||||||
|
y, x = other.Y, other.X
|
||||||
|
else:
|
||||||
|
y, x = other
|
||||||
|
return self.Y * x - self.X * y
|
||||||
|
|
||||||
|
def __lt__(self, other: Union[Tuple[int, int], "Slope"]) -> bool:
|
||||||
|
return self.compare(other) < 0
|
||||||
|
|
||||||
|
def __eq__(self, other: Union[Tuple[int, int], "Slope"]) -> bool:
|
||||||
|
return self.compare(other) == 0
|
||||||
|
|
||||||
|
|
||||||
class Map:
|
class Map:
|
||||||
"""
|
"""
|
||||||
Object that represents a Map with its width, height
|
Object that represents a Map with its width, height
|
||||||
|
@ -40,6 +62,7 @@ class Map:
|
||||||
start_y: int
|
start_y: int
|
||||||
start_x: int
|
start_x: int
|
||||||
tiles: List[List["Tile"]]
|
tiles: List[List["Tile"]]
|
||||||
|
visibility: List[List[bool]]
|
||||||
entities: List["Entity"]
|
entities: List["Entity"]
|
||||||
logs: Logs
|
logs: Logs
|
||||||
# coordinates of the point that should be
|
# coordinates of the point that should be
|
||||||
|
@ -54,6 +77,8 @@ class Map:
|
||||||
self.start_y = start_y
|
self.start_y = start_y
|
||||||
self.start_x = start_x
|
self.start_x = start_x
|
||||||
self.tiles = tiles
|
self.tiles = tiles
|
||||||
|
self.visibility = [[False for _ in range(len(tiles[0]))]
|
||||||
|
for _ in range(len(tiles))]
|
||||||
self.entities = []
|
self.entities = []
|
||||||
self.logs = Logs()
|
self.logs = Logs()
|
||||||
|
|
||||||
|
@ -129,7 +154,7 @@ class Map:
|
||||||
"""
|
"""
|
||||||
Put randomly {count} hedgehogs on the map, where it is available.
|
Put randomly {count} hedgehogs on the map, where it is available.
|
||||||
"""
|
"""
|
||||||
for ignored in range(count):
|
for _ignored in range(count):
|
||||||
y, x = 0, 0
|
y, x = 0, 0
|
||||||
while True:
|
while True:
|
||||||
y, x = randint(0, self.height - 1), randint(0, self.width - 1)
|
y, x = randint(0, self.height - 1), randint(0, self.width - 1)
|
||||||
|
@ -140,6 +165,125 @@ class Map:
|
||||||
entity.move(y, x)
|
entity.move(y, x)
|
||||||
self.add_entity(entity)
|
self.add_entity(entity)
|
||||||
|
|
||||||
|
def compute_visibility(self, y: int, x: int, max_range: int) -> None:
|
||||||
|
"""
|
||||||
|
Sets the visible tiles to be the ones visible by an entity at point
|
||||||
|
(y, x), using a twaked shadow casting algorithm
|
||||||
|
"""
|
||||||
|
|
||||||
|
for line in self.visibility:
|
||||||
|
for i in range(len(line)):
|
||||||
|
line[i] = False
|
||||||
|
self.visibility[y][x] = True
|
||||||
|
for octant in range(8):
|
||||||
|
self.compute_visibility_octant(octant, (y, x), max_range, 1,
|
||||||
|
Slope(1, 1), Slope(0, 1))
|
||||||
|
|
||||||
|
def crop_top_visibility(self, octant: int, origin: Tuple[int, int],
|
||||||
|
x: int, top: Slope) -> int:
|
||||||
|
if top.X == 1:
|
||||||
|
top_y = x
|
||||||
|
else:
|
||||||
|
top_y = ((x * 2 - 1) * top.Y + top.X) / (top.X * 2)
|
||||||
|
if self.is_wall(top_y, x, octant, origin):
|
||||||
|
if top >= (top_y * 2 + 1, x * 2) and not\
|
||||||
|
self.is_wall(top_y + 1, x, octant, origin):
|
||||||
|
top_y += 1
|
||||||
|
else:
|
||||||
|
ax = x * 2
|
||||||
|
if self.is_wall(top_y + 1, x + 1, octant, origin):
|
||||||
|
ax += 1
|
||||||
|
if top > (top_y * 2 + 1, ax):
|
||||||
|
top_y += 1
|
||||||
|
return top_y
|
||||||
|
|
||||||
|
def crop_bottom_visibility(self, octant: int, origin: Tuple[int, int],
|
||||||
|
x: int, bottom: Slope) -> int:
|
||||||
|
if bottom.Y == 0:
|
||||||
|
bottom_y = 0
|
||||||
|
else:
|
||||||
|
bottom_y = ((x * 2 + 1) * bottom.Y + bottom.X) /\
|
||||||
|
(bottom.X * 2)
|
||||||
|
if bottom >= (bottom_y * 2 + 1, x * 2) and\
|
||||||
|
self.is_wall(bottom_y, x, octant, origin) and\
|
||||||
|
not self.is_wall(bottom_y + 1, x, octant, origin):
|
||||||
|
bottom_y += 1
|
||||||
|
return bottom_y
|
||||||
|
|
||||||
|
def compute_visibility_octant(self, octant: int, origin: Tuple[int, int],
|
||||||
|
max_range: int, distance: int, top: Slope,
|
||||||
|
bottom: Slope) -> None:
|
||||||
|
for x in range(distance, max_range):
|
||||||
|
top_y = self.crop_top_visibility(octant, origin, x, top)
|
||||||
|
bottom_y = self.crop_bottom_visibility(octant, origin, x, bottom)
|
||||||
|
was_opaque = -1
|
||||||
|
for y in range(top_y, bottom_y - 1, -1):
|
||||||
|
if sqrt(x**2 + y**2) > max_range:
|
||||||
|
continue
|
||||||
|
is_opaque = self.is_wall(y, x, octant, origin)
|
||||||
|
is_visible = is_opaque\
|
||||||
|
or ((y != top_y or top > (y * 4 - 1, x * 4 - 1))
|
||||||
|
and (y != bottom_y or bottom < (y * 4 + 1, x * 4 + 1)))
|
||||||
|
if is_visible:
|
||||||
|
self.set_visible(y, x, octant, origin)
|
||||||
|
if x == max_range:
|
||||||
|
continue
|
||||||
|
if is_opaque and was_opaque == 0:
|
||||||
|
nx, ny = x * 2, y * 2 + 1
|
||||||
|
if self.is_wall(y + 1, x, octant, origin):
|
||||||
|
nx -= 1
|
||||||
|
if top > (ny, nx):
|
||||||
|
if y == bottom_y:
|
||||||
|
bottom = Slope(ny, nx)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.compute_visibility_octant(
|
||||||
|
octant, origin, max_range, x + 1, top,
|
||||||
|
Slope(ny, nx))
|
||||||
|
else:
|
||||||
|
if y == bottom_y:
|
||||||
|
return
|
||||||
|
elif not is_opaque and was_opaque == 1:
|
||||||
|
nx, ny = x * 2, y * 2 + 1
|
||||||
|
if self.is_wall(y + 1, x + 1, octant, origin):
|
||||||
|
nx += 1
|
||||||
|
if bottom >= (ny, nx):
|
||||||
|
return
|
||||||
|
was_opaque = is_opaque
|
||||||
|
if was_opaque != 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def translate_coord(y: int, x: int, octant: int,
|
||||||
|
origin: Tuple[int, int]) -> Tuple[int, int]:
|
||||||
|
ny, nx = origin
|
||||||
|
if octant == 0:
|
||||||
|
return nx + x, ny - y
|
||||||
|
elif octant == 1:
|
||||||
|
return nx + y, ny - x
|
||||||
|
elif octant == 2:
|
||||||
|
return nx - y, ny - x
|
||||||
|
elif octant == 3:
|
||||||
|
return nx - x, ny - y
|
||||||
|
elif octant == 4:
|
||||||
|
return nx - x, ny + y
|
||||||
|
elif octant == 5:
|
||||||
|
return nx - y, ny + x
|
||||||
|
elif octant == 6:
|
||||||
|
return nx + y, ny + x
|
||||||
|
elif octant == 7:
|
||||||
|
return nx + x, ny + y
|
||||||
|
|
||||||
|
def is_wall(self, y: int, x: int, octant: int,
|
||||||
|
origin: Tuple[int, int]) -> bool:
|
||||||
|
y, x = self.translate_coord(y, x, octant, origin)
|
||||||
|
return self.tiles[y][x].is_wall()
|
||||||
|
|
||||||
|
def set_visible(self, y: int, x: int, octant: int,
|
||||||
|
origin: Tuple[int, int]) -> None:
|
||||||
|
y, x = self.translate_coord(y, x, octant, origin)
|
||||||
|
self.visibility[y][x] = True
|
||||||
|
|
||||||
def tick(self) -> None:
|
def tick(self) -> None:
|
||||||
"""
|
"""
|
||||||
Trigger all entity events.
|
Trigger all entity events.
|
||||||
|
|
Loading…
Reference in New Issue