Merge branch 'lighting' into 'master'
Lighting Closes #27 See merge request ynerant/squirrel-battle!34
This commit is contained in:
commit
da47731faf
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
env/
|
env/
|
||||||
venv/
|
venv/
|
||||||
|
local/
|
||||||
|
|
||||||
.coverage
|
.coverage
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
1 6
|
||||||
|
################################################################################
|
||||||
|
#..............................................................................#
|
||||||
|
#..#...........................................................................#
|
||||||
|
#...........#..................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
#..............................................................................#
|
||||||
|
################################################################################
|
|
@ -22,16 +22,21 @@ class MapDisplay(Display):
|
||||||
self.pack.tile_width * self.map.width + 1)
|
self.pack.tile_width * self.map.width + 1)
|
||||||
|
|
||||||
def update_pad(self) -> None:
|
def update_pad(self) -> None:
|
||||||
self.pad.resize(500, 500)
|
for j in range(len(self.map.tiles)):
|
||||||
for i in range(self.map.height):
|
for i in range(len(self.map.tiles[j])):
|
||||||
for j in range(self.map.width):
|
if not self.map.seen_tiles[j][i]:
|
||||||
self.addstr(self.pad, i, j * self.pack.tile_width,
|
continue
|
||||||
self.map.tiles[i][j].char(self.pack),
|
fg, bg = self.map.tiles[j][i].visible_color(self.pack) if \
|
||||||
*self.map.tiles[i][j].color(self.pack))
|
self.map.visibility[j][i] else \
|
||||||
|
self.map.tiles[j][i].hidden_color(self.pack)
|
||||||
|
self.addstr(self.pad, j, self.pack.tile_width * i,
|
||||||
|
self.map.tiles[j][i].char(self.pack), fg, bg)
|
||||||
for e in self.map.entities:
|
for e in self.map.entities:
|
||||||
|
if self.map.visibility[e.y][e.x]:
|
||||||
self.addstr(self.pad, e.y, self.pack.tile_width * e.x,
|
self.addstr(self.pad, e.y, self.pack.tile_width * e.x,
|
||||||
self.pack[e.name.upper()],
|
self.pack[e.name.upper()],
|
||||||
self.pack.entity_fg_color, self.pack.entity_bg_color)
|
self.pack.entity_fg_color,
|
||||||
|
self.pack.entity_bg_color)
|
||||||
|
|
||||||
# Display Path map for debug purposes
|
# Display Path map for debug purposes
|
||||||
# from squirrelbattle.entities.player import Player
|
# from squirrelbattle.entities.player import Player
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
from typing import Any
|
from typing import Any, Union, Tuple
|
||||||
|
|
||||||
|
|
||||||
class TexturePack:
|
class TexturePack:
|
||||||
|
@ -13,10 +13,11 @@ class TexturePack:
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
tile_width: int
|
tile_width: int
|
||||||
tile_fg_color: int
|
tile_fg_color: Union[int, Tuple[int, int, int]]
|
||||||
tile_bg_color: int
|
tile_fg_visible_color: Union[int, Tuple[int, int, int]]
|
||||||
entity_fg_color: int
|
tile_bg_color: Union[int, Tuple[int, int, int]]
|
||||||
entity_bg_color: int
|
entity_fg_color: Union[int, Tuple[int, int, int]]
|
||||||
|
entity_bg_color: Union[int, Tuple[int, int, int]]
|
||||||
|
|
||||||
BODY_SNATCH_POTION: str
|
BODY_SNATCH_POTION: str
|
||||||
BOMB: str
|
BOMB: str
|
||||||
|
@ -58,9 +59,10 @@ class TexturePack:
|
||||||
TexturePack.ASCII_PACK = TexturePack(
|
TexturePack.ASCII_PACK = TexturePack(
|
||||||
name="ascii",
|
name="ascii",
|
||||||
tile_width=1,
|
tile_width=1,
|
||||||
|
tile_fg_visible_color=(1000, 1000, 1000),
|
||||||
tile_fg_color=curses.COLOR_WHITE,
|
tile_fg_color=curses.COLOR_WHITE,
|
||||||
tile_bg_color=curses.COLOR_BLACK,
|
tile_bg_color=curses.COLOR_BLACK,
|
||||||
entity_fg_color=curses.COLOR_WHITE,
|
entity_fg_color=(1000, 1000, 1000),
|
||||||
entity_bg_color=curses.COLOR_BLACK,
|
entity_bg_color=curses.COLOR_BLACK,
|
||||||
|
|
||||||
BODY_SNATCH_POTION='S',
|
BODY_SNATCH_POTION='S',
|
||||||
|
@ -86,17 +88,19 @@ TexturePack.ASCII_PACK = TexturePack(
|
||||||
TexturePack.SQUIRREL_PACK = TexturePack(
|
TexturePack.SQUIRREL_PACK = TexturePack(
|
||||||
name="squirrel",
|
name="squirrel",
|
||||||
tile_width=2,
|
tile_width=2,
|
||||||
|
tile_fg_visible_color=(1000, 1000, 1000),
|
||||||
tile_fg_color=curses.COLOR_WHITE,
|
tile_fg_color=curses.COLOR_WHITE,
|
||||||
tile_bg_color=curses.COLOR_BLACK,
|
tile_bg_color=curses.COLOR_BLACK,
|
||||||
entity_fg_color=curses.COLOR_WHITE,
|
entity_fg_color=(1000, 1000, 1000),
|
||||||
entity_bg_color=curses.COLOR_WHITE,
|
entity_bg_color=(1000, 1000, 1000),
|
||||||
|
|
||||||
BODY_SNATCH_POTION='🔀',
|
BODY_SNATCH_POTION='🔀',
|
||||||
BOMB='💣',
|
BOMB='💣',
|
||||||
EMPTY=' ',
|
EMPTY=' ',
|
||||||
EXPLOSION='💥',
|
EXPLOSION='💥',
|
||||||
FLOOR='██',
|
FLOOR='██',
|
||||||
LADDER=('🪜', curses.COLOR_WHITE, curses.COLOR_WHITE),
|
LADDER=('🪜', curses.COLOR_WHITE, (1000, 1000, 1000),
|
||||||
|
curses.COLOR_WHITE, (1000, 1000, 1000)),
|
||||||
HAZELNUT='🌰',
|
HAZELNUT='🌰',
|
||||||
HEART='💜',
|
HEART='💜',
|
||||||
HEDGEHOG='🦔',
|
HEDGEHOG='🦔',
|
||||||
|
|
|
@ -17,7 +17,7 @@ class Player(InventoryHolder, FightingEntity):
|
||||||
strength: int = 5, intelligence: int = 1, charisma: int = 1,
|
strength: int = 5, intelligence: int = 1, charisma: int = 1,
|
||||||
dexterity: int = 1, constitution: int = 1, level: int = 1,
|
dexterity: int = 1, constitution: int = 1, level: int = 1,
|
||||||
current_xp: int = 0, max_xp: int = 10, inventory: list = None,
|
current_xp: int = 0, max_xp: int = 10, inventory: list = None,
|
||||||
hazel: int = 42, *args, **kwargs) \
|
hazel: int = 42, vision: int = 5, *args, **kwargs) \
|
||||||
-> None:
|
-> None:
|
||||||
super().__init__(name=name, maxhealth=maxhealth, strength=strength,
|
super().__init__(name=name, maxhealth=maxhealth, strength=strength,
|
||||||
intelligence=intelligence, charisma=charisma,
|
intelligence=intelligence, charisma=charisma,
|
||||||
|
@ -28,6 +28,7 @@ class Player(InventoryHolder, FightingEntity):
|
||||||
self.inventory = self.translate_inventory(inventory or [])
|
self.inventory = self.translate_inventory(inventory or [])
|
||||||
self.paths = dict()
|
self.paths = dict()
|
||||||
self.hazel = hazel
|
self.hazel = hazel
|
||||||
|
self.vision = vision
|
||||||
|
|
||||||
def move(self, y: int, x: int) -> None:
|
def move(self, y: int, x: int) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -38,6 +39,7 @@ class Player(InventoryHolder, FightingEntity):
|
||||||
self.map.currenty = y
|
self.map.currenty = y
|
||||||
self.map.currentx = x
|
self.map.currentx = x
|
||||||
self.recalculate_paths()
|
self.recalculate_paths()
|
||||||
|
self.map.compute_visibility(self.y, self.x, self.vision)
|
||||||
|
|
||||||
def level_up(self) -> None:
|
def level_up(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from math import sqrt
|
from math import ceil, sqrt
|
||||||
from random import choice, randint
|
from random import choice, randint
|
||||||
from typing import List, Optional, Any, Dict, Tuple
|
from typing import List, Optional, Any, Dict, Tuple
|
||||||
from queue import PriorityQueue
|
from queue import PriorityQueue
|
||||||
|
@ -32,6 +32,34 @@ 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: "Slope") -> int:
|
||||||
|
y, x = other.Y, other.X
|
||||||
|
return self.Y * x - self.X * y
|
||||||
|
|
||||||
|
def __lt__(self, other: "Slope") -> bool:
|
||||||
|
return self.compare(other) < 0
|
||||||
|
|
||||||
|
def __eq__(self, other: "Slope") -> bool:
|
||||||
|
return self.compare(other) == 0
|
||||||
|
|
||||||
|
def __gt__(self, other: "Slope") -> bool:
|
||||||
|
return self.compare(other) > 0
|
||||||
|
|
||||||
|
def __le__(self, other: "Slope") -> bool:
|
||||||
|
return self.compare(other) <= 0
|
||||||
|
|
||||||
|
def __ge__(self, other: "Slope") -> bool:
|
||||||
|
return self.compare(other) >= 0
|
||||||
|
|
||||||
|
|
||||||
class Map:
|
class Map:
|
||||||
"""
|
"""
|
||||||
The Map object represents a with its width, height
|
The Map object represents a with its width, height
|
||||||
|
@ -43,6 +71,8 @@ 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]]
|
||||||
|
seen_tiles: 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
|
||||||
|
@ -58,6 +88,10 @@ 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.seen_tiles = [[False for _ in range(len(tiles[0]))]
|
||||||
|
for _ in range(len(tiles))]
|
||||||
self.entities = []
|
self.entities = []
|
||||||
self.logs = Logs()
|
self.logs = Logs()
|
||||||
|
|
||||||
|
@ -147,7 +181,8 @@ class Map:
|
||||||
"""
|
"""
|
||||||
Puts randomly {count} entities on the map, only on empty ground tiles.
|
Puts randomly {count} entities on the map, only on empty ground tiles.
|
||||||
"""
|
"""
|
||||||
for ignored in range(count):
|
for _ignored in range(count):
|
||||||
|
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)
|
||||||
tile = self.tiles[y][x]
|
tile = self.tiles[y][x]
|
||||||
|
@ -157,6 +192,126 @@ 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.set_visible(0, 0, 0, (y, x))
|
||||||
|
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 = ceil(((x * 2 - 1) * top.Y + top.X) / (top.X * 2))
|
||||||
|
if self.is_wall(top_y, x, octant, origin):
|
||||||
|
top_y += top >= Slope(top_y * 2 + 1, x * 2) and not \
|
||||||
|
self.is_wall(top_y + 1, x, octant, origin)
|
||||||
|
else:
|
||||||
|
ax = x * 2
|
||||||
|
ax += self.is_wall(top_y + 1, x + 1, octant, origin)
|
||||||
|
top_y += top > Slope(top_y * 2 + 1, ax)
|
||||||
|
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 = ceil(((x * 2 - 1) * bottom.Y + bottom.X)
|
||||||
|
/ (bottom.X * 2))
|
||||||
|
bottom_y += bottom >= Slope(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)
|
||||||
|
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 + 1):
|
||||||
|
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 x + y > max_range:
|
||||||
|
continue
|
||||||
|
is_opaque = self.is_wall(y, x, octant, origin)
|
||||||
|
is_visible = is_opaque\
|
||||||
|
or ((y != top_y or top > Slope(y * 4 - 1, x * 4 + 1))
|
||||||
|
and (y != bottom_y
|
||||||
|
or bottom < Slope(y * 4 + 1, x * 4 - 1)))
|
||||||
|
# is_visible = is_opaque\
|
||||||
|
# or ((y != top_y or top >= Slope(y, x))
|
||||||
|
# and (y != bottom_y or bottom <= Slope(y, x)))
|
||||||
|
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
|
||||||
|
nx -= self.is_wall(y + 1, x, octant, origin)
|
||||||
|
if top > Slope(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))
|
||||||
|
elif y == bottom_y: # pragma: no cover
|
||||||
|
return
|
||||||
|
elif not is_opaque and was_opaque == 1:
|
||||||
|
nx, ny = x * 2, y * 2 + 1
|
||||||
|
nx += self.is_wall(y + 1, x + 1, octant, origin)
|
||||||
|
if bottom >= Slope(ny, nx): # pragma: no cover
|
||||||
|
return
|
||||||
|
top = Slope(ny, nx)
|
||||||
|
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 ny - y, nx + x
|
||||||
|
elif octant == 1:
|
||||||
|
return ny - x, nx + y
|
||||||
|
elif octant == 2:
|
||||||
|
return ny - x, nx - y
|
||||||
|
elif octant == 3:
|
||||||
|
return ny - y, nx - x
|
||||||
|
elif octant == 4:
|
||||||
|
return ny + y, nx - x
|
||||||
|
elif octant == 5:
|
||||||
|
return ny + x, nx - y
|
||||||
|
elif octant == 6:
|
||||||
|
return ny + x, nx + y
|
||||||
|
elif octant == 7:
|
||||||
|
return ny + y, nx + x
|
||||||
|
|
||||||
|
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 0 <= y < len(self.tiles) and 0 <= x < len(self.tiles[0]) and \
|
||||||
|
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)
|
||||||
|
if 0 <= y < len(self.tiles) and 0 <= x < len(self.tiles[0]):
|
||||||
|
self.visibility[y][x] = True
|
||||||
|
self.seen_tiles[y][x] = True
|
||||||
|
|
||||||
def tick(self, p: Any) -> None:
|
def tick(self, p: Any) -> None:
|
||||||
"""
|
"""
|
||||||
Triggers all entity events.
|
Triggers all entity events.
|
||||||
|
@ -228,12 +383,21 @@ class Tile(Enum):
|
||||||
val = getattr(pack, self.name)
|
val = getattr(pack, self.name)
|
||||||
return val[0] if isinstance(val, tuple) else val
|
return val[0] if isinstance(val, tuple) else val
|
||||||
|
|
||||||
def color(self, pack: TexturePack) -> Tuple[int, int]:
|
def visible_color(self, pack: TexturePack) -> Tuple[int, int]:
|
||||||
|
"""
|
||||||
|
Retrieve the tuple (fg_color, bg_color) of the current Tile
|
||||||
|
if it is visible.
|
||||||
|
"""
|
||||||
|
val = getattr(pack, self.name)
|
||||||
|
return (val[2], val[4]) if isinstance(val, tuple) else \
|
||||||
|
(pack.tile_fg_visible_color, pack.tile_bg_color)
|
||||||
|
|
||||||
|
def hidden_color(self, pack: TexturePack) -> Tuple[int, int]:
|
||||||
"""
|
"""
|
||||||
Retrieve the tuple (fg_color, bg_color) of the current Tile.
|
Retrieve the tuple (fg_color, bg_color) of the current Tile.
|
||||||
"""
|
"""
|
||||||
val = getattr(pack, self.name)
|
val = getattr(pack, self.name)
|
||||||
return (val[1], val[2]) if isinstance(val, tuple) else \
|
return (val[1], val[3]) if isinstance(val, tuple) else \
|
||||||
(pack.tile_fg_color, pack.tile_bg_color)
|
(pack.tile_fg_color, pack.tile_bg_color)
|
||||||
|
|
||||||
def is_wall(self) -> bool:
|
def is_wall(self) -> bool:
|
||||||
|
|
|
@ -175,7 +175,7 @@ class TestEntities(unittest.TestCase):
|
||||||
self.assertEqual(item.y, 42)
|
self.assertEqual(item.y, 42)
|
||||||
self.assertEqual(item.x, 42)
|
self.assertEqual(item.x, 42)
|
||||||
# Wait for the explosion
|
# Wait for the explosion
|
||||||
for ignored in range(5):
|
for _ignored in range(5):
|
||||||
item.act(self.map)
|
item.act(self.map)
|
||||||
self.assertTrue(hedgehog.dead)
|
self.assertTrue(hedgehog.dead)
|
||||||
self.assertTrue(teddy_bear.dead)
|
self.assertTrue(teddy_bear.dead)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from squirrelbattle.display.texturepack import TexturePack
|
from squirrelbattle.display.texturepack import TexturePack
|
||||||
from squirrelbattle.interfaces import Map, Tile
|
from squirrelbattle.interfaces import Map, Tile, Slope
|
||||||
from squirrelbattle.resources import ResourceManager
|
from squirrelbattle.resources import ResourceManager
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,3 +37,21 @@ class TestInterfaces(unittest.TestCase):
|
||||||
self.assertFalse(Tile.WALL.can_walk())
|
self.assertFalse(Tile.WALL.can_walk())
|
||||||
self.assertFalse(Tile.EMPTY.can_walk())
|
self.assertFalse(Tile.EMPTY.can_walk())
|
||||||
self.assertRaises(ValueError, Tile.from_ascii_char, 'unknown')
|
self.assertRaises(ValueError, Tile.from_ascii_char, 'unknown')
|
||||||
|
|
||||||
|
def test_slope(self) -> None:
|
||||||
|
"""
|
||||||
|
Test good behaviour of slopes (basically vectors, compared according to
|
||||||
|
the determinant)
|
||||||
|
"""
|
||||||
|
a = Slope(1, 1)
|
||||||
|
b = Slope(0, 1)
|
||||||
|
self.assertTrue(b < a)
|
||||||
|
self.assertTrue(b <= a)
|
||||||
|
self.assertTrue(a <= a)
|
||||||
|
self.assertTrue(a == a)
|
||||||
|
self.assertTrue(a > b)
|
||||||
|
self.assertTrue(a >= b)
|
||||||
|
|
||||||
|
# def test_visibility(self) -> None:
|
||||||
|
# m = Map.load(ResourceManager.get_asset_path("example_map_3.txt"))
|
||||||
|
# m.compute_visibility(1, 1, 50)
|
||||||
|
|
Loading…
Reference in New Issue