McRogueFace/docs/cookbook/procgen/34_advanced_volcanic.py

187 lines
6.4 KiB
Python
Raw Normal View History

2026-01-13 19:42:37 -05:00
"""Advanced: Volcanic Crater Region
Combines: Hills (mountains) + dig_hill (craters) + Voronoi (lava flows) + Erosion + Noise
Creates a volcanic landscape with active lava, ash fields, and rocky terrain.
"""
import mcrfpy
from mcrfpy import automation
GRID_WIDTH, GRID_HEIGHT = 64, 48
CELL_SIZE = 16
def volcanic_color(elevation, lava_intensity, ash_level):
"""Color based on elevation, lava presence, and ash coverage."""
# Lava overrides everything
if lava_intensity > 0.6:
t = (lava_intensity - 0.6) / 0.4
return mcrfpy.Color(
int(200 + t * 55),
int(80 + t * 80),
int(20 + t * 30)
)
elif lava_intensity > 0.4:
# Cooling lava
t = (lava_intensity - 0.4) / 0.2
return mcrfpy.Color(
int(80 + t * 120),
int(30 + t * 50),
int(20)
)
# Check for crater interior (very low elevation)
if elevation < 0.15:
t = elevation / 0.15
return mcrfpy.Color(int(40 + t * 30), int(20 + t * 20), int(10 + t * 15))
# Ash coverage
if ash_level > 0.6:
t = (ash_level - 0.6) / 0.4
base = int(60 + t * 40)
return mcrfpy.Color(base, base - 5, base - 10)
# Normal terrain by elevation
if elevation < 0.3:
# Volcanic plain
t = (elevation - 0.15) / 0.15
return mcrfpy.Color(int(50 + t * 30), int(40 + t * 25), int(35 + t * 20))
elif elevation < 0.5:
# Rocky slopes
t = (elevation - 0.3) / 0.2
return mcrfpy.Color(int(70 + t * 20), int(60 + t * 15), int(50 + t * 15))
elif elevation < 0.7:
# Mountain sides
t = (elevation - 0.5) / 0.2
return mcrfpy.Color(int(85 + t * 25), int(75 + t * 20), int(65 + t * 20))
elif elevation < 0.85:
# High slopes
t = (elevation - 0.7) / 0.15
return mcrfpy.Color(int(100 + t * 30), int(90 + t * 25), int(80 + t * 25))
else:
# Peaks
t = (elevation - 0.85) / 0.15
return mcrfpy.Color(int(130 + t * 50), int(120 + t * 50), int(115 + t * 50))
def run_demo(runtime):
# Step 1: Create base terrain with noise
noise = mcrfpy.NoiseSource(dimensions=2, algorithm='simplex', seed=42)
terrain = mcrfpy.HeightMap((GRID_WIDTH, GRID_HEIGHT), fill=0.3)
terrain.add_noise(noise, world_size=(12, 10), mode='fbm', octaves=4, scale=0.2)
# Step 2: Add volcanic mountains
# Main volcano
terrain.add_hill((GRID_WIDTH // 2, GRID_HEIGHT // 2), 20, 0.7)
terrain.add_hill((GRID_WIDTH // 2, GRID_HEIGHT // 2), 12, 0.3) # Steep peak
# Secondary volcanoes
terrain.add_hill((15, 15), 10, 0.4)
terrain.add_hill((GRID_WIDTH - 12, GRID_HEIGHT - 15), 8, 0.35)
terrain.add_hill((10, GRID_HEIGHT - 10), 6, 0.25)
# Step 3: Create craters
terrain.dig_hill((GRID_WIDTH // 2, GRID_HEIGHT // 2), 6, 0.1) # Main crater
terrain.dig_hill((15, 15), 4, 0.15) # Secondary crater
terrain.dig_hill((GRID_WIDTH - 12, GRID_HEIGHT - 15), 3, 0.18) # Third crater
# Step 4: Create lava flow pattern using voronoi
lava = mcrfpy.HeightMap((GRID_WIDTH, GRID_HEIGHT), fill=0.0)
lava.add_voronoi(num_points=12, coefficients=(1.0, -0.8), seed=77)
lava.normalize(0.0, 1.0)
# Lava originates from craters - enhance around crater centers
lava.add_hill((GRID_WIDTH // 2, GRID_HEIGHT // 2), 8, 0.5)
lava.add_hill((15, 15), 5, 0.3)
# Lava flows downhill - multiply by inverted terrain
terrain_inv = terrain.inverse()
terrain_inv.normalize(0.0, 1.0)
lava.multiply(terrain_inv)
lava.normalize(0.0, 1.0)
# Step 5: Create ash distribution using noise
ash_noise = mcrfpy.NoiseSource(dimensions=2, algorithm='perlin', seed=123)
ash = mcrfpy.HeightMap((GRID_WIDTH, GRID_HEIGHT), fill=0.0)
ash.add_noise(ash_noise, world_size=(8, 6), mode='turbulence', octaves=3)
ash.normalize(0.0, 1.0)
# Ash settles on lower areas
ash.multiply(terrain_inv)
# Step 6: Apply erosion for realistic channels
terrain.rain_erosion(drops=1500, erosion=0.1, sedimentation=0.03, seed=42)
terrain.normalize(0.0, 1.0)
# Step 7: Add lava rivers from craters
lava.dig_bezier(
points=((GRID_WIDTH // 2, GRID_HEIGHT // 2 + 5),
(GRID_WIDTH // 2 - 5, GRID_HEIGHT // 2 + 15),
(GRID_WIDTH // 3, GRID_HEIGHT - 10),
(10, GRID_HEIGHT - 5)),
start_radius=2, end_radius=3,
start_height=0.9, end_height=0.7
)
lava.dig_bezier(
points=((GRID_WIDTH // 2 + 3, GRID_HEIGHT // 2 + 3),
(GRID_WIDTH // 2 + 15, GRID_HEIGHT // 2 + 8),
(GRID_WIDTH - 15, GRID_HEIGHT // 2 + 5),
(GRID_WIDTH - 5, GRID_HEIGHT // 2 + 10)),
start_radius=1.5, end_radius=2.5,
start_height=0.85, end_height=0.65
)
# Step 8: Render
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
elev = terrain.get((x, y))
lava_val = lava.get((x, y))
ash_val = ash.get((x, y))
color_layer.set(((x, y)), volcanic_color(elev, lava_val, ash_val))
# Add smoke/steam particles around crater rims
crater_centers = [
(GRID_WIDTH // 2, GRID_HEIGHT // 2, 6),
(15, 15, 4),
(GRID_WIDTH - 12, GRID_HEIGHT - 15, 3)
]
import math
for cx, cy, radius in crater_centers:
for angle in range(0, 360, 30):
rad = math.radians(angle)
px = int(cx + math.cos(rad) * radius)
py = int(cy + math.sin(rad) * radius)
if 0 <= px < GRID_WIDTH and 0 <= py < GRID_HEIGHT:
# Smoke color
color_layer.set(((px, py)), mcrfpy.Color(150, 140, 130, 180))
# Title
title = mcrfpy.Caption(text="Volcanic Region: Hills + Craters + Voronoi Lava + Erosion", pos=(10, 10))
title.fill_color = mcrfpy.Color(255, 255, 255)
title.outline = 1
title.outline_color = mcrfpy.Color(0, 0, 0)
scene.children.append(title)
# Setup
scene = mcrfpy.Scene("volcanic")
grid = mcrfpy.Grid(
grid_size=(GRID_WIDTH, GRID_HEIGHT),
pos=(0, 0),
size=(GRID_WIDTH * CELL_SIZE, GRID_HEIGHT * CELL_SIZE),
layers={}
)
grid.fill_color = mcrfpy.Color(0, 0, 0)
color_layer = grid.add_layer("color", z_index=-1)
scene.children.append(grid)
scene.activate()
# Run the demo
run_demo(0)
# Take screenshot
automation.screenshot("procgen_34_advanced_volcanic.png")
print("Screenshot saved: procgen_34_advanced_volcanic.png")