import pygame import sys import os import copy from Levels import Level22 as Level pygame.init() FONT = pygame.font.SysFont(None, 18) # Config PROJECT_DIR = os.path.dirname(__file__) LEVEL_MODULE = Level # Derive the level file path and name dynamically from the imported module try: LEVEL_PATH = getattr(LEVEL_MODULE, "__file__", None) or os.path.join(PROJECT_DIR, "Level.py") LEVEL_PATH = os.path.abspath(LEVEL_PATH) LEVEL_NAME = os.path.splitext(os.path.basename(LEVEL_PATH))[0] except Exception: LEVEL_PATH = os.path.join(PROJECT_DIR, "Level.py") LEVEL_NAME = os.path.splitext(os.path.basename(LEVEL_PATH))[0] data = copy.deepcopy(getattr(LEVEL_MODULE, "data_svet", [])) if not data: print(f"No data_svet found in {LEVEL_NAME}.py") sys.exit(1) ROWS = len(data) COLS = len(data[0]) if ROWS > 0 else 0 # Palette of commonly used tile ids (customize as needed) PALETTE = [0, 1, 4, 5, 6, 7, 9, 10, 11, 12, 55, 99] # 0=empty, 99=player spawn PALETTE_NAMES = { 0: "EMPTY", 1: "GROUND", 4: "SHOOTER", 5: "TURRET", 6: "SPIKE_S", 7: "SPIKE_T", 9: "DOOR", 10: "ENEMY", 11: "JUMP1", 12: "JUMP2", 55: "SLOW_TURRET", 99: "PLAYER" } # Determine window size and tile size to fit comfortably SCREEN_W = max(800, min(1400, COLS * 48 + 300)) SCREEN_H = max(600, min(900, ROWS * 48 + 200)) tile_size = max(12, min(96, min((SCREEN_W - 300) // COLS if COLS else 12, (SCREEN_H - 120) // ROWS if ROWS else 12))) # Preload textures if available (try common names) TEXTURES = { 1: r"Textures\ground_block_normal.png", 4: r"Textures\enemy_pistol.png", 5: r"Textures\Turret.png", 6: r"Textures\spike_short.png", 7: r"Textures\spike_tall.png", 9: r"Textures\door.png", 10: r"Textures\enemy.png", 11: r"Textures\crawler2.png", 12: r"Textures\crawler2.png", 55: r"Textures\Turret.png", 99: r"Textures\Player.png" } palette_surfaces = {} for tid in PALETTE: path = TEXTURES.get(tid) surf = None if path: full = os.path.join(PROJECT_DIR, path) try: surf = pygame.image.load(full).convert_alpha() surf = pygame.transform.smoothscale(surf, (tile_size, tile_size)) except Exception: try: # try forward-slash variant surf = pygame.image.load(full.replace("\\", "/")).convert_alpha() surf = pygame.transform.smoothscale(surf, (tile_size, tile_size)) except Exception: surf = None if surf is None: # fallback colored tile surf = pygame.Surface((tile_size, tile_size), pygame.SRCALPHA) colors = { 0: (40, 40, 40, 100), 1: (100, 100, 100), 4: (180, 80, 80), 5: (120, 120, 220), 6: (200, 120, 40), 7: (180, 60, 60), 9: (140, 100, 60), 10: (160, 40, 40), 11: (160, 80, 40), 12: (120, 200, 80), 55: (120, 120, 40), 99: (40, 200, 120), } c = colors.get(tid, (200, 200, 200)) surf.fill(c) palette_surfaces[tid] = surf screen = pygame.display.set_mode((SCREEN_W, SCREEN_H)) pygame.display.set_caption(f"Level Editor - {LEVEL_NAME}") clock = pygame.time.Clock() # Layout grid_x = 20 grid_y = 20 grid_w = COLS * tile_size grid_h = ROWS * tile_size palette_x = grid_x + grid_w + 20 palette_y = grid_y selected_idx = 1 if 1 in PALETTE else 0 selected_tile = PALETTE[selected_idx] mouse_down = False eraser_mode = False def draw_ui(): screen.fill((30, 30, 30)) # draw grid background pygame.draw.rect(screen, (18, 18, 18), (grid_x - 2, grid_y - 2, grid_w + 4, grid_h + 4)) # draw tiles for r in range(ROWS): for c in range(COLS): tid = data[r][c] x = grid_x + c * tile_size y = grid_y + r * tile_size # draw cell bg pygame.draw.rect(screen, (60, 60, 60), (x, y, tile_size, tile_size)) if tid != 0: surf = palette_surfaces.get(tid) if surf: screen.blit(surf, (x, y)) else: # fallback small colored rect pygame.draw.rect(screen, (200, 0, 0), (x + 2, y + 2, tile_size - 4, tile_size - 4)) pygame.draw.rect(screen, (20, 20, 20), (x, y, tile_size, tile_size), 1) # palette title t = FONT.render("Palette (keys 0-9 / P / Arrows) S=Save Esc=Quit", True, (220, 220, 220)) screen.blit(t, (palette_x, palette_y)) # draw palette entries py = palette_y + 28 for i, tid in enumerate(PALETTE): sx = palette_x sy = py + i * (tile_size + 8) # highlight selection if tid == selected_tile: pygame.draw.rect(screen, (255, 200, 60), (sx - 4, sy - 4, tile_size + 8, tile_size + 8), 3) screen.blit(palette_surfaces[tid], (sx, sy)) name = PALETTE_NAMES.get(tid, str(tid)) lbl = FONT.render(f"{tid} - {name}", True, (230, 230, 230)) screen.blit(lbl, (sx + tile_size + 8, sy + (tile_size - lbl.get_height()) // 2)) # instructions info_y = palette_y + max(len(PALETTE), 6) * (tile_size + 8) + 8 lines = [ f"Selected: {selected_tile} ({PALETTE_NAMES.get(selected_tile)})", "Left drag: paint | Right click: erase | P: player spawn (99)", "S: save -> overwrites Level.py data_svet", ] for i, ln in enumerate(lines): screen.blit(FONT.render(ln, True, (200, 200, 200)), (palette_x, info_y + i * 20)) def screen_to_cell(mx, my): if mx < grid_x or my < grid_y or mx >= grid_x + grid_w or my >= grid_y + grid_h: return None c = (mx - grid_x) // tile_size r = (my - grid_y) // tile_size if 0 <= r < ROWS and 0 <= c < COLS: return int(r), int(c) return None def place_tile_at(mouse_pos, tile_id): cell = screen_to_cell(*mouse_pos) if not cell: return r, c = cell data[r][c] = tile_id def save_level(): # Write data_svet back into the imported level module's file (simple pretty print) header = "# Auto-generated by LevelEditor - overwrites data_svet\n" header += "data_svet = [\n" body_lines = [] for row in data: body_lines.append(" " + repr(list(row)) + ",\n") footer = "]\n" try: with open(LEVEL_PATH, "w", encoding="utf-8") as f: f.write(header) for ln in body_lines: f.write(ln) f.write(footer) # Update imported module in memory try: LEVEL_MODULE.data_svet = data except Exception: pass print(f"Saved {LEVEL_NAME}.py") except Exception as e: print("Failed to save:", e) running = True while running: clock.tick(60) mx, my = pygame.mouse.get_pos() for ev in pygame.event.get(): if ev.type == pygame.QUIT: running = False elif ev.type == pygame.KEYDOWN: if ev.key == pygame.K_ESCAPE: running = False elif ev.key == pygame.K_s: save_level() elif ev.key == pygame.K_p: selected_tile = 99 elif ev.key in (pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN): # change selection with arrows if ev.key == pygame.K_LEFT: selected_idx = max(0, selected_idx - 1) elif ev.key == pygame.K_RIGHT: selected_idx = min(len(PALETTE) - 1, selected_idx + 1) elif ev.key == pygame.K_UP: selected_idx = max(0, selected_idx - 1) elif ev.key == pygame.K_DOWN: selected_idx = min(len(PALETTE) - 1, selected_idx + 1) selected_tile = PALETTE[selected_idx] else: # number keys quick-select (0..9) if ev.unicode and ev.unicode.isdigit(): val = int(ev.unicode) if val in PALETTE: selected_idx = PALETTE.index(val) selected_tile = val elif ev.type == pygame.MOUSEBUTTONDOWN: mouse_down = True # NEW: allow clicking palette entries to select them if ev.button == 1: # left # palette entries start at (palette_x, palette_y + 28), each entry height (tile_size + 8) pal_top = palette_y + 28 pal_left = palette_x pal_right = palette_x + (tile_size + 200) # allow label area too if pal_left <= mx < pal_right and pal_top <= my < pal_top + len(PALETTE) * (tile_size + 8): rel_y = my - pal_top idx = int(rel_y // (tile_size + 8)) if 0 <= idx < len(PALETTE): selected_idx = idx selected_tile = PALETTE[selected_idx] else: place_tile_at((mx, my), selected_tile) else: place_tile_at((mx, my), selected_tile) elif ev.button == 3: # right = erase # if right-click over palette, treat as no-op for selection; otherwise erase grid cell pal_top = palette_y + 28 pal_left = palette_x pal_right = palette_x + (tile_size + 200) if not (pal_left <= mx < pal_right and pal_top <= my < pal_top + len(PALETTE) * (tile_size + 8)): place_tile_at((mx, my), 0) elif ev.type == pygame.MOUSEBUTTONUP: mouse_down = False elif ev.type == pygame.MOUSEMOTION and mouse_down: if pygame.mouse.get_pressed()[0]: place_tile_at((mx, my), selected_tile) elif pygame.mouse.get_pressed()[2]: place_tile_at((mx, my), 0) draw_ui() # draw mouse hover highlight cell = screen_to_cell(mx, my) if cell: r, c = cell rx = grid_x + c * tile_size ry = grid_y + r * tile_size pygame.draw.rect(screen, (255, 255, 255), (rx, ry, tile_size, tile_size), 2) pygame.display.flip() pygame.quit() sys.exit()