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

154 lines
5.1 KiB
Python
Raw Normal View History

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
from scripts.models import Tile
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
self.tile_size = tile_size
self.tilemap = {}
self.offgrid_tiles = []
for i in range(10):
2026-04-24 16:45:31 +00:00
self.tilemap[str(3 + i) + ";10"] = Tile("grass", 1, (3 + i, 10))
self.tilemap["10;" + str(5 + i)] = Tile("stone", 1, (10, i + 5))
2026-04-17 16:04:33 +00:00
2026-04-24 16:45:31 +00:00
def render(self, surface: pg.Surface, offset: tuple = (0, 0)):
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,
):
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],
),
)
def tiles_around(self, pos: list):
2026-02-06 17:51:57 +00:00
tiles = []
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-04-24 16:45:31 +00:00
def physics_rects_around(self, pos: list) -> list:
2026-02-06 17:51:57 +00:00
# Erzeuge eine leere Liste für die Rechtecke
rects = []
# 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-04-24 16:45:31 +00:00
def autotile(self):
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:
2026-04-17 16:04:33 +00:00
2026-04-24 16:45:31 +00:00
data = {
"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-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-04-24 16:45:31 +00:00
def load(self, path: str):
"""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)
self.tilemap: dict[str:Tile] = {}
self.offgrid_tiles: list[Tile] = []
for key, data in map_data.items():
self.tilemap[key] = Tile.from_dict(data)
2026-02-06 17:51:57 +00:00
2026-04-24 16:45:31 +00:00
for tile in map_data["offgrid"]:
self.offgrid_tiles.append(Tile.from_dict(tile))