Wilderness System Guide¶
Overview¶
The MAID Wilderness System provides on-demand procedural generation of outdoor areas. As players explore, rooms are generated using deterministic noise-based terrain generation, creating consistent, reproducible wilderness landscapes that can expand infinitely within defined boundaries.
Key features: - On-Demand Generation: Rooms created as players explore - Deterministic Terrain: Same coordinates always produce same terrain - Biome System: Multiple biome types with unique characteristics - Landmark Support: Fixed points of interest that anchor procedural terrain - Resource/Encounter Spawning: Procedural content spawning based on biome - Automatic Cleanup: Stale empty rooms cleaned up to manage memory - Player Tracking: Track players across wilderness rooms
Quick Start¶
from maid_engine.world import WildernessManager, WildernessConfig, Landmark
from maid_engine.core.world import World
# Create wilderness manager
world = World()
config = WildernessConfig(
seed=42, # Deterministic seed
min_x=-1000, max_x=1000,
min_y=-1000, max_y=1000,
cleanup_interval=300.0, # Clean up every 5 minutes
room_max_age=600.0, # Rooms stale after 10 minutes empty
)
wilderness = WildernessManager(world=world, config=config)
# Add a town landmark
wilderness.add_landmark(Landmark(
coord=(0, 0),
name="Starting Town",
room_id=town_room_id,
radius=5,
biome_override="plains",
))
# Get or create a room at coordinates
room = wilderness.get_or_create_room(100, 200)
print(f"Room: {room.room_id}, Biome: {room.biome}")
# Get spawned content for the room
content = wilderness.get_spawned_content(100, 200)
print(f"Resources: {content.resources}")
print(f"Encounters: {content.encounters}")
# Start cleanup loop
await wilderness.start()
Core Classes¶
WildernessConfig¶
Configuration for wilderness generation and management.
from maid_engine.world import WildernessConfig
config = WildernessConfig(
seed=42, # Random seed for deterministic generation
terrain_config=None, # Custom TerrainConfig (optional)
min_x=-5000, max_x=5000, # X boundaries
min_y=-5000, max_y=5000, # Y boundaries
cleanup_interval=300.0, # Seconds between cleanup runs
room_max_age=600.0, # Seconds before stale room cleanup
default_z=0, # Z-level for wilderness rooms
)
| Field | Type | Default | Description |
|---|---|---|---|
seed |
int |
0 |
Random seed for deterministic terrain |
terrain_config |
TerrainConfig \| None |
None |
Custom terrain configuration |
min_x, max_x |
int |
-1000, 1000 |
X coordinate boundaries |
min_y, max_y |
int |
-1000, 1000 |
Y coordinate boundaries |
cleanup_interval |
float |
300.0 |
Seconds between cleanup runs |
room_max_age |
float |
600.0 |
Seconds before stale room cleanup |
default_z |
int |
0 |
Z-level for all wilderness rooms |
WildernessManager¶
The main interface for wilderness generation and management.
from maid_engine.world import WildernessManager, WildernessConfig
from maid_engine.core.world import World
# Create with world reference
world = World()
wilderness = WildernessManager(
world=world,
config=WildernessConfig(seed=42),
)
# Or standalone for testing
wilderness = WildernessManager(config=WildernessConfig(seed=42))
GeneratedRoom¶
Metadata for a procedurally generated room.
from maid_engine.world import GeneratedRoom
from uuid import UUID
import time
room = GeneratedRoom(
room_id=UUID("..."),
coord=(100, 200),
biome="forest",
created_at=time.time(),
last_visited=time.time(),
player_count=0,
)
# Check if room is stale
if room.is_stale(max_age=600.0):
print("Room can be cleaned up")
Landmark¶
Fixed points of interest in the wilderness.
from maid_engine.world import Landmark
from uuid import uuid4
# Landmark with pre-existing room
town = Landmark(
coord=(0, 0),
name="Starting Town",
room_id=town_room_id,
radius=5, # Influence radius
biome_override="plains", # Force biome in radius
)
# Landmark without pre-existing room
dungeon = Landmark(
coord=(500, 300),
name="Dragon's Lair",
room_id=None, # Will generate room when visited
radius=3,
description="A dark cave entrance looms in the mountainside.",
biome_override="mountain",
metadata={"dungeon_id": "dragons_lair", "level_range": (15, 20)},
)
Terrain Generation¶
The wilderness uses noise-based terrain generation to create biomes.
TerrainConfig¶
from maid_engine.world import TerrainConfig
config = TerrainConfig(
seed=42,
scale=100.0, # Larger = wider terrain features
octaves=4, # Detail layers
persistence=0.5, # Amplitude decrease per octave
lacunarity=2.0, # Frequency increase per octave
water_level=0.3, # Below this = water
mountain_level=0.7, # Above this = mountains
moisture_scale=80.0, # Moisture noise scale
moisture_octaves=3, # Moisture detail
temperature_scale=150.0, # Temperature noise scale
base_temperature=0.5, # 0=arctic, 1=tropical
)
TerrainGenerator¶
from maid_engine.world import TerrainGenerator, TerrainConfig
generator = TerrainGenerator(TerrainConfig(seed=42))
# Get terrain type at coordinate
terrain = generator.get_terrain(100, 200) # "forest"
# Get full terrain data
data = generator.get_terrain_data(100, 200)
print(f"Type: {data.terrain_type}")
print(f"Elevation: {data.elevation}") # 0-1
print(f"Moisture: {data.moisture}") # 0-1
print(f"Temperature: {data.temperature}") # 0-1
print(f"Movement Cost: {data.movement_cost}")
# Individual values
elevation = generator.get_elevation(100, 200)
moisture = generator.get_moisture(100, 200)
temperature = generator.get_temperature(100, 200)
Biome Types¶
The terrain generator produces these biome types:
| Biome | Movement Cost | Conditions |
|---|---|---|
water |
3.0 | Low elevation (< 0.3) |
plains |
0.8 | Default for moderate elevation |
forest |
1.0 | Moderate moisture (> 0.5) |
desert |
1.5 | Hot (> 0.7) and dry (< 0.3) |
tundra |
1.5 | Cold (< 0.25) |
mountain |
2.0 | High elevation (> 0.7) |
swamp |
2.0 | High moisture near water level |
hills |
1.3 | Elevated terrain with moderate moisture |
Biome Definitions¶
Biomes define room characteristics, resources, and encounters.
BiomeDefinition¶
from maid_engine.world import BiomeDefinition, ResourceSpawn, EncounterSpawn
from maid_engine.world.procedural import register_biome
custom_biome = BiomeDefinition(
name="enchanted_forest",
display_name="Enchanted Forest",
movement_cost=1.5,
sector_type="outdoor",
description_templates=[
"Ethereal light filters through ancient trees.",
"Magical energies crackle in the air.",
],
name_templates=["Mystical Grove", "Fey Crossing", "Ancient Woods"],
resources=[
ResourceSpawn("fey_dust", spawn_chance=0.2, min_quantity=1, max_quantity=3),
ResourceSpawn("moonflower", spawn_chance=0.1),
],
encounters=[
EncounterSpawn("sprite", spawn_chance=0.15, min_level=3, max_level=8),
EncounterSpawn("treant", spawn_chance=0.05, min_level=10, max_level=15),
],
ambient_sounds=["Bells chime softly in the distance."],
ambient_messages=["A butterfly made of light dances past."],
)
register_biome(custom_biome)
ResourceSpawn¶
from maid_engine.world import ResourceSpawn
from random import Random
resource = ResourceSpawn(
resource_type="iron_ore",
spawn_chance=0.15, # 15% chance to spawn
min_quantity=1,
max_quantity=3,
)
# Roll for quantity when spawned
rng = Random(42)
quantity = resource.roll_quantity(rng)
EncounterSpawn¶
from maid_engine.world import EncounterSpawn
from random import Random
encounter = EncounterSpawn(
encounter_type="wolf",
spawn_chance=0.20,
min_level=3,
max_level=7,
group_size_min=2,
group_size_max=5,
)
rng = Random(42)
level = encounter.roll_level(rng)
group_size = encounter.roll_group_size(rng)
Default Biomes¶
Access default biome definitions:
from maid_engine.world.procedural import (
get_biome,
get_all_biome_names,
DEFAULT_BIOMES,
)
# Get specific biome
forest = get_biome("forest")
print(f"Movement cost: {forest.movement_cost}")
# List all biomes
names = get_all_biome_names() # ['desert', 'forest', 'hills', ...]
# Access default definitions
plains = DEFAULT_BIOMES["plains"]
Room Generation¶
On-Demand Generation¶
# Get existing room or generate new one
room = wilderness.get_or_create_room(100, 200)
# Check if room exists
if wilderness.is_generated(100, 200):
room = wilderness.get_generated_room(100, 200)
# Get biome without generating room
biome = wilderness.get_biome(100, 200) # "forest"
# Preview terrain without generating
info = wilderness.preview_terrain(100, 200)
print(f"Biome: {info['biome']}")
print(f"Elevation: {info['elevation']:.2f}")
print(f"Movement Cost: {info['movement_cost']}")
print(f"In Bounds: {info['in_bounds']}")
Spawned Content¶
# Get spawned content for a room
content = wilderness.get_spawned_content(100, 200)
if content:
print(f"Room Name: {content.room_name}")
print(f"Description: {content.room_description}")
print(f"Resources: {content.resources}") # ['wood', 'mushroom']
print(f"Encounters: {content.encounters}") # ['deer']
Custom Room Factory¶
def my_room_factory(x, y, biome_name, biome_def, rng):
"""Create actual room entity in the world."""
room = world.create_entity()
room.add(RoomComponent(
name=biome_def.generate_name(rng),
description=biome_def.generate_description(rng),
))
room.add(PositionComponent(x=x, y=y))
return room.id
wilderness = WildernessManager(
world=world,
config=config,
room_factory=my_room_factory,
)
Landmark Management¶
Adding Landmarks¶
from maid_engine.world import Landmark
# Add a landmark
wilderness.add_landmark(Landmark(
coord=(0, 0),
name="Starting Town",
room_id=town_id,
radius=5,
))
# Add landmark with biome override
wilderness.add_landmark(Landmark(
coord=(100, 100),
name="Oasis",
radius=10,
biome_override="plains", # Force plains in desert area
))
Querying Landmarks¶
# Get landmark at exact coordinate
landmark = wilderness.get_landmark(0, 0)
# Get landmark influencing coordinate (checks radius)
landmark = wilderness.get_landmark_at(3, 3) # Within Starting Town radius
# Get all landmarks
landmarks = wilderness.get_all_landmarks()
# Find landmarks in range
nearby = wilderness.get_landmarks_in_range(50, 50, radius=100)
nearby = wilderness.landmarks_in_radius(50, 50, radius=100.0)
# Remove landmark
removed = wilderness.remove_landmark(0, 0)
Player Tracking¶
Tracking Player Movement¶
# Track player entering room
wilderness.player_entered(room_id, player_id)
# Track player leaving room
wilderness.player_left(room_id, player_id)
# Alternative: update by coordinates
wilderness.increment_player_count(100, 200)
wilderness.decrement_player_count(100, 200)
# Update last visited timestamp
wilderness.update_last_visited(100, 200)
Querying Player Locations¶
# Get players in a room
players = wilderness.get_players_in_room(room_id)
# Get room where player is located
room_id = wilderness.get_player_room(player_id)
# Get all occupied rooms
occupied = wilderness.get_occupied_rooms()
for room in occupied:
print(f"{room.coord}: {room.player_count} players")
Cleanup System¶
Automatic Cleanup¶
# Start the cleanup loop
await wilderness.start()
# ... game runs ...
# Stop the cleanup loop
await wilderness.stop()
# Check if running
if wilderness.is_running:
print("Cleanup loop active")
Manual Cleanup¶
# Get stale rooms
stale = wilderness.get_stale_rooms()
# Manual cleanup
removed = wilderness.cleanup_stale_rooms_sync()
print(f"Removed {removed} rooms")
# Clear all generated rooms
wilderness.clear()
Cleanup Configuration¶
| Config | Default | Description |
|---|---|---|
cleanup_interval |
300.0 | Seconds between automatic cleanup |
room_max_age |
600.0 | Seconds before empty room is stale |
Set cleanup_interval=0 to disable automatic cleanup.
Statistics and Debugging¶
stats = wilderness.get_stats()
print(f"Generated Rooms: {stats['total_generated']}")
print(f"Landmarks: {stats['total_landmarks']}")
print(f"Stale Rooms: {stats['stale_count']}")
print(f"Players in Wilderness: {stats['player_count']}")
print(f"Biome Distribution: {stats['biome_counts']}")
print(f"Bounds: {stats['bounds']}")
Boundary Handling¶
# Check if coordinate is in bounds
if wilderness.is_in_bounds(500, 500):
room = wilderness.get_or_create_room(500, 500)
else:
print("Outside wilderness boundaries")
# Get boundary message for movement
msg = wilderness.get_edge_description(1000, 500, "east")
if msg:
print(msg) # "You cannot travel further east..."
Serialization¶
Export State¶
import json
state = wilderness.export_state()
with open("wilderness.json", "w") as f:
json.dump(state, f)
Import State¶
The export includes: - Configuration settings - All generated rooms - All landmarks - Spawned content - Last cleanup timestamp
Builder Commands¶
MAID provides in-game commands for wilderness management (BUILDER access level):
@wilderness info - Show wilderness statistics
@wilderness biome <x> <y> - Show biome at coordinate
@wilderness preview <x> <y> - Preview terrain at coordinate
@wilderness landmark add <name> <x> <y> [radius] - Add landmark
@wilderness landmark remove <x> <y> - Remove landmark
@wilderness landmark list - List all landmarks
@wilderness cleanup - Manually trigger cleanup
@wilderness generate <x> <y> - Force generate room
@wilderness stats - Detailed statistics
Integration with Content Packs¶
Content packs can extend the wilderness system:
from maid_engine.plugins.protocol import ContentPack
from maid_engine.world import WildernessManager, BiomeDefinition
from maid_engine.world.procedural import register_biome
class MyContentPack(ContentPack):
async def on_load(self, engine):
# Register custom biomes
register_biome(BiomeDefinition(
name="haunted_forest",
display_name="Haunted Forest",
movement_cost=2.0,
# ...
))
# Configure wilderness
wilderness = engine.world.wilderness
wilderness.add_landmark(Landmark(
coord=(0, 0),
name="Starting Area",
radius=10,
))
Example: Creating a Wilderness Zone¶
from maid_engine.world import (
WildernessManager,
WildernessConfig,
TerrainConfig,
Landmark,
)
# Configure terrain for a temperate climate
terrain_config = TerrainConfig(
seed=12345,
scale=150.0, # Larger features
water_level=0.25, # Less water
mountain_level=0.75, # Higher mountains
base_temperature=0.6, # Slightly warm
)
# Configure wilderness
config = WildernessConfig(
seed=12345,
terrain_config=terrain_config,
min_x=-500, max_x=500,
min_y=-500, max_y=500,
cleanup_interval=120.0, # Aggressive cleanup
room_max_age=300.0,
)
wilderness = WildernessManager(world=world, config=config)
# Add key locations
wilderness.add_landmark(Landmark(
coord=(0, 0),
name="Central Town",
room_id=town_id,
radius=8,
biome_override="plains",
))
wilderness.add_landmark(Landmark(
coord=(200, 150),
name="Ancient Ruins",
radius=5,
description="Crumbling stone pillars mark the entrance to forgotten ruins.",
metadata={"dungeon_id": "ancient_ruins"},
))
wilderness.add_landmark(Landmark(
coord=(-100, 300),
name="Hermit's Hut",
radius=2,
description="A small wooden hut sits alone in the wilderness.",
))
# Start the wilderness system
await wilderness.start()