1
Fork 0
mirror of https://github.com/Steffo99/lihzahrd.git synced 2024-11-24 00:54:23 +00:00

Use a tilematrix instead of a list of lists

This commit is contained in:
Steffo 2019-08-12 21:57:08 +02:00
parent 317d5d5190
commit 86ebd39c4e
6 changed files with 78 additions and 12 deletions

2
lihzahrd/errors.py Normal file
View file

@ -0,0 +1,2 @@
class InvalidFooterError(Exception):
"""This exception is raised if the footer contents do not match the expected data."""

View file

@ -9,6 +9,7 @@ from .block import Block
from .wall import Wall from .wall import Wall
from .liquid import Liquid from .liquid import Liquid
from .tile import Tile from .tile import Tile
from .tilematrix import TileMatrix
__all__ = ["LiquidType", "RLEEncoding", "Shape", "Wiring", "BlockType", "FrameImportantData", "WallType", "Block", __all__ = ["LiquidType", "RLEEncoding", "Shape", "Wiring", "BlockType", "FrameImportantData", "WallType", "Block",
"Wall", "Liquid", "Tile"] "Wall", "Liquid", "Tile", "TileMatrix"]

View file

@ -0,0 +1,61 @@
import typing
from .tile import Tile
from ..fileutils import FileReader, Coordinates
class TileMatrix:
"""A huge matrix containing the tiles of a whole world."""
def __init__(self):
self._tiles: typing.List[typing.List[Tile]] = []
def __repr__(self):
if len(self._tiles) > 0:
return f"<TileMatrix {len(self._tiles)}x{len(self._tiles[0])}>"
return f"<TileMatrix 0x0>"
def __getitem__(self, item: typing.Union[typing.Tuple, Coordinates]):
"""Get a tile at specific coordinates.
(x=0, y=0) returns the top-left tile in the map.
You can specify a negative coordinate to count tiles respectively from the right or from the bottom:
(x=-1, y=-1) returns the bottom-right tile in the map."""
if isinstance(item, Coordinates):
return self._tiles[item.x][item.y]
elif isinstance(item, tuple):
return self._tiles[item[0]][item[1]]
else:
raise TypeError(f"Unsupported type: {item.__class__.__name__}")
def __setitem__(self, key: typing.Union[typing.Tuple, Coordinates], value: Tile):
"""Change a tile at specific coordinates.
The same properties that apply to __getitem__ are valid for __setitem__."""
if not isinstance(value, Tile):
raise TypeError(f"Invalid type: {value.__class__.__name__} instead of Tile")
if isinstance(key, Coordinates):
self._tiles[key.x][key.y] = value
elif isinstance(key, tuple):
self._tiles[key[0]][key[1]] = value
else:
raise TypeError(f"Invalid type: {key.__class__.__name__} instead of tuple or Coordinates")
def __len__(self):
"""Return the amount of tiles present in the matrix."""
if len(self._tiles) > 0:
return len(self._tiles) * len(self._tiles[0])
return 0
def add_column(self, column: typing.List[Tile]):
"""Add a new column to the matrix."""
if len(self._tiles) > 0 and len(column) != len(self._tiles[0]):
raise ValueError("column has a different length than the others in the matrix")
self._tiles.append(column)
@property
def size(self) -> Coordinates:
"""Return the size of the matrix as a pair of coordinates."""
if len(self._tiles) > 0:
return Coordinates(len(self._tiles), len(self._tiles[0]))
return Coordinates(0, 0)

View file

@ -37,4 +37,4 @@ class Wiring:
if flags3 is not None: if flags3 is not None:
return cls(red=flags2[1], green=flags2[2], blue=flags2[3], yellow=flags3[5], actuator=flags3[1]) return cls(red=flags2[1], green=flags2[2], blue=flags2[3], yellow=flags3[5], actuator=flags3[1])
return cls(red=flags2[1], green=flags2[2], blue=flags2[3]) return cls(red=flags2[1], green=flags2[2], blue=flags2[3])
return None return cls()

View file

