Add container support to thing template loader

Extended ThingTemplate with optional container fields (capacity, closed, locked).
When a template includes capacity, spawn_thing now creates a Container instead
of a regular Thing.

Added two example container templates:
- chest.toml: non-portable, capacity 5, starts closed
- sack.toml: portable, capacity 3, starts open
This commit is contained in:
Jared Miller 2026-02-11 20:43:09 -05:00
parent 3be4370b2f
commit 68161fd025
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
4 changed files with 186 additions and 0 deletions

View file

@ -0,0 +1,7 @@
name = "chest"
description = "a sturdy wooden chest with iron bindings"
portable = false
capacity = 5
closed = true
locked = false
aliases = ["box"]

7
content/things/sack.toml Normal file
View file

@ -0,0 +1,7 @@
name = "sack"
description = "a rough cloth sack with drawstring closure"
portable = true
capacity = 3
closed = false
locked = false
aliases = ["bag"]

View file

@ -4,6 +4,7 @@ import tomllib
from dataclasses import dataclass, field
from pathlib import Path
from mudlib.container import Container
from mudlib.object import Object
from mudlib.thing import Thing
@ -16,6 +17,10 @@ class ThingTemplate:
description: str
portable: bool = True
aliases: list[str] = field(default_factory=list)
# Container fields (presence of capacity indicates a container template)
capacity: int | None = None
closed: bool = False
locked: bool = False
# Module-level registry
@ -31,6 +36,9 @@ def load_thing_template(path: Path) -> ThingTemplate:
description=data["description"],
portable=data.get("portable", True),
aliases=data.get("aliases", []),
capacity=data.get("capacity"),
closed=data.get("closed", False),
locked=data.get("locked", False),
)
@ -51,6 +59,21 @@ def spawn_thing(
y: int | None = None,
) -> Thing:
"""Create a Thing instance from a template at the given location."""
# If template has capacity, spawn a Container instead of a Thing
if template.capacity is not None:
return Container(
name=template.name,
description=template.description,
portable=template.portable,
aliases=list(template.aliases),
capacity=template.capacity,
closed=template.closed,
locked=template.locked,
location=location,
x=x,
y=y,
)
return Thing(
name=template.name,
description=template.description,

View file

@ -0,0 +1,149 @@
"""Tests for container template loading and spawning."""
from pathlib import Path
from tempfile import TemporaryDirectory
from mudlib.container import Container
from mudlib.thing import Thing
from mudlib.things import load_thing_template, spawn_thing
from mudlib.zone import Zone
def test_load_container_template_with_capacity():
"""load_thing_template recognizes container templates (has capacity field)."""
with TemporaryDirectory() as tmpdir:
toml_path = Path(tmpdir) / "chest.toml"
toml_path.write_text(
"""
name = "chest"
description = "a wooden chest"
capacity = 5
"""
)
template = load_thing_template(toml_path)
assert template.name == "chest"
assert template.description == "a wooden chest"
assert hasattr(template, "capacity")
assert template.capacity == 5
def test_load_container_template_with_closed():
"""load_thing_template handles closed field."""
with TemporaryDirectory() as tmpdir:
toml_path = Path(tmpdir) / "chest.toml"
toml_path.write_text(
"""
name = "chest"
description = "a wooden chest"
capacity = 5
closed = true
"""
)
template = load_thing_template(toml_path)
assert template.closed is True
def test_load_container_template_with_locked():
"""load_thing_template handles locked field."""
with TemporaryDirectory() as tmpdir:
toml_path = Path(tmpdir) / "chest.toml"
toml_path.write_text(
"""
name = "chest"
description = "a wooden chest"
capacity = 5
closed = true
locked = true
"""
)
template = load_thing_template(toml_path)
assert template.locked is True
def test_load_container_template_defaults():
"""Container template fields have sensible defaults."""
with TemporaryDirectory() as tmpdir:
toml_path = Path(tmpdir) / "sack.toml"
toml_path.write_text(
"""
name = "sack"
description = "a cloth sack"
capacity = 3
"""
)
template = load_thing_template(toml_path)
assert template.capacity == 3
assert template.closed is False # default: open
assert template.locked is False # default: unlocked
def test_spawn_container_from_template():
"""spawn_thing creates a Container when template has capacity."""
with TemporaryDirectory() as tmpdir:
toml_path = Path(tmpdir) / "chest.toml"
toml_path.write_text(
"""
name = "chest"
description = "a wooden chest"
capacity = 5
closed = true
"""
)
template = load_thing_template(toml_path)
terrain = [["." for _ in range(10)] for _ in range(10)]
zone = Zone(name="test", width=10, height=10, terrain=terrain)
thing = spawn_thing(template, zone, x=5, y=5)
assert isinstance(thing, Container)
assert thing.name == "chest"
assert thing.description == "a wooden chest"
assert thing.capacity == 5
assert thing.closed is True
assert thing.location is zone
def test_spawn_regular_thing_from_template_without_capacity():
"""spawn_thing creates a regular Thing when template lacks capacity."""
with TemporaryDirectory() as tmpdir:
toml_path = Path(tmpdir) / "rock.toml"
toml_path.write_text(
"""
name = "rock"
description = "a smooth grey rock"
portable = true
"""
)
template = load_thing_template(toml_path)
terrain = [["." for _ in range(10)] for _ in range(10)]
zone = Zone(name="test", width=10, height=10, terrain=terrain)
thing = spawn_thing(template, zone, x=5, y=5)
assert isinstance(thing, Thing)
assert not isinstance(thing, Container)
assert thing.name == "rock"
assert thing.portable is True
def test_spawn_portable_container():
"""spawn_thing creates a portable Container (like a sack)."""
with TemporaryDirectory() as tmpdir:
toml_path = Path(tmpdir) / "sack.toml"
toml_path.write_text(
"""
name = "sack"
description = "a cloth sack"
capacity = 3
portable = true
"""
)
template = load_thing_template(toml_path)
terrain = [["." for _ in range(10)] for _ in range(10)]
zone = Zone(name="test", width=10, height=10, terrain=terrain)
thing = spawn_thing(template, zone, x=5, y=5)
assert isinstance(thing, Container)
assert thing.portable is True
assert thing.capacity == 3