Cookbook: draft docs
This commit is contained in:
parent
8628ac164b
commit
73230989ad
42 changed files with 3352 additions and 0 deletions
163
docs/cookbook/procgen/33_advanced_caves.py
Normal file
163
docs/cookbook/procgen/33_advanced_caves.py
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
"""Advanced: Natural Cave System
|
||||
|
||||
Combines: Noise (cave formation) + Threshold (open areas) + Kernel (smoothing) + BSP (structured areas)
|
||||
Creates organic cave networks with some structured rooms.
|
||||
"""
|
||||
import mcrfpy
|
||||
from mcrfpy import automation
|
||||
|
||||
GRID_WIDTH, GRID_HEIGHT = 64, 48
|
||||
CELL_SIZE = 16
|
||||
|
||||
def run_demo(runtime):
|
||||
# Step 1: Generate cave base using turbulent noise
|
||||
noise = mcrfpy.NoiseSource(dimensions=2, algorithm='simplex', seed=42)
|
||||
|
||||
cave_noise = mcrfpy.HeightMap((GRID_WIDTH, GRID_HEIGHT), fill=0.0)
|
||||
cave_noise.add_noise(noise, world_size=(10, 8), mode='turbulence', octaves=4)
|
||||
cave_noise.normalize(0.0, 1.0)
|
||||
|
||||
# Step 2: Create cave mask via threshold
|
||||
# Values > 0.45 become open cave, rest is rock
|
||||
cave_mask = cave_noise.threshold_binary((0.4, 1.0), 1.0)
|
||||
|
||||
# Step 3: Apply smoothing kernel to remove isolated pixels
|
||||
smooth_kernel = {
|
||||
(-1, -1): 1, (0, -1): 2, (1, -1): 1,
|
||||
(-1, 0): 2, (0, 0): 4, (1, 0): 2,
|
||||
(-1, 1): 1, (0, 1): 2, (1, 1): 1,
|
||||
}
|
||||
cave_mask.kernel_transform(smooth_kernel)
|
||||
cave_mask.normalize(0.0, 1.0)
|
||||
|
||||
# Re-threshold after smoothing
|
||||
cave_mask = cave_mask.threshold_binary((0.5, 1.0), 1.0)
|
||||
|
||||
# Step 4: Add some structured rooms using BSP in one corner
|
||||
# This represents ancient ruins within the caves
|
||||
bsp = mcrfpy.BSP(pos=(GRID_WIDTH - 22, GRID_HEIGHT - 18), size=(18, 14))
|
||||
bsp.split_recursive(depth=2, min_size=(6, 5), seed=42)
|
||||
|
||||
ruins_hmap = bsp.to_heightmap(
|
||||
size=(GRID_WIDTH, GRID_HEIGHT),
|
||||
select='leaves',
|
||||
shrink=1,
|
||||
value=1.0
|
||||
)
|
||||
|
||||
# Step 5: Combine caves and ruins
|
||||
combined = mcrfpy.HeightMap((GRID_WIDTH, GRID_HEIGHT), fill=0.0)
|
||||
combined.copy_from(cave_mask)
|
||||
combined.max(ruins_hmap)
|
||||
|
||||
# Step 6: Add connecting tunnels from ruins to main cave
|
||||
# Find a cave entrance point
|
||||
tunnel_points = []
|
||||
for y in range(GRID_HEIGHT - 18, GRID_HEIGHT - 10):
|
||||
for x in range(GRID_WIDTH - 25, GRID_WIDTH - 20):
|
||||
if cave_mask.get((x, y)) > 0.5:
|
||||
tunnel_points.append((x, y))
|
||||
break
|
||||
if tunnel_points:
|
||||
break
|
||||
|
||||
if tunnel_points:
|
||||
tx, ty = tunnel_points[0]
|
||||
# Carve tunnel to ruins entrance
|
||||
combined.dig_bezier(
|
||||
points=((tx, ty), (tx + 3, ty), (GRID_WIDTH - 22, ty + 2), (GRID_WIDTH - 20, GRID_HEIGHT - 15)),
|
||||
start_radius=1.5, end_radius=1.5,
|
||||
start_height=1.0, end_height=1.0
|
||||
)
|
||||
|
||||
# Step 7: Add large cavern (central chamber)
|
||||
combined.add_hill((GRID_WIDTH // 3, GRID_HEIGHT // 2), 8, 0.6)
|
||||
|
||||
# Step 8: Create water pools in low noise areas
|
||||
water_noise = mcrfpy.NoiseSource(dimensions=2, algorithm='perlin', seed=99)
|
||||
water_map = mcrfpy.HeightMap((GRID_WIDTH, GRID_HEIGHT), fill=0.0)
|
||||
water_map.add_noise(water_noise, world_size=(15, 12), mode='fbm', octaves=3)
|
||||
water_map.normalize(0.0, 1.0)
|
||||
|
||||
# Step 9: Render
|
||||
for y in range(GRID_HEIGHT):
|
||||
for x in range(GRID_WIDTH):
|
||||
cave_val = combined.get((x, y))
|
||||
water_val = water_map.get((x, y))
|
||||
original_noise = cave_noise.get((x, y))
|
||||
|
||||
# Check if in ruins area
|
||||
in_ruins = (x >= GRID_WIDTH - 22 and x < GRID_WIDTH - 4 and
|
||||
y >= GRID_HEIGHT - 18 and y < GRID_HEIGHT - 4)
|
||||
|
||||
if cave_val < 0.3:
|
||||
# Solid rock
|
||||
base = 30 + int(original_noise * 25)
|
||||
color_layer.set(((x, y)), mcrfpy.Color(base + 10, base + 5, base + 15))
|
||||
elif cave_val < 0.5:
|
||||
# Cave wall edge
|
||||
color_layer.set(((x, y)), mcrfpy.Color(45, 40, 50))
|
||||
else:
|
||||
# Open cave floor
|
||||
if water_val > 0.7 and not in_ruins:
|
||||
# Water pool
|
||||
t = (water_val - 0.7) / 0.3
|
||||
color_layer.set(((x, y)), mcrfpy.Color(
|
||||
int(30 + t * 20), int(50 + t * 30), int(100 + t * 50)
|
||||
))
|
||||
elif in_ruins and ruins_hmap.get((x, y)) > 0.5:
|
||||
# Ruins floor (worked stone)
|
||||
base = 85 + ((x + y) % 3) * 5
|
||||
color_layer.set(((x, y)), mcrfpy.Color(base + 10, base + 5, base))
|
||||
else:
|
||||
# Natural cave floor
|
||||
base = 55 + int(original_noise * 20)
|
||||
var = ((x * 3 + y * 5) % 8)
|
||||
color_layer.set(((x, y)), mcrfpy.Color(base + var, base - 5 + var, base - 8))
|
||||
|
||||
# Glowing fungi spots
|
||||
fungi_noise = mcrfpy.NoiseSource(dimensions=2, algorithm='simplex', seed=777)
|
||||
for y in range(GRID_HEIGHT):
|
||||
for x in range(GRID_WIDTH):
|
||||
if combined.get((x, y)) > 0.5: # Only in open areas
|
||||
fungi_val = fungi_noise.get((x * 0.5, y * 0.5))
|
||||
if fungi_val > 0.8:
|
||||
color_layer.set(((x, y)), mcrfpy.Color(80, 180, 120))
|
||||
|
||||
# Border
|
||||
for x in range(GRID_WIDTH):
|
||||
color_layer.set(((x, 0)), mcrfpy.Color(20, 18, 25))
|
||||
color_layer.set(((x, GRID_HEIGHT - 1)), mcrfpy.Color(20, 18, 25))
|
||||
for y in range(GRID_HEIGHT):
|
||||
color_layer.set(((0, y)), mcrfpy.Color(20, 18, 25))
|
||||
color_layer.set(((GRID_WIDTH - 1, y)), mcrfpy.Color(20, 18, 25))
|
||||
|
||||
# Title
|
||||
title = mcrfpy.Caption(text="Cave System: Noise + Threshold + Kernel + BSP Ruins", 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("caves")
|
||||
|
||||
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_33_advanced_caves.png")
|
||||
print("Screenshot saved: procgen_33_advanced_caves.png")
|
||||
Loading…
Add table
Add a link
Reference in a new issue