@ -3,7 +3,8 @@ import typing
class Timer: class Timer:
def __init__(self, name: str, display: bool = False): """An object to track and print the time required to perform a section of code."""
def __init__(self, name: str, display: bool = True):
self.name: str = name self.name: str = name
self.display: bool = display self.display: bool = display
self._start_time: typing.Optional[float] = None self._start_time: typing.Optional[float] = None

View file

@ -11,6 +11,7 @@ from .tileentities import *
from .pressureplates import * from .pressureplates import *
from .townmanager import * from .townmanager import *
from .timer import Timer from .timer import Timer
from .errors import InvalidFooterError
class World: class World:
@ -46,7 +47,7 @@ class World:
anglers_quest: AnglerQuest, anglers_quest: AnglerQuest,
clouds: Clouds, clouds: Clouds,
cultist_delay: int, cultist_delay: int,
tiles: typing.List[typing.List[Tile]], tiles: TileMatrix,
chests: typing.List[Chest], chests: typing.List[Chest],
signs: typing.List[Sign], signs: typing.List[Sign],
npcs: typing.List[NPC], npcs: typing.List[NPC],
@ -145,7 +146,7 @@ class World:
self.anglers_quest: AnglerQuest = anglers_quest self.anglers_quest: AnglerQuest = anglers_quest
"""Information about today's Angler's Quest.""" """Information about today's Angler's Quest."""
self.tiles: typing.List[typing.List[Tile]] = tiles self.tiles: TileMatrix = tiles
"""A matrix of all the tiles present in the world.""" """A matrix of all the tiles present in the world."""
self.chests: typing.List[Chest] = chests self.chests: typing.List[Chest] = chests
@ -515,13 +516,13 @@ class World:
unknown_world_header_data = f.read_until(pointers.world_tiles) unknown_world_header_data = f.read_until(pointers.world_tiles)
with Timer("World Tiles", display=True): with Timer("World Tiles", display=True):
tiles = [] tm = TileMatrix()
while len(tiles) < world_size.x: while tm.size.x < world_size.x:
column = [] column = []
while len(column) < world_size.y: while len(column) < world_size.y:
readtiles = cls._read_tile_block(f, tileframeimportant) readtiles = cls._read_tile_block(f, tileframeimportant)
column = [*column, *readtiles] column = [*column, *readtiles]
tiles.append(column) tm.add_column(column)
unknown_world_tiles_data = f.read_until(pointers.chests) unknown_world_tiles_data = f.read_until(pointers.chests)
@ -642,7 +643,7 @@ class World:
time=time, events=events, dungeon_point=dungeon_point, world_evil=world_evil, time=time, events=events, dungeon_point=dungeon_point, world_evil=world_evil,
saved_npcs=saved_npcs, altars_smashed=altars_smashed, is_hardmode=is_hardmode, saved_npcs=saved_npcs, altars_smashed=altars_smashed, is_hardmode=is_hardmode,
shadow_orbs=shadow_orbs, bosses_defeated=bosses_defeated, anglers_quest=anglers_quest, shadow_orbs=shadow_orbs, bosses_defeated=bosses_defeated, anglers_quest=anglers_quest,
clouds=clouds, cultist_delay=cultist_delay, tiles=tiles, chests=chests, signs=signs, clouds=clouds, cultist_delay=cultist_delay, tiles=tm, chests=chests, signs=signs,
npcs=npcs, mobs=mobs, tile_entities=tile_entities, npcs=npcs, mobs=mobs, tile_entities=tile_entities,
weighed_pressure_plates=weighed_pressure_plates, rooms=rooms, weighed_pressure_plates=weighed_pressure_plates, rooms=rooms,
unknown_file_format_data=unknown_file_format_data, unknown_file_format_data=unknown_file_format_data,
@ -657,10 +658,10 @@ class World:
with Timer("Footer", display=True): with Timer("Footer", display=True):
if not f.bool(): if not f.bool():
raise Exception("Invalid footer") raise InvalidFooterError("Invalid footer")
if not f.string() == world.name: if not f.string() == world.name:
raise Exception("Invalid footer") raise InvalidFooterError("Invalid footer")
if not f.int4() == world.id: if not f.int4() == world.id:
raise Exception("Invalid footer") raise InvalidFooterError("Invalid footer")
return world return world