Ninja-Jump-and-run/scripts/tilemap.py

171 lines
5.9 KiB
Python
Raw Normal View History

2026-04-17 16:04:33 +00:00
import json
import pygame
from scripts.models import Tile
2026-04-24 16:45:31 +00:00
AUTOTILE_MAP: dict[tuple[tuple[int, int], ...], int] = {
2026-04-17 16:04:33 +00:00
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,
}
2026-06-12 14:43:28 +00:00
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"}
2026-04-24 16:45:31 +00:00
2026-02-06 17:51:57 +00:00
class Tilemap:
def __init__(self, game, tile_size: int = 16) -> None:
2026-02-06 17:51:57 +00:00
self.game = game
self.tile_size: int = tile_size
self.tilemap: dict[str, Tile] = {}
self.offgrid_tiles: list[Tile] = []
2026-06-12 14:43:28 +00:00
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()
2026-06-12 14:43:28 +00:00
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] = []
2026-06-12 14:43:28 +00:00
tile_location: tuple[int, int] = (
int(pos[0] // self.tile_size),
int(pos[1] // self.tile_size),
)
2026-02-06 17:51:57 +00:00
for offset in NEIGHBOR_OFFSETS:
2026-06-12 14:43:28 +00:00
check_location: str = (
str(tile_location[0] + offset[0])
+ ";"
+ str(tile_location[1] + offset[1])
)
2026-02-06 17:51:57 +00:00
if check_location in self.tilemap:
tiles.append(self.tilemap[check_location])
return tiles
2026-06-12 14:43:28 +00:00
def save(self, path: str) -> None:
data: dict = {
2026-06-12 14:43:28 +00:00
"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],
}
2026-06-12 14:43:28 +00:00
with open(path, "w") as f:
json.dump(data, f)
2026-06-12 14:43:28 +00:00
def load(self, path: str) -> None:
2026-06-12 14:43:28 +00:00
with open(path, "r") as f:
map_data = json.load(f)
2026-06-12 14:43:28 +00:00
self.tile_size: int = map_data["tile_size"]
# Grid-Tiles: Dict -> Tile-Objekte
self.tilemap: dict[str, Tile] = {}
2026-06-12 14:43:28 +00:00
for key, tile_data in map_data["tilemap"].items():
self.tilemap[key] = Tile.from_dict(tile_data)
2026-06-12 14:43:28 +00:00
# Offgrid-Tiles: Dict -> Tile-Objekte
self.offgrid_tiles: list[Tile] = [
2026-06-12 14:43:28 +00:00
Tile.from_dict(t) for t in map_data["offgrid"]
]
2026-06-12 14:43:28 +00:00
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] = []
2026-02-06 17:51:57 +00:00
for tile in self.tiles_around(pos):
if tile.type in PHYSICS_TILES:
2026-06-12 14:43:28 +00:00
rects.append(
pygame.Rect(
tile.pos[0] * self.tile_size,
tile.pos[1] * self.tile_size,
self.tile_size,
self.tile_size,
)
)
2026-02-06 17:51:57 +00:00
return rects
2026-06-12 14:43:28 +00:00
def autotile(self):
2026-04-24 16:45:31 +00:00
for loc in self.tilemap:
tile: Tile = self.tilemap[loc]
neighbors: set[tuple[int, int]] = set()
2026-04-24 16:45:31 +00:00
for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
2026-06-12 14:43:28 +00:00
check_loc: str = (
str(tile.pos[0] + shift[0]) + ";" + str(tile.pos[1] + shift[1])
)
2026-04-24 16:45:31 +00:00
if check_loc in self.tilemap:
if self.tilemap[check_loc].type == tile.type:
2026-04-24 16:45:31 +00:00
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]
2026-06-12 14:43:28 +00:00
def render(
self, surface: pygame.Surface, offset: tuple[float, float] = (0, 0)
) -> None:
for tile in self.offgrid_tiles:
surface.blit(
2026-06-12 14:43:28 +00:00
self.game.assets[tile.type][tile.variant],
(tile.pos[0] - offset[0], tile.pos[1] - offset[1]),
)
2026-06-12 14:43:28 +00:00
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(
2026-06-12 14:43:28 +00:00
self.game.assets[tile.type][tile.variant],
(
tile.pos[0] * self.tile_size - offset[0],
tile.pos[1] * self.tile_size - offset[1],
),
)