Add tutorial zones (flower and treehouse)
Implements the new player funnel with two tutorial zones: - Flower: 7x7 sealed zone with translucent petals, spawn at center - Treehouse: 20x15 platform zone with rope ladder and branch exits Both zones are bounded (non-toroidal) and include portals for progression.
This commit is contained in:
parent
7154dd86d3
commit
56c82700b0
3 changed files with 193 additions and 0 deletions
27
content/zones/flower.toml
Normal file
27
content/zones/flower.toml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
name = "flower"
|
||||||
|
description = "you lie in the heart of a giant flower, warm light filtering through translucent petals"
|
||||||
|
width = 7
|
||||||
|
height = 7
|
||||||
|
toroidal = false
|
||||||
|
spawn_x = 3
|
||||||
|
spawn_y = 3
|
||||||
|
|
||||||
|
[terrain]
|
||||||
|
rows = [
|
||||||
|
"ooooooo",
|
||||||
|
"ooo.ooo",
|
||||||
|
"oo...oo",
|
||||||
|
"o.....o",
|
||||||
|
"oo...oo",
|
||||||
|
"ooo.ooo",
|
||||||
|
"ooo.ooo",
|
||||||
|
]
|
||||||
|
|
||||||
|
[terrain.impassable]
|
||||||
|
tiles = ["o"]
|
||||||
|
|
||||||
|
[[portals]]
|
||||||
|
x = 3
|
||||||
|
y = 6
|
||||||
|
target = "treehouse:10,7"
|
||||||
|
label = "an opening in the petals"
|
||||||
41
content/zones/treehouse.toml
Normal file
41
content/zones/treehouse.toml
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
name = "treehouse"
|
||||||
|
description = "a sprawling treehouse platform high in an ancient oak, branches creaking in the wind"
|
||||||
|
width = 20
|
||||||
|
height = 15
|
||||||
|
toroidal = false
|
||||||
|
spawn_x = 10
|
||||||
|
spawn_y = 7
|
||||||
|
|
||||||
|
[terrain]
|
||||||
|
rows = [
|
||||||
|
"~TTTTTTTTTTTTTTTTTT~",
|
||||||
|
"~TToooooooooooooTTT~",
|
||||||
|
"~TToo..........ooTT~",
|
||||||
|
"~TTo............oT~~",
|
||||||
|
"~TTo............oT~~",
|
||||||
|
"~TTo............oT~~",
|
||||||
|
"~TTo............oT~~",
|
||||||
|
"...o............oT~~",
|
||||||
|
"~TTo............oT~~",
|
||||||
|
"~TTo............oT~~",
|
||||||
|
"~TTo............oT~~",
|
||||||
|
"~TTo............oT~~",
|
||||||
|
"~TToo..........ooT~~",
|
||||||
|
"~TTToooooooooooTTT~~",
|
||||||
|
"~TTT...oooo...TTTT~~",
|
||||||
|
]
|
||||||
|
|
||||||
|
[terrain.impassable]
|
||||||
|
tiles = ["o", "~"]
|
||||||
|
|
||||||
|
[[portals]]
|
||||||
|
x = 10
|
||||||
|
y = 14
|
||||||
|
target = "overworld:500,500"
|
||||||
|
label = "a rope ladder dangles into the mist below"
|
||||||
|
|
||||||
|
[[portals]]
|
||||||
|
x = 0
|
||||||
|
y = 7
|
||||||
|
target = "hub:7,14"
|
||||||
|
label = "a narrow branch leads to a distant platform"
|
||||||
125
tests/test_tutorial_zones.py
Normal file
125
tests/test_tutorial_zones.py
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
"""Tests for tutorial zones (flower and treehouse)."""
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
from mudlib.zones import load_zone
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_flower_zone():
|
||||||
|
"""Load the flower zone and verify basic properties."""
|
||||||
|
project_root = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
flower_path = project_root / "content" / "zones" / "flower.toml"
|
||||||
|
|
||||||
|
zone = load_zone(flower_path)
|
||||||
|
|
||||||
|
assert zone.name == "flower"
|
||||||
|
assert zone.width == 7
|
||||||
|
assert zone.height == 7
|
||||||
|
assert zone.toroidal is False
|
||||||
|
assert zone.spawn_x == 3
|
||||||
|
assert zone.spawn_y == 3
|
||||||
|
assert len(zone.terrain) == 7
|
||||||
|
|
||||||
|
|
||||||
|
def test_flower_zone_mostly_sealed():
|
||||||
|
"""Verify flower zone has mostly sealed borders with impassable petals."""
|
||||||
|
project_root = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
flower_path = project_root / "content" / "zones" / "flower.toml"
|
||||||
|
|
||||||
|
zone = load_zone(flower_path)
|
||||||
|
|
||||||
|
# Verify 'o' is impassable (petals)
|
||||||
|
assert "o" in zone.impassable
|
||||||
|
|
||||||
|
# Count impassable border tiles
|
||||||
|
border_tiles = []
|
||||||
|
# Top and bottom rows
|
||||||
|
for x in range(zone.width):
|
||||||
|
border_tiles.append((x, 0))
|
||||||
|
border_tiles.append((x, zone.height - 1))
|
||||||
|
# Left and right columns (excluding corners already counted)
|
||||||
|
for y in range(1, zone.height - 1):
|
||||||
|
border_tiles.append((0, y))
|
||||||
|
border_tiles.append((zone.width - 1, y))
|
||||||
|
|
||||||
|
impassable_border_count = sum(
|
||||||
|
1 for x, y in border_tiles if not zone.is_passable(x, y)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Most of the border should be impassable (allow for 1-2 openings)
|
||||||
|
total_border = len(border_tiles)
|
||||||
|
assert impassable_border_count >= total_border - 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_flower_has_portal_to_treehouse():
|
||||||
|
"""Verify flower zone has a portal targeting treehouse."""
|
||||||
|
project_root = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
flower_path = project_root / "content" / "zones" / "flower.toml"
|
||||||
|
|
||||||
|
zone = load_zone(flower_path)
|
||||||
|
|
||||||
|
# Find portals in zone contents
|
||||||
|
portals = [obj for obj in zone._contents if obj.__class__.__name__ == "Portal"]
|
||||||
|
assert len(portals) == 1
|
||||||
|
|
||||||
|
portal = portals[0]
|
||||||
|
assert portal.target_zone == "treehouse"
|
||||||
|
assert portal.target_x == 10
|
||||||
|
assert portal.target_y == 7
|
||||||
|
assert "petal" in portal.name.lower() or "opening" in portal.name.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_treehouse_zone():
|
||||||
|
"""Load the treehouse zone and verify basic properties."""
|
||||||
|
project_root = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
treehouse_path = project_root / "content" / "zones" / "treehouse.toml"
|
||||||
|
|
||||||
|
zone = load_zone(treehouse_path)
|
||||||
|
|
||||||
|
assert zone.name == "treehouse"
|
||||||
|
assert zone.width == 20
|
||||||
|
assert zone.height == 15
|
||||||
|
assert zone.toroidal is False
|
||||||
|
assert zone.spawn_x == 10
|
||||||
|
assert zone.spawn_y == 7
|
||||||
|
assert len(zone.terrain) == 15
|
||||||
|
|
||||||
|
|
||||||
|
def test_treehouse_has_portal_to_overworld():
|
||||||
|
"""Verify treehouse has a portal to the overworld."""
|
||||||
|
project_root = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
treehouse_path = project_root / "content" / "zones" / "treehouse.toml"
|
||||||
|
|
||||||
|
zone = load_zone(treehouse_path)
|
||||||
|
|
||||||
|
# Find portals
|
||||||
|
portals = [obj for obj in zone._contents if obj.__class__.__name__ == "Portal"]
|
||||||
|
|
||||||
|
# Find the overworld portal
|
||||||
|
overworld_portals = [p for p in portals if p.target_zone == "overworld"]
|
||||||
|
assert len(overworld_portals) == 1
|
||||||
|
|
||||||
|
portal = overworld_portals[0]
|
||||||
|
assert portal.target_x == 500
|
||||||
|
assert portal.target_y == 500
|
||||||
|
assert "ladder" in portal.name.lower() or "mist" in portal.name.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def test_treehouse_has_portal_to_hub():
|
||||||
|
"""Verify treehouse has a portal to the hub."""
|
||||||
|
project_root = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
treehouse_path = project_root / "content" / "zones" / "treehouse.toml"
|
||||||
|
|
||||||
|
zone = load_zone(treehouse_path)
|
||||||
|
|
||||||
|
# Find portals
|
||||||
|
portals = [obj for obj in zone._contents if obj.__class__.__name__ == "Portal"]
|
||||||
|
|
||||||
|
# Find the hub portal
|
||||||
|
hub_portals = [p for p in portals if p.target_zone == "hub"]
|
||||||
|
assert len(hub_portals) == 1
|
||||||
|
|
||||||
|
portal = hub_portals[0]
|
||||||
|
assert portal.target_x == 7
|
||||||
|
assert portal.target_y == 14
|
||||||
|
assert "branch" in portal.name.lower() or "platform" in portal.name.lower()
|
||||||
Loading…
Reference in a new issue