Tutorial Part 4: Creating Rooms & Items¶
Estimated time: 45 minutes
Welcome back! In Part 3, you learned how to create commands - the primary way players interact with your game. Now it is time to build the world itself: rooms for players to explore and items for them to find and use.
What You Will Learn¶
By the end of this tutorial, you will:
- Understand how rooms and items are represented in MAID
- Create rooms programmatically with components
- Use builder commands to quickly create game content
- Build items with proper components and templates
- Connect rooms with exits
- Complete a hands-on exercise: building a 5-room dungeon
Why Rooms and Items Matter¶
In a MUD, the world is built from two fundamental elements:
-
Rooms - The locations players can visit. Every player is always "in" a room. Rooms contain descriptions, exits to other rooms, and can hold items and NPCs.
-
Items - Objects players can interact with. Items can be picked up, dropped, equipped, used, or stored in containers.
Together, rooms and items create the tangible world that brings your game to life. A well-designed room tells a story through its description, rewards exploration through hidden items, and connects logically to other parts of your world.
Room Entity Structure¶
In MAID, rooms are registered with the World and contain several pieces of information:
+------------------+
| Room Data |
+------------------+
| - id (UUID) |
| - name |
| - description |
| - exits {} |
| - area_id |
+------------------+
Unlike player characters or items, rooms are not full ECS entities. Instead, they are simple data objects that hold:
- id - Unique identifier (UUID)
- name - Display name shown to players
- description - What players see when they "look"
- exits - Dictionary mapping directions to destination room IDs
- area_id - Optional zone/area grouping
Core Room Components¶
While basic rooms use simple data objects, you can enhance rooms with components for richer functionality.
DescriptionComponent¶
The DescriptionComponent provides names, descriptions, and keywords:
from maid_stdlib.components import DescriptionComponent
DescriptionComponent(
name="Town Square",
short_desc="A bustling town square",
long_desc="You stand in the heart of town, surrounded by merchants...",
keywords=["square", "plaza", "center"],
)
| Field | Type | Description |
|---|---|---|
name |
str | Display name |
short_desc |
str | One-line description (for room lists, etc.) |
long_desc |
str | Full description shown when looking |
keywords |
list[str] | Words that can match this entity |
ExtendedRoomComponent¶
For dynamic descriptions that change based on time of day, weather, or season, use ExtendedRoomComponent:
from maid_stdlib.components import (
ExtendedRoomComponent,
ExtendedDescriptions,
TimeOfDay,
Weather,
Season,
)
extended = ExtendedRoomComponent(
descriptions=ExtendedDescriptions(
base_description="A cozy tavern with a roaring fireplace.",
time_variants={
TimeOfDay.NIGHT: "The tavern is dimly lit by flickering candles.",
TimeOfDay.MORNING: "Morning light streams through dusty windows.",
},
weather_effects={
Weather.RAIN: "Rain drums steadily on the roof above.",
Weather.STORM: "Thunder rumbles outside, making the walls shake.",
},
season_variants={
Season.WINTER: "A thick layer of frost covers the windows.",
},
)
)
The description automatically adapts based on current conditions:
context = {
"time": TimeOfDay.NIGHT,
"weather": Weather.RAIN,
"season": Season.WINTER,
}
description = extended.get_description(context)
# "A cozy tavern with a roaring fireplace.
# The tavern is dimly lit by flickering candles.
# A thick layer of frost covers the windows.
# Rain drums steadily on the roof above."
Creating Rooms Programmatically¶
Let's create a room using code. This is useful when building world generation systems or loading rooms from data files.
Basic Room Creation¶
from uuid import uuid4
from maid_stdlib.commands.building.create import RoomData
# Create room data
room_id = uuid4()
room = RoomData(
id=room_id,
name="Town Square",
description="You stand in the heart of the bustling town. "
"Merchants hawk their wares from colorful stalls, "
"and the smell of fresh bread wafts from a nearby bakery.",
exits={}, # No exits yet
area_id=None,
)
# Register the room with the world
world.register_room(room_id, room)
Connecting Rooms with Exits¶
Exits are stored as a dictionary mapping direction names to destination room IDs:
# Create two rooms
square_id = uuid4()
square = RoomData(
id=square_id,
name="Town Square",
description="The central plaza of the town.",
exits={},
)
market_id = uuid4()
market = RoomData(
id=market_id,
name="Market Street",
description="A busy street lined with merchant stalls.",
exits={},
)
# Register both rooms
world.register_room(square_id, square)
world.register_room(market_id, market)
# Connect them with exits (bidirectional)
square.exits["north"] = market_id
market.exits["south"] = square_id
Standard Directions¶
MAID supports these standard directions:
| Direction | Opposite | Aliases |
|---|---|---|
| north | south | n |
| south | north | s |
| east | west | e |
| west | east | w |
| up | down | u |
| down | up | d |
| northeast | southwest | ne |
| northwest | southeast | nw |
| southeast | northwest | se |
| southwest | northeast | sw |
| in | out | - |
| out | in | - |
You can also use custom direction names like "portal", "trapdoor", or "climb".
Creating a Room Helper Function¶
Here's a reusable function for creating connected rooms:
from uuid import UUID, uuid4
from maid_stdlib.commands.building.create import RoomData
def create_room(
world,
name: str,
description: str,
area_id: UUID | None = None,
) -> UUID:
"""Create and register a new room.
Returns:
The UUID of the created room.
"""
room_id = uuid4()
room = RoomData(
id=room_id,
name=name,
description=description,
exits={},
area_id=area_id,
)
world.register_room(room_id, room)
return room_id
def connect_rooms(
world,
from_room_id: UUID,
direction: str,
to_room_id: UUID,
bidirectional: bool = True,
) -> None:
"""Connect two rooms with exits.
Args:
world: The game world
from_room_id: Source room UUID
direction: Exit direction (e.g., "north")
to_room_id: Destination room UUID
bidirectional: If True, create return exit
"""
opposites = {
"north": "south", "south": "north",
"east": "west", "west": "east",
"up": "down", "down": "up",
}
from_room = world.get_room(from_room_id)
to_room = world.get_room(to_room_id)
# Create forward exit
from_room.exits[direction] = to_room_id
# Create return exit if bidirectional
if bidirectional and direction in opposites:
to_room.exits[opposites[direction]] = from_room_id
Using Builder Commands¶
While programmatic room creation is powerful, MAID provides builder commands for rapid world building directly from within the game. These commands are available to users with BUILDER access level or higher.
@dig - Create Rooms and Connect Them¶
The @dig command creates a new room and automatically connects it to your current room.
Syntax:
Options:
- --oneway - Create only the outgoing exit (no return path)
- --door - The exit has a door
- --locked - The door starts locked (implies --door)
- --hidden - The exit is hidden until discovered
Examples:
> @dig north
Dug north to 'New Room (north of Town Square)' (ID: abc123...)
> @dig north = The Dark Cave
Dug north to 'The Dark Cave' (ID: def456...)
> @dig up = Tower Top --oneway
Dug up to 'Tower Top' (ID: ghi789...)
(one-way exit)
> @dig east = Secret Room --door --locked --hidden
Dug east to 'Secret Room' (ID: jkl012...)
(with door, locked)
(hidden)
@describe - Set Room Descriptions¶
After creating a room, use @describe to set its description.
Syntax:
Examples:
> @describe here
Room: The Dark Cave
Description: An empty room.
> @describe here A damp cave stretches before you. Water drips from stalactites overhead, and strange mushrooms glow faintly in the darkness.
Set description for room 'The Dark Cave'.
@name - Rename Rooms¶
Change a room's name with @name.
Syntax:
Example:
@tunnel - Connect to Existing Rooms¶
Use @tunnel to create a passage to a room that already exists.
Syntax:
Example:
@link and @unlink - Manage Exits¶
For finer control over exits:
@link - Create an exit to a destination:
@unlink - Remove an exit:
> @unlink north
Removed exit to the north.
> @unlink east --both
Removed exit to the east.
Removed return exit (west) from destination.
Practical Walkthrough: Building a Small Area¶
Let's build a small tavern area using builder commands:
# Start in the Town Square
> look
Town Square
You are in the center of a bustling town...
Exits: none
# Create the tavern to the east
> @dig east = The Rusty Tankard
Dug east to 'The Rusty Tankard' (ID: abc123...)
# Go to the tavern
> east
The Rusty Tankard
An empty room.
Exits: west
# Set the tavern's description
> @describe here A cozy tavern filled with the smell of ale and roasting meat. Wooden beams criss-cross the ceiling, and a large fireplace crackles warmly against the far wall.
Set description for room 'The Rusty Tankard'.
# Create the cellar (one-way down - trapdoor!)
> @dig down = Tavern Cellar --door
Dug down to 'Tavern Cellar' (ID: def456...)
(with door)
# Go to the cellar
> open down
You open the trapdoor.
> down
Tavern Cellar
An empty room.
Exits: up
# Describe the cellar
> @describe here A cool, damp cellar lined with barrels of ale and crates of supplies. Cobwebs hang in the corners, and you hear rats scurrying in the shadows.
Set description for room 'Tavern Cellar'.
Item Entity Structure¶
Items in MAID are full ECS entities with components attached. This makes them flexible and extensible.
+------------------+
| Item Entity |
+------------------+
| id: UUID |
| tags: [item] |
+------------------+
|
v
+------------------+ +------------------+
|DescriptionComp | | ItemComponent |
+------------------+ +------------------+
| name | | item_type |
| short_desc | | quality |
| long_desc | | weight |
| keywords | | value |
+------------------+ | stack_count |
| is_bound |
| durability |
+------------------+
ItemComponent¶
The ItemComponent defines item-specific properties:
from maid_stdlib.components import ItemComponent
ItemComponent(
item_type="weapon", # weapon, armor, consumable, material, misc, etc.
quality="uncommon", # common, uncommon, rare, epic, legendary
weight=2.5, # Weight in arbitrary units
value=100, # Base gold value
stack_count=1, # Current stack size
max_stack=1, # Maximum stack size (1 = not stackable)
is_bound=False, # Cannot be traded/dropped
durability=100, # Current durability
max_durability=100, # Maximum durability
)
Common Item Types¶
| Type | Description | Examples |
|---|---|---|
weapon |
Deals damage | Sword, axe, bow |
armor |
Provides protection | Helmet, chainmail |
consumable |
Single-use items | Potion, food, scroll |
material |
Crafting components | Iron ore, leather |
container |
Holds other items | Bag, chest |
key |
Opens locked doors | Brass key, skeleton key |
misc |
Everything else | Torch, rope, book |
Creating Items Programmatically¶
Basic Item Creation¶
from maid_stdlib.components import DescriptionComponent, ItemComponent
# Create item entity
item = world.create_entity()
item.add_tag("item")
# Add description
item.add(DescriptionComponent(
name="Iron Sword",
short_desc="A sturdy iron sword lies here.",
long_desc="This well-forged iron sword has a leather-wrapped hilt "
"and a blade that gleams in the light.",
keywords=["sword", "iron", "blade", "weapon"],
))
# Add item component
item.add(ItemComponent(
item_type="weapon",
quality="common",
weight=3.0,
value=50,
))
# Place in a room
world.place_entity_in_room(item.id, room_id)
Item Templates¶
Templates let you define reusable item configurations:
from maid_stdlib.commands.building.create import (
EntityTemplate,
register_template,
)
# Define a sword template
iron_sword_template = EntityTemplate(
name="iron_sword",
entity_type="item",
description="A standard iron sword.",
keywords=["sword", "iron", "blade", "weapon"],
metadata={
"item_type": "weapon",
"quality": "common",
"weight": 3.0,
"value": 50,
},
tags=["weapon", "melee"],
)
# Register the template
register_template(iron_sword_template)
Now you can create items from the template:
from maid_stdlib.commands.building.create import (
get_entity_factory,
get_template,
)
# Get the factory and template
factory = get_entity_factory("item")
template = get_template("iron_sword")
# Create an item from the template
sword = factory.create(
world,
"Iron Sword",
template=template,
room_id=room_id,
)
Using @create for Items¶
In-game, use @create to make items:
> @create item Rusty Dagger
Created item 'Rusty Dagger' with ID: abc123... in current room
> @create item Health Potion = potion_template
Created item 'Health Potion' with ID: def456... in current room
Container Items¶
Containers are special items that can hold other items. They have both ItemComponent and InventoryComponent.
Creating Containers¶
from maid_stdlib.components import (
DescriptionComponent,
ItemComponent,
InventoryComponent,
)
# Create container entity
chest = world.create_entity()
chest.add_tag("item")
chest.add_tag("container")
# Add description
chest.add(DescriptionComponent(
name="Wooden Chest",
short_desc="A sturdy wooden chest sits here.",
long_desc="This iron-bound wooden chest looks old but solid. "
"A simple latch holds it closed.",
keywords=["chest", "wooden", "container", "box"],
))
# Add item component (containers are items)
chest.add(ItemComponent(
item_type="container",
weight=10.0,
value=25,
))
# Add inventory component (containers have inventory)
chest.add(InventoryComponent(
capacity=20, # Can hold 20 items
weight_limit=100.0, # Maximum weight capacity
))
# Place in room
world.place_entity_in_room(chest.id, room_id)
Using @create for Containers¶
Putting Items in Containers¶
Programmatically:
# Get the container's inventory
inventory = chest.get(InventoryComponent)
# Add an item to the container
item_weight = item.get(ItemComponent).weight
inventory.add_item(item.id, weight=item_weight)
# Update item's location (remove from room)
world.remove_entity_from_room(item.id)
Spawning Items¶
The @spawn command creates items from templates with additional options:
> @spawn health_potion
Spawned 'Health Potion' (item) in 'Town Square' with ID: abc123...
> @spawn health_potion 5
Spawned 5 'Health Potion' (item) entities in 'Town Square'
- ID: abc123...
- ID: def456...
- ID: ghi789...
- ID: jkl012...
- ID: mno345...
> @spawn treasure_chest 3 = #550e8400...
Spawned 3 'Treasure Chest' (container) entities in 'Dungeon Treasury'
Respawning Mechanics¶
For items that should reappear over time (like loot in a dungeon), you would typically implement a respawn system. This is covered in more detail in the content pack tutorials, but the basic approach is:
- Create item templates for respawnable items
- Implement a
RespawnSystemthat tracks spawn points - Use the tick loop to check and respawn items periodically
Exercise: Build a 5-Room Dungeon¶
Now let's put everything together! Your task is to build a small dungeon with:
- Entrance Hall - The entry point
- Guard Room - Contains a torch
- Treasury - Contains a treasure chest with gold
- Prison Cell - Contains a skeleton key
- Boss Chamber - Contains a locked door requiring the key
Step 1: Create the Rooms¶
Connect via telnet and create the dungeon:
# Start at your current location (or create a starting room)
> @dig down = Dungeon Entrance
Dug down to 'Dungeon Entrance' (ID: entrance-id...)
> down
Dungeon Entrance
An empty room.
> @describe here You descend into a damp stone passage. Moss covers the ancient walls, and the air smells of earth and decay. Flickering torchlight reveals passages leading deeper into the darkness.
Set description for room 'Dungeon Entrance'.
# Create the Guard Room to the north
> @dig north = Guard Room
Dug north to 'Guard Room' (ID: guard-id...)
> north
> @describe here An abandoned guard post. Rusty weapons hang on the walls, and a battered table sits in the corner. Empty bottles suggest the guards left in a hurry.
Set description for room 'Guard Room'.
# Create the Treasury to the east (from Guard Room)
> @dig east = Treasury --door --locked
Dug east to 'Treasury' (ID: treasury-id...)
(with door, locked)
# Go back and create the Prison Cell to the west of entrance
> south
> @dig west = Prison Cell
Dug west to 'Prison Cell' (ID: prison-id...)
> west
> @describe here A cramped cell with rusted iron bars. Bones litter the floor, and chains hang from the walls. Whatever was imprisoned here is long gone.
Set description for room 'Prison Cell'.
# Create the Boss Chamber to the north of entrance (requires going back)
> east
> @dig east = Boss Chamber --door --locked --hidden
Dug east to 'Boss Chamber' (ID: boss-id...)
(with door, locked)
(hidden)
Step 2: Add Items¶
# Go to Guard Room and create a torch
> north
> @create item Burning Torch
Created item 'Burning Torch' with ID: torch-id...
> @describe torch A torch burning with a bright, steady flame. It illuminates the surrounding area.
Set description for 'Burning Torch'.
# Go to Prison Cell and create a skeleton key
> south
> west
> @create item Skeleton Key
Created item 'Skeleton Key' with ID: key-id...
> @describe key An ancient key made of yellowed bone. It seems to vibrate slightly when held.
Set description for 'Skeleton Key'.
# Go to Treasury (need to unlock it first - for now, use @goto)
> @goto #treasury-id
Treasury
An empty room.
> @describe here Piles of gold coins glitter in the torchlight. Jewel-encrusted goblets and ancient artifacts line the shelves. This must have been the dragon's hoard.
Set description for room 'Treasury'.
> @create container Treasure Chest
Created container 'Treasure Chest' with ID: chest-id...
> @describe chest A massive iron-bound chest overflowing with gold and jewels.
Set description for 'Treasure Chest'.
# Go to Boss Chamber
> @goto #boss-id
Boss Chamber
An empty room.
> @describe here A vast cavern with a high vaulted ceiling. Bones of previous adventurers crunch underfoot. In the center, upon a throne of skulls, waits the dungeon's master.
Set description for room 'Boss Chamber'.
Step 3: Verify Your Dungeon¶
Walk through your dungeon to make sure everything is connected:
> @goto #entrance-id
Dungeon Entrance
You descend into a damp stone passage...
Exits: up, north, west, east
> look
Dungeon Entrance
You descend into a damp stone passage. Moss covers the ancient walls, and the air smells of earth and decay. Flickering torchlight reveals passages leading deeper into the darkness.
Obvious exits: up, north, west, east
> north
Guard Room
An abandoned guard post...
You see here:
A Burning Torch
Congratulations!¶
You have built your first dungeon! This exercise demonstrated:
- Creating rooms with
@dig - Setting descriptions with
@describe - Creating items with
@create - Creating containers
- Using doors and locks
- Connecting areas logically
What's Next?¶
You now know how to:
- Create rooms programmatically and with builder commands
- Use components to define room properties
- Build items with templates
- Create containers for item storage
- Connect rooms with exits
- Use the @dig, @describe, @create, and related commands
In the next tutorial, you will learn about NPCs and AI, bringing your world to life with characters that players can interact with.
Coming up in Part 5: NPCs & Basic AI
- Creating NPC entities
- NPC components and behaviors
- AI-powered dialogue
- NPC spawn points and respawning
- Simple patrol and behavior patterns
< Previous: Part 3 - Commands Back to Tutorial Index Next: Part 5 - NPCs & AI >
Quick Reference¶
Room Creation Commands¶
| Command | Description | Example |
|---|---|---|
@dig <dir> [= name] |
Create room with exit | @dig north = Cave |
@tunnel <dir> <id> |
Connect to existing room | @tunnel west #abc123 |
@describe <target> [text] |
Set/view description | @describe here A cave. |
@name <target> <name> |
Rename room/entity | @name here Dark Cave |
@link <dir> <dest> |
Create exit | @link portal #abc123 |
@unlink <dir> |
Remove exit | @unlink north --both |
@dig Options¶
| Option | Description |
|---|---|
--oneway |
No return exit |
--door |
Exit has a door |
--locked |
Door starts locked |
--hidden |
Exit is hidden |
Item Creation Commands¶
| Command | Description | Example |
|---|---|---|
@create item <name> |
Create item | @create item Sword |
@create item <name> = <template> |
Create from template | @create item Sword = iron_sword |
@create container <name> |
Create container | @create container Chest |
@spawn <template> [count] |
Spawn from template | @spawn goblin 5 |
Standard Components¶
| Component | Purpose | Key Fields |
|---|---|---|
DescriptionComponent |
Names and descriptions | name, short_desc, long_desc, keywords |
ItemComponent |
Item properties | item_type, quality, weight, value |
InventoryComponent |
Storage capacity | capacity, weight_limit, items |
ExtendedRoomComponent |
Dynamic descriptions | descriptions, exit_descriptions |
Item Types¶
| Type | Description |
|---|---|
weapon |
Damage-dealing items |
armor |
Protective equipment |
consumable |
Single-use items |
material |
Crafting components |
container |
Holds other items |
key |
Opens locks |
misc |
Miscellaneous items |