2026-02-06 17:51:57 +00:00
|
|
|
import pygame as pg
|
2026-04-17 16:04:33 +00:00
|
|
|
import json
|
2026-04-24 16:45:31 +00:00
|
|
|
|
|
|
|
|
NEIGHBOR_OFFSETS = [
|
|
|
|
|
(0, 0),
|
|
|
|
|
(-1, 0),
|
|
|
|
|
(-1, -1),
|
|
|
|
|
(-1, 1),
|
|
|
|
|
(0, 1),
|
|
|
|
|
(0, -1),
|
|
|
|
|
(1, 1),
|
|
|
|
|
(1, 0),
|
|
|
|
|
(1, -1),
|
|
|
|
|
]
|
2026-02-06 17:51:57 +00:00
|
|
|
PHYSICS_TILES = {"grass", "stone"}
|
2026-04-24 16:45:31 +00:00
|
|
|
AUTOTILE_TYPES = {"grass", "stone"}
|
2026-04-17 16:04:33 +00:00
|
|
|
AUTOTILE_MAP = {
|
|
|
|
|
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-04-24 16:45:31 +00:00
|
|
|
|
2026-02-06 17:51:57 +00:00
|
|
|
class Tilemap:
|
2026-04-24 16:45:31 +00:00
|
|
|
"""Eine Map aus Tiles."""
|
|
|
|
|
|
|
|
|
|
def __init__(self, game, tile_size: int = 16):
|
2026-02-06 17:51:57 +00:00
|
|
|
self.game = game
|
2026-05-11 08:37:37 +00:00
|
|
|
self.tile_size: int = tile_size
|
|
|
|
|
self.tilemap: dict[str, dict] = {}
|
|
|
|
|
self.offgrid_tiles: list[dict] = []
|
2026-02-06 17:51:57 +00:00
|
|
|
for i in range(10):
|
2026-05-11 08:19:31 +00:00
|
|
|
self.tilemap[str(3 + i) + ";10"] = {
|
|
|
|
|
"type": "grass",
|
|
|
|
|
"variant": 1,
|
|
|
|
|
"pos": (3 + i, 10),
|
|
|
|
|
}
|
|
|
|
|
self.tilemap["10;" + str(5 + i)] = {
|
|
|
|
|
"type": "stone",
|
|
|
|
|
"variant": 1,
|
|
|
|
|
"pos": (10, i + 5),
|
|
|
|
|
}
|
2026-04-17 16:04:33 +00:00
|
|
|
|
2026-05-11 08:37:37 +00:00
|
|
|
def render(self, surface: pg.Surface, offset: tuple[int, int] = (0, 0)) -> None:
|
2026-02-06 17:51:57 +00:00
|
|
|
for tile in self.offgrid_tiles:
|
2026-04-24 16:45:31 +00:00
|
|
|
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,
|
|
|
|
|
):
|
2026-02-13 17:34:59 +00:00
|
|
|
location_key = f"{x};{y}"
|
|
|
|
|
if location_key in self.tilemap:
|
|
|
|
|
tile = self.tilemap[location_key]
|
2026-04-24 16:45:31 +00:00
|
|
|
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],
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
2026-05-11 08:37:37 +00:00
|
|
|
def tiles_around(self, pos: list[float]) -> list[dict]:
|
|
|
|
|
tiles: list[dict] = []
|
2026-02-06 17:51:57 +00:00
|
|
|
tile_location = (int(pos[0] // self.tile_size), int(pos[1] // self.tile_size))
|
|
|
|
|
for offset in NEIGHBOR_OFFSETS:
|
2026-04-24 16:45:31 +00:00
|
|
|
check_location = (
|
|
|
|
|
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])
|
2026-04-24 16:45:31 +00:00
|
|
|
|
2026-02-06 17:51:57 +00:00
|
|
|
return tiles
|
2026-02-13 17:34:59 +00:00
|
|
|
|
2026-05-11 08:37:37 +00:00
|
|
|
def physics_rects_around(self, pos: list[float]) -> list[pg.Rect]:
|
2026-02-06 17:51:57 +00:00
|
|
|
# Erzeuge eine leere Liste für die Rechtecke
|
2026-05-11 08:37:37 +00:00
|
|
|
rects: list[pg.Rect] = []
|
2026-02-06 17:51:57 +00:00
|
|
|
# Durchlaufe alle Tiles aus der Umgebung (tiles_around)
|
|
|
|
|
for tile in self.tiles_around(pos):
|
|
|
|
|
# Prüfe, ob der Tile-Typ in PHYSICS_TILES enthalten ist
|
|
|
|
|
# (nur diese Tiles sollen kollidieren)
|
|
|
|
|
if tile["type"] in PHYSICS_TILES:
|
|
|
|
|
# Rechne die Tile-Position in Pixel-Koordinaten um
|
2026-04-24 16:45:31 +00:00
|
|
|
x_pixel = tile["pos"][0] * self.tile_size
|
|
|
|
|
y_pixel = tile["pos"][1] * self.tile_size
|
2026-02-06 17:51:57 +00:00
|
|
|
|
|
|
|
|
# Erzeuge ein pygame.Rect mit:
|
|
|
|
|
# - x_pixel
|
|
|
|
|
# - y_pixel
|
|
|
|
|
# - Breite = self.tile_size
|
|
|
|
|
# - Höhe = self.tile_size
|
|
|
|
|
rect = pg.Rect(x_pixel, y_pixel, self.tile_size, self.tile_size)
|
|
|
|
|
# Füge das Rechteck der Liste rects hinzu
|
|
|
|
|
rects.append(rect)
|
|
|
|
|
|
|
|
|
|
# Gib die Liste der Rechtecke zurück
|
|
|
|
|
return rects
|
2026-04-17 16:04:33 +00:00
|
|
|
|
2026-05-11 08:37:37 +00:00
|
|
|
def autotile(self) -> None:
|
2026-04-17 16:04:33 +00:00
|
|
|
|
2026-04-24 16:45:31 +00:00
|
|
|
for loc in self.tilemap:
|
|
|
|
|
tile = self.tilemap[loc]
|
2026-04-17 16:04:33 +00:00
|
|
|
|
2026-04-24 16:45:31 +00:00
|
|
|
neighbors = set()
|
2026-04-17 16:04:33 +00:00
|
|
|
|
2026-04-24 16:45:31 +00:00
|
|
|
for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
|
|
|
|
|
check_loc = (
|
|
|
|
|
str(tile["pos"][0] + shift[0])
|
|
|
|
|
+ ";"
|
|
|
|
|
+ str(tile["pos"][1] + shift[1])
|
|
|
|
|
)
|
2026-04-17 16:04:33 +00:00
|
|
|
|
2026-04-24 16:45:31 +00:00
|
|
|
if check_loc in self.tilemap:
|
|
|
|
|
if self.tilemap[check_loc]["type"] == tile["type"]:
|
|
|
|
|
neighbors.add(shift)
|
2026-04-17 16:04:33 +00:00
|
|
|
|
2026-04-24 16:45:31 +00:00
|
|
|
neighbors = tuple(sorted(neighbors))
|
2026-04-17 16:04:33 +00:00
|
|
|
|
2026-04-24 16:45:31 +00:00
|
|
|
if (tile["type"] in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP):
|
|
|
|
|
tile["variant"] = AUTOTILE_MAP[neighbors]
|
2026-04-17 16:04:33 +00:00
|
|
|
|
2026-04-24 16:45:31 +00:00
|
|
|
def save(self, path: str) -> None:
|
|
|
|
|
data = {
|
2026-05-11 08:19:31 +00:00
|
|
|
"tilemap": self.tilemap,
|
2026-04-24 16:45:31 +00:00
|
|
|
"tile_size": self.tile_size,
|
2026-05-11 08:19:31 +00:00
|
|
|
"offgrid": self.offgrid_tiles,
|
2026-04-24 16:45:31 +00:00
|
|
|
}
|
2026-04-17 16:04:33 +00:00
|
|
|
|
|
|
|
|
with open(path, "w") as f:
|
2026-04-24 16:45:31 +00:00
|
|
|
json.dump(data, f)
|
2026-04-17 16:04:33 +00:00
|
|
|
|
2026-05-11 08:19:31 +00:00
|
|
|
def load(self, path: str) -> None:
|
2026-04-24 16:45:31 +00:00
|
|
|
"""Lädt die gespeicherte Tilemap."""
|
2026-04-17 16:04:33 +00:00
|
|
|
|
|
|
|
|
with open(path, "r") as f:
|
2026-04-24 16:45:31 +00:00
|
|
|
map_data: dict = json.load(f)
|
|
|
|
|
|
2026-05-11 08:19:31 +00:00
|
|
|
self.tilemap: dict[str, dict] = map_data["tilemap"]
|
|
|
|
|
self.tile_size: int = map_data["tile_size"]
|
|
|
|
|
self.offgrid_tiles: list[dict] = map_data["offgrid"]
|