forked from tas/major_tom
Extend moon level to 1000 tiles with wide platform gaps
Expand moon01.lvl from 200 to 1000 tiles using a Python generator script. The level features 27 gap sections with floating one-way platforms at varying heights, progressing from easy (8-tile gaps) to challenging (22-tile gaps with tiny platforms). 20 asteroids spread across the level. Increase tilemap line buffer from 1024 to 16384 bytes to support wide levels.
This commit is contained in:
200
tools/gen_moon_level.py
Normal file
200
tools/gen_moon_level.py
Normal file
@@ -0,0 +1,200 @@
|
||||
#!/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")
|
||||
Reference in New Issue
Block a user