diff --git a/editor.py b/editor.py index 2dbefb2..85dbf7b 100644 --- a/editor.py +++ b/editor.py @@ -1,11 +1,11 @@ import pygame import sys -from scripts.tilemap import * -from scripts.clouds import Clouds +from scripts.tilemap import Tilemap from scripts.utils import load_images RENDER_SCALE = 2.0 + class better_list(list): def __init__(self, iterable): super().__init__(iterable) @@ -15,20 +15,16 @@ class Editor: def __init__(self): pygame.init() - pygame.display.set_caption("Editor") self.screen = pygame.display.set_mode((640, 480)) self.display = pygame.Surface((320, 240)) - - self.clock = pygame.time.Clock() self.running = True self.collision_area = pygame.Rect(50, 50, 300, 50) self.movement = [False, False, False, False] - self.assets = { "decor": load_images("tiles/decor"), "grass": load_images("tiles/grass"), @@ -39,10 +35,10 @@ class Editor: self.tilemap = Tilemap(self, 16) try: - self.tilemap.load('map.json') + self.tilemap.load("map.json") except FileNotFoundError: pass - + self.isJumping = False self.scroll = [0, 0] self.tile_list = better_list(self.assets) @@ -60,55 +56,71 @@ class Editor: def run(self): while self.running: - self.display.fill((0, 0, 0)) self.scroll[0] += (self.movement[1] - self.movement[0]) * 3 self.scroll[1] += (self.movement[3] - self.movement[2]) * 3 - render_scroll = (int(self.scroll[0]), int(self.scroll[1])) self.tilemap.render(surface=self.display, offset=render_scroll) - current_tile_img = self.assets[self.tile_list[self.tile_group]][self.tile_variant].copy() + current_tile_img = self.assets[self.tile_list[self.tile_group]][ + self.tile_variant + ].copy() current_tile_img.set_alpha(100) mpos = pygame.mouse.get_pos() mpos = (mpos[0] / RENDER_SCALE, mpos[1] / RENDER_SCALE) - tile_pos = (int((mpos[0] + self.scroll[0]) // self.tilemap.tile_size), - int((mpos[1] + self.scroll[1]) // self.tilemap.tile_size)) + tile_pos = ( + int((mpos[0] + self.scroll[0]) // self.tilemap.tile_size), + int((mpos[1] + self.scroll[1]) // self.tilemap.tile_size), + ) # print(tile_pos[0] * self.tilemap.tile_size + self.scroll[0], tile_pos[1]*self.tilemap.tile_size + self.scroll[1]) # print(mpos) if self.on_grid: - - self.display.blit(current_tile_img, (tile_pos[0] * self.tilemap.tile_size - self.scroll[0], tile_pos[1]*self.tilemap.tile_size - self.scroll[1])) + self.display.blit( + current_tile_img, + ( + tile_pos[0] * self.tilemap.tile_size - self.scroll[0], + tile_pos[1] * self.tilemap.tile_size - self.scroll[1], + ), + ) else: - self.display.blit(current_tile_img, (mpos[0], mpos[1])) - if self.clicking: if self.on_grid: - self.tilemap.tilemap[str(tile_pos[0]) + ';' + str(tile_pos[1])] = { - 'type': self.tile_list[self.tile_group], - 'variant': self.tile_variant, - 'pos': tile_pos + self.tilemap.tilemap[str(tile_pos[0]) + ";" + str(tile_pos[1])] = { + "type": self.tile_list[self.tile_group], + "variant": self.tile_variant, + "pos": tile_pos, } if not self.on_grid: - self.tilemap.offgrid_tiles.append({"type": self.tile_list[self.tile_group], "variant": self.tile_variant, "pos": (mpos[0]+self.scroll[0], mpos[1]+self.scroll[1])}) + self.tilemap.offgrid_tiles.append( + { + "type": self.tile_list[self.tile_group], + "variant": self.tile_variant, + "pos": (mpos[0] + self.scroll[0], mpos[1] + self.scroll[1]), + } + ) if self.right_clicking: - tile_loc = str(tile_pos[0]) + ';' + str(tile_pos[1]) + tile_loc = str(tile_pos[0]) + ";" + str(tile_pos[1]) if tile_loc in self.tilemap.tilemap: del self.tilemap.tilemap[tile_loc] for offi_tile in self.tilemap.offgrid_tiles.copy(): - offi_tile_img = self.assets[offi_tile['type']][offi_tile['variant']] - offi_tile_r = pygame.Rect(offi_tile["pos"][0]-self.scroll[0], offi_tile["pos"][1]-self.scroll[1], offi_tile_img.get_width(), offi_tile_img.get_height()) + offi_tile_img = self.assets[offi_tile["type"]][offi_tile["variant"]] + offi_tile_r = pygame.Rect( + offi_tile["pos"][0] - self.scroll[0], + offi_tile["pos"][1] - self.scroll[1], + offi_tile_img.get_width(), + offi_tile_img.get_height(), + ) if offi_tile_r.collidepoint(mpos): self.tilemap.offgrid_tiles.remove(offi_tile) self.display.blit(current_tile_img, (5, 5)) @@ -116,14 +128,15 @@ class Editor: for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False - - if event.type == pygame.MOUSEBUTTONUP: - if event.button == 1: self.clicking = False - if event.button == 3: self.right_clicking = False + if event.type == pygame.MOUSEBUTTONUP: + if event.button == 1: + self.clicking = False + + if event.button == 3: + self.right_clicking = False if event.type == pygame.KEYDOWN: - if event.key == pygame.K_ESCAPE: self.running = False @@ -144,17 +157,15 @@ class Editor: if event.key == pygame.K_g: self.on_grid = not self.on_grid - + if event.key == pygame.K_o: - self.tilemap.save('map.json') + self.tilemap.save("map.json") print("Map gespeichert") if event.key == pygame.K_t: self.tilemap.autotile() - if event.type == pygame.KEYUP: - if event.key == pygame.K_a: self.movement[0] = False @@ -178,13 +189,16 @@ class Editor: if event.button == 4: if self.shifting: - if self.tile_variant >= len(self.assets[self.tile_list[self.tile_group]])-1: + if ( + self.tile_variant + >= len(self.assets[self.tile_list[self.tile_group]]) - 1 + ): self.tile_variant = 0 else: self.tile_variant += 1 else: - if self.tile_group >= len(self.tile_list)-1: + if self.tile_group >= len(self.tile_list) - 1: self.tile_group = 0 self.tile_variant = 0 else: @@ -193,32 +207,34 @@ class Editor: if event.button == 5: if self.shifting: - if self.tile_variant <= 0: - self.tile_variant = len(self.assets[self.tile_list[self.tile_group]])-1 # Lösung vom Problem musste das von oben nehmen: len(self.assets[self.tile_list[self.tile_group]])-1 + self.tile_variant = ( + len(self.assets[self.tile_list[self.tile_group]]) + - 1 + ) # Lösung vom Problem musste das von oben nehmen: len(self.assets[self.tile_list[self.tile_group]])-1 else: self.tile_variant -= 1 else: if self.tile_group <= 0: - self.tile_group = len(self.tile_list)-1 + self.tile_group = len(self.tile_list) - 1 self.tile_variant = 0 else: self.tile_group -= 1 self.tile_variant = 0 - self.zaehler += 1 if self.zaehler >= self.sekunde: self.zaehler = 0 # print(f"{self.tile_group} + {self.tile_variant}") - self.screen.blit(pygame.transform.scale(self.display, self.screen.get_size()), (0,0)) + self.screen.blit( + pygame.transform.scale(self.display, self.screen.get_size()), (0, 0) + ) pygame.display.update() self.clock.tick(60) - pygame.quit() sys.exit() diff --git a/game.py b/game.py index 0e19166..d368f47 100644 --- a/game.py +++ b/game.py @@ -110,8 +110,6 @@ class Game: game = Game() -if __name__ == "__main__": - # game.testolino() - pass + game.run() diff --git a/scripts/entities.py b/scripts/entities.py index bc483c5..42be0eb 100644 --- a/scripts/entities.py +++ b/scripts/entities.py @@ -1,24 +1,29 @@ import pygame -class PhysicsEntity: +from scripts.tilemap import Tilemap - def __init__(self, game, e_type, pos:tuple, size): + +class PhysicsEntity: + def __init__(self, game, e_type, pos: tuple, size): self.game = game self.e_type = e_type self.pos = list(pos) self.size = size - self.velocity = [0,0] - self.collisions = {"top":False, "bottom":False, "left":False, "right":False} + self.velocity = [0, 0] + self.collisions = {"top": False, "bottom": False, "left": False, "right": False} self.speed = 3 # Animationen self.action = "" self.flip = False self.animation_offset = (-3, -3) - self.set_action('idle') + self.set_action("idle") - def update(self, tilemap, movement:tuple=(0,0)): - self.collisions = {"top":False, "bottom":False, "left":False, "right":False} - frame_movement = ((movement[0] + self.velocity[0])*self.speed, movement[1] + self.velocity[1]) + def update(self, tilemap: Tilemap, movement: tuple = (0, 0)): + self.collisions = {"top": False, "bottom": False, "left": False, "right": False} + frame_movement = ( + (movement[0] + self.velocity[0]) * self.speed, + movement[1] + self.velocity[1], + ) self.pos[0] += frame_movement[0] entity_rect = self.rect() @@ -29,21 +34,18 @@ class PhysicsEntity: self.collisions["right"] = True self.game.isJumping = False - if frame_movement[0] < 0: entity_rect.left = recto.right self.collisions["left"] = True self.game.isJumping = False - self.pos[0] = entity_rect.x - + self.pos[1] += frame_movement[1] entity_rect = self.rect() for rectolino in tilemap.physics_rects_around(self.pos): if entity_rect.colliderect(rectolino): - if frame_movement[1] > 0: entity_rect.bottom = rectolino.top self.collisions["bottom"] = True @@ -54,26 +56,31 @@ class PhysicsEntity: self.collisions["top"] = True self.pos[1] = entity_rect.y - + if movement[0] > 0: self.flip = False if movement[0] < 0: self.flip = True - - if self.collisions['bottom'] or self.collisions['top']: - self.velocity[1] = 0 + if self.collisions["bottom"] or self.collisions["top"]: + self.velocity[1] = 0 self.velocity[1] = min(5, self.velocity[1] + 0.1) self.animation.update() - def render(self, surface:pygame.Surface, offset:tuple=(0,0)): - surface.blit(pygame.transform.flip(self.animation.img(), self.flip, False), (self.pos[0] - offset[0] + self.animation_offset[0],self.pos[1] - offset[1] + self.animation_offset[1])) - + def render(self, surface: pygame.Surface, offset: tuple = (0, 0)): + surface.blit( + pygame.transform.flip(self.animation.img(), self.flip, False), + ( + self.pos[0] - offset[0] + self.animation_offset[0], + self.pos[1] - offset[1] + self.animation_offset[1], + ), + ) + def rect(self): return pygame.Rect(self.pos[0], self.pos[1], self.size[0], self.size[1]) - + def set_action(self, action): if action != self.action: self.action = action @@ -86,7 +93,7 @@ class Player(PhysicsEntity): super().__init__(game, "player", pos, size) self.air_time = 0 # Neu! Zählt, wie lange wir fallen/springe - def update(self, tilemap, movement = (0, 0)): + def update(self, tilemap, movement=(0, 0)): super().update(tilemap, movement) self.air_time += 1 @@ -95,13 +102,12 @@ class Player(PhysicsEntity): self.air_time = 0 # Wenn wir länger als 4 Frames in der Luft sind -> Springen! if self.air_time > 4: - self.set_action('jump') - + self.set_action("jump") + # Wenn wir nicht in der Luft sind, aber uns bewegen -> Rennen! elif movement[0] != 0: - self.set_action('run') - + self.set_action("run") + # Sonst -> Rumstehen! else: - self.set_action('idle') - \ No newline at end of file + self.set_action("idle") diff --git a/scripts/models.py b/scripts/models.py index 38d445d..101b919 100644 --- a/scripts/models.py +++ b/scripts/models.py @@ -9,15 +9,21 @@ class Tile: def grid_key(self) -> str: """Erzeugt den String-Key für die Tilemap.""" - return f"{self.pos[0]};{self.pos[1]}" - + return f"{self.pos[0]};{self.post[1]}" + def to_dict(self) -> dict: """Konvertiert zurück in ein Dict (für JSON-Speicherung).""" - - return {"type": self.type, "variant": self.variant, "pos": list(self.pos)} - + return { + 'type': self.type, + 'variant': self.variant, + 'pos': list(self.post) + } + @staticmethod - def from_dict(data: dict) -> "Tile": + def from_dict(data: dict) -> 'Tile': """Erstellt ein Tile aus einem Dictionary.""" - - return Tile(type=data["type"], variant=data["variant"], pos=data["pos"]) + return Tile( + type=data['type'], + variant=data['variant'], + pos=list(data['pos']), + ) \ No newline at end of file diff --git a/scripts/tilemap.py b/scripts/tilemap.py index ec7687b..ad7d0a0 100644 --- a/scripts/tilemap.py +++ b/scripts/tilemap.py @@ -1,9 +1,20 @@ import pygame as pg import json +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)] +NEIGHBOR_OFFSETS = [ + (0, 0), + (-1, 0), + (-1, -1), + (-1, 1), + (0, 1), + (0, -1), + (1, 1), + (1, 0), + (1, -1), +] PHYSICS_TILES = {"grass", "stone"} -AUTOTILE_TYPES = {'grass', 'stone'} +AUTOTILE_TYPES = {"grass", "stone"} AUTOTILE_MAP = { tuple(sorted([(1, 0), (0, 1)])): 0, tuple(sorted([(1, 0), (0, 1), (-1, 0)])): 1, @@ -16,50 +27,69 @@ AUTOTILE_MAP = { tuple(sorted([(1, 0), (-1, 0), (0, 1), (0, -1)])): 8, } + class Tilemap: - def __init__(self, game, tile_size=16): + """Eine Map aus Tiles.""" + + def __init__(self, game, tile_size: int = 16): self.game = game self.tile_size = tile_size self.tilemap = {} self.offgrid_tiles = [] for i in range(10): - 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)} + self.tilemap[str(3 + i) + ";10"] = Tile("grass", 1, (3 + i, 10)) + self.tilemap["10;" + str(5 + i)] = Tile("stone", 1, (10, i + 5)) - def render(self, surface:pg.Surface, offset:tuple=(0,0)): + def render(self, surface: pg.Surface, offset: tuple = (0, 0)): 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): + 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] - 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])) + 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): + def tiles_around(self, pos: list): tiles = [] tile_location = (int(pos[0] // self.tile_size), int(pos[1] // self.tile_size)) for offset in NEIGHBOR_OFFSETS: - check_location = str(tile_location[0] + offset[0]) + ';' + str(tile_location[1] + offset[1]) + check_location = ( + 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 physics_rects_around(self, pos) -> list: + def physics_rects_around(self, pos: list) -> list: # 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 - x_pixel = tile['pos'][0] * self.tile_size - y_pixel = tile['pos'][1] * self.tile_size + x_pixel = tile["pos"][0] * self.tile_size + y_pixel = tile["pos"][1] * self.tile_size # Erzeuge ein pygame.Rect mit: # - x_pixel @@ -72,44 +102,52 @@ class Tilemap: # Gib die Liste der Rechtecke zurück return rects - + def autotile(self): - for loc in self.tilemap: + for loc in self.tilemap: + tile = self.tilemap[loc] - tile = self.tilemap[loc] + neighbors = set() - neighbors = set() + 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]) + ) - for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]: + if check_loc in self.tilemap: + if self.tilemap[check_loc]["type"] == tile["type"]: + neighbors.add(shift) - check_loc = str(tile['pos'][0] + shift[0]) + ';' + str(tile['pos'][1] + shift[1]) + neighbors = tuple(sorted(neighbors)) - if check_loc in self.tilemap: + if (tile["type"] in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP): + tile["variant"] = AUTOTILE_MAP[neighbors] - if self.tilemap[check_loc]['type'] == tile['type']: + def save(self, path: str) -> None: - neighbors.add(shift) + 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], + } - neighbors = tuple(sorted(neighbors)) - - if (tile['type'] in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP): - - tile['variant'] = AUTOTILE_MAP[neighbors] - - - def save(self, path): - with open(path, "w") as f: + json.dump(data, f) - json.dump({'tilemap': self.tilemap, 'tile_size': self.tile_size, 'offgrid': self.offgrid_tiles}, f) + def load(self, path: str): + """Lädt die gespeicherte Tilemap.""" - - def load(self, path): with open(path, "r") as f: - map_data = json.load(f) + map_data: dict = json.load(f) - self.tilemap = map_data['tilemap'] - self.tile_size = map_data['tile_size'] - self.offgrid_tiles = map_data['offgrid'] - + self.tilemap: dict[str:Tile] = {} + self.offgrid_tiles: list[Tile] = [] + + for key, data in map_data.items(): + self.tilemap[key] = Tile.from_dict(data) + + for tile in map_data["offgrid"]: + self.offgrid_tiles.append(Tile.from_dict(tile))