import json import pygame from scripts.models import Tile AUTOTILE_MAP: dict[tuple[tuple[int, int], ...], int] = { tuple(sorted([(1, 0), (0, 1)])): 0, tuple(sorted([(1, 0), (0, 1), (-1, 0)])): 1, tuple(sorted([(-1, 0), (0, 1)])): 2, tuple(sorted([(-1, 0), (0, -1), (0, 1)])): 3, tuple(sorted([(-1, 0), (0, -1)])): 4, tuple(sorted([(-1, 0), (0, -1), (1, 0)])): 5, tuple(sorted([(1, 0), (0, -1)])): 6, tuple(sorted([(1, 0), (0, -1), (0, 1)])): 7, tuple(sorted([(1, 0), (-1, 0), (0, 1), (0, -1)])): 8, } NEIGHBOR_OFFSETS: list[tuple[int, int]] = [ (-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (0, 0), (-1, 1), (0, 1), (1, 1), ] PHYSICS_TILES: set[str] = {"grass", "stone"} AUTOTILE_TYPES: set[str] = {"grass", "stone"} class Tilemap: def __init__(self, game, tile_size: int = 16) -> None: self.game = game self.tile_size: int = tile_size self.tilemap: dict[str, Tile] = {} self.offgrid_tiles: list[Tile] = [] def extract( self, id_pairs: list[tuple[str, int]], keep: bool = False ) -> list[dict]: matches: list[dict] = [] for tile in self.offgrid_tiles.copy(): if (tile.type, tile.variant) in id_pairs: matches.append(tile.to_dict()) if not keep: self.offgrid_tiles.remove(tile) for loc in list(self.tilemap): tilemap_tile = self.tilemap[loc] if (tilemap_tile.type, tilemap_tile.variant) in id_pairs: end_match = tilemap_tile.to_dict() end_match["pos"] = [ end_match["pos"][0] * self.tile_size, end_match["pos"][1] * self.tile_size, ] matches.append(end_match) if not keep: del self.tilemap[loc] return matches def tiles_around(self, pos: tuple[float, float]) -> list[Tile]: tiles: list[Tile] = [] tile_location: tuple[int, int] = ( int(pos[0] // self.tile_size), int(pos[1] // self.tile_size), ) for offset in NEIGHBOR_OFFSETS: check_location: str = ( str(tile_location[0] + offset[0]) + ";" + str(tile_location[1] + offset[1]) ) if check_location in self.tilemap: tiles.append(self.tilemap[check_location]) return tiles def save(self, path: str) -> None: data: dict = { "tilemap": {k: tile.to_dict() for k, tile in self.tilemap.items()}, "tile_size": self.tile_size, "offgrid": [tile.to_dict() for tile in self.offgrid_tiles], } with open(path, "w") as f: json.dump(data, f) def load(self, path: str) -> None: with open(path, "r") as f: map_data = json.load(f) self.tile_size: int = map_data["tile_size"] # Grid-Tiles: Dict -> Tile-Objekte self.tilemap: dict[str, Tile] = {} for key, tile_data in map_data["tilemap"].items(): self.tilemap[key] = Tile.from_dict(tile_data) # Offgrid-Tiles: Dict -> Tile-Objekte self.offgrid_tiles: list[Tile] = [ Tile.from_dict(t) for t in map_data["offgrid"] ] def solid_check(self, pos: tuple[float, float]): tile_loc = ( str(int(pos[0] // self.tile_size)) + ";" + str(int(pos[1] // self.tile_size)) ) if tile_loc in self.tilemap: if self.tilemap[tile_loc].type in PHYSICS_TILES: return self.tilemap[tile_loc] def physics_rects_around(self, pos: tuple[float, float]) -> list[pygame.Rect]: rects: list[pygame.Rect] = [] for tile in self.tiles_around(pos): if tile.type in PHYSICS_TILES: rects.append( pygame.Rect( tile.pos[0] * self.tile_size, tile.pos[1] * self.tile_size, self.tile_size, self.tile_size, ) ) return rects def autotile(self): for loc in self.tilemap: tile: Tile = self.tilemap[loc] neighbors: set[tuple[int, int]] = set() for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]: check_loc: str = ( str(tile.pos[0] + shift[0]) + ";" + str(tile.pos[1] + shift[1]) ) if check_loc in self.tilemap: if self.tilemap[check_loc].type == tile.type: neighbors.add(shift) neighbors: tuple[tuple[int, int], ...] = tuple(sorted(neighbors)) if (tile.type in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP): tile.variant = AUTOTILE_MAP[neighbors] def render( self, surface: pygame.Surface, offset: tuple[float, float] = (0, 0) ) -> None: for tile in self.offgrid_tiles: surface.blit( self.game.assets[tile.type][tile.variant], (tile.pos[0] - offset[0], tile.pos[1] - offset[1]), ) for x in range( offset[0] // self.tile_size, (offset[0] + surface.get_width()) // self.tile_size + 1, ): for y in range( offset[1] // self.tile_size, (offset[1] + surface.get_height()) // self.tile_size + 1, ): location_key: str = str(x) + ";" + str(y) if location_key in self.tilemap: tile: Tile = self.tilemap[location_key] surface.blit( self.game.assets[tile.type][tile.variant], ( tile.pos[0] * self.tile_size - offset[0], tile.pos[1] * self.tile_size - offset[1], ), )