#!/usr/bin/env python3 """Generate the 1000-tile-wide moon level. Layout philosophy: - Start with a flat landing zone (spacecraft intro) - Alternate between ground sections and wide chasms with floating platforms - Chasms get progressively wider / platforms more sparse as the level goes on - Moon gravity (300) gives ~20 tiles of full-jump horizontal range - Gaps are 8-18 tiles wide; platforms are 3-5 tiles wide at varying heights - Ground depth: rows 19-22 (surface at 19, bedrock fills below) - Total: 1000 x 23 tiles """ import random WIDTH = 1000 HEIGHT = 23 GROUND_ROW = 19 # top of ground surface SURFACE_ROWS = 3 # rows of ground below surface (19, 20, 21, 22 = index 19-22) random.seed(42) # reproducible # Tile IDs: # 0 = air # 1 = solid surface # 2 = solid surface variant (decorative, still solid) # 3 = solid surface variant (decorative, still solid) # 4 = one-way platform def make_empty_grid(): return [[0] * WIDTH for _ in range(HEIGHT)] def place_ground(grid, col_start, col_end, row=GROUND_ROW): """Fill ground from col_start to col_end (exclusive), from row down to bottom.""" for r in range(row, HEIGHT): for c in range(col_start, min(col_end, WIDTH)): if r == row: grid[r][c] = 1 elif r == row + 1: # second row: sprinkle variant tiles grid[r][c] = 2 if random.random() < 0.12 else 1 else: # deep rows: more variant grid[r][c] = 3 if random.random() < 0.1 else 1 def place_platform(grid, col_start, length, row): """Place a one-way platform.""" for c in range(col_start, min(col_start + length, WIDTH)): grid[row][c] = 4 def generate_level(): grid = make_empty_grid() entities = [] # === Section 1: Landing zone (cols 0-30) === place_ground(grid, 0, 30) entities.append(("spacecraft", 1, 14)) # === Generate sections with gaps === col = 30 section = 0 # Define gap/ground section patterns # (ground_len, gap_len, platform_configs) # platform_configs: list of (offset_into_gap, length, row) sections = [ # Easy warmup: small gaps, generous platforms (15, 8, [(2, 4, 16)]), (12, 10, [(3, 3, 14)]), (14, 10, [(2, 3, 17), (6, 3, 15)]), # Getting harder: wider gaps (10, 14, [(3, 3, 15), (9, 3, 13)]), (12, 12, [(4, 4, 16)]), (8, 16, [(3, 3, 14), (8, 3, 17), (12, 3, 12)]), # Big chasms with platform chains (10, 18, [(2, 3, 16), (7, 3, 13), (13, 3, 16)]), (6, 14, [(3, 4, 15), (9, 3, 12)]), (12, 16, [(2, 3, 17), (7, 3, 14), (12, 3, 17)]), # Mid-level: varied terrain (20, 10, [(3, 3, 16)]), (8, 18, [(2, 3, 15), (8, 4, 12), (14, 3, 16)]), (15, 8, [(2, 3, 14)]), (6, 16, [(3, 3, 16), (9, 3, 13), (13, 3, 16)]), # Hard section: wide gaps, small platforms (8, 18, [(3, 3, 14), (9, 3, 17), (14, 2, 12)]), (10, 14, [(3, 3, 13), (8, 3, 16)]), (6, 18, [(2, 2, 16), (6, 3, 12), (11, 2, 16), (15, 2, 13)]), (12, 16, [(4, 3, 14), (10, 3, 17)]), # Staircase sections — platforms ascending/descending (10, 20, [(2, 3, 17), (6, 3, 15), (10, 3, 13), (14, 3, 11), (17, 3, 14)]), (8, 16, [(2, 3, 11), (6, 3, 13), (10, 3, 15), (13, 3, 17)]), # Very hard: sparse platforms over huge gaps (15, 18, [(4, 3, 14), (11, 2, 12)]), (6, 20, [(3, 3, 15), (8, 2, 12), (13, 3, 16), (17, 2, 13)]), (10, 16, [(3, 2, 13), (8, 3, 16), (13, 2, 11)]), # Climax: super wide with tiny platforms (8, 22, [(3, 2, 15), (8, 2, 12), (13, 2, 16), (18, 2, 13)]), (6, 18, [(3, 3, 14), (9, 2, 11), (14, 3, 16)]), (10, 20, [(2, 2, 16), (7, 3, 13), (12, 2, 10), (16, 3, 16)]), # Victory approach (12, 14, [(3, 3, 15), (9, 3, 13)]), (8, 10, [(3, 4, 16)]), ] for ground_len, gap_len, platforms in sections: if col + ground_len + gap_len > WIDTH - 30: break # Place ground section place_ground(grid, col, col + ground_len) col += ground_len # Place platforms in the gap for p_offset, p_len, p_row in platforms: place_platform(grid, col + p_offset, p_len, p_row) col += gap_len section += 1 # Fill remaining with ground to the exit end_ground_start = col place_ground(grid, end_ground_start, WIDTH) # === Asteroids: spread across the level, ~1 per 50 tiles in gap areas === asteroid_cols = list(range(60, WIDTH - 40, 45)) random.shuffle(asteroid_cols) asteroid_cols = sorted(asteroid_cols[:20]) # pick ~20, spread out for ac in asteroid_cols: ar = random.randint(0, 3) entities.append(("asteroid", ac, ar)) # === Exit zone near the end === exit_col = WIDTH - 4 exit_row = GROUND_ROW - 2 return grid, entities, exit_col, exit_row def write_level(filename): grid, entities, exit_col, exit_row = generate_level() lines = [] lines.append("# Moon Surface - Landing") lines.append("# ======================") lines.append("# Extended moon level: wide chasms with floating platforms.") lines.append("# Pure jump-and-run intro with asteroid hazards. No gun.") lines.append("") lines.append("TILESET assets/tiles/moon_tileset.png") lines.append(f"SIZE {WIDTH} {HEIGHT}") lines.append("SPAWN 3 18") lines.append("GRAVITY 300") lines.append("BG_COLOR 5 5 15") lines.append("PARALLAX_STYLE 4") lines.append("MUSIC assets/sounds/algardalgar.ogg") lines.append("PLAYER_UNARMED") lines.append("") # Entities for etype, ex, ey in entities: lines.append(f"ENTITY {etype} {ex} {ey}") lines.append("") # Exit lines.append(f"EXIT {exit_col} {exit_row} 2 3 assets/levels/level01.lvl") lines.append("") # Tile definitions lines.append("# Tile definitions") lines.append("TILEDEF 1 0 0 1") lines.append("TILEDEF 2 1 0 1") lines.append("TILEDEF 3 2 0 1") lines.append("TILEDEF 4 0 1 2") lines.append("") # Collision layer lines.append("LAYER collision") for row in grid: lines.append(" ".join(str(t) for t in row)) with open(filename, "w") as f: f.write("\n".join(lines) + "\n") # Print stats gap_tiles = sum(1 for r in range(HEIGHT) for c in range(WIDTH) if grid[GROUND_ROW][c] == 0 and grid[r][c] == 0) plat_count = sum(1 for r in range(HEIGHT) for c in range(WIDTH) if grid[r][c] == 4) print(f"Generated {filename}: {WIDTH}x{HEIGHT} tiles") print(f" Entities: {len(entities)}") print(f" Platforms: {plat_count} tiles") print(f" Ground surface gaps: {sum(1 for c in range(WIDTH) if grid[GROUND_ROW][c] == 0)} columns") if __name__ == "__main__": write_level("assets/levels/moon01.lvl